Files
2026-04-23 21:38:24 -04:00

262 lines
6.5 KiB
Markdown

# Multi-Tenancy Deployment Guide
## Pre-Deployment Checklist
- [ ] Code review completed
- [ ] All tests passing
- [ ] Database backup created
- [ ] Rollback plan prepared
- [ ] Downtime window scheduled (if needed)
## Deployment Steps
### Step 1: Backup Database
```bash
# SQL Server backup example
BACKUP DATABASE PowderCoatingDb
TO DISK = 'C:\Backups\PowderCoatingDb_PreMultiTenancy.bak'
WITH FORMAT, COMPRESSION;
```
### Step 2: Build Solution
```bash
cd Y:\PCC\PowderCoatingApp
dotnet build
```
Ensure no build errors.
### Step 3: Review Migration
Check the migration file to understand what will happen:
```bash
# View the migration
cat src/PowderCoating.Infrastructure/Migrations/20260205220415_AddMultiTenancy.cs
```
The migration will:
- Create `Companies` table
- Add `CompanyId` columns to all entities
- Add `CompanyRole` to `AspNetUsers`
- Create foreign keys and indexes
### Step 4: Apply Migration
```bash
cd src/PowderCoating.Web
dotnet ef database update --project ../PowderCoating.Infrastructure
```
**Expected output:**
```
Applying migration '20260205220415_AddMultiTenancy'.
Done.
```
### Step 5: Run Seed Data
The seed data will run automatically on application startup and will:
- Create default "Demo Company"
- Create SuperAdmin user
- Create CompanyAdmin user
- Create Manager user
Start the application:
```bash
dotnet run --project src/PowderCoating.Web
```
Watch the logs for:
```
Company 'Demo Company' created
User 'superadmin@powdercoating.com' created
User 'admin@demo.com' created
User 'manager@demo.com' created
```
### Step 6: Verify Migration Success
Connect to the database and verify:
```sql
-- Check Companies table exists
SELECT * FROM Companies;
-- Check CompanyId added to entities
SELECT TOP 5 Id, CompanyId FROM Customers;
SELECT TOP 5 Id, CompanyId FROM Jobs;
-- Check ApplicationUser has CompanyId
SELECT TOP 5 Id, Email, CompanyId, CompanyRole FROM AspNetUsers;
-- Verify foreign keys
SELECT
fk.name AS ForeignKeyName,
OBJECT_NAME(fk.parent_object_id) AS TableName,
COL_NAME(fkc.parent_object_id, fkc.parent_column_id) AS ColumnName,
OBJECT_NAME (fk.referenced_object_id) AS ReferencedTableName
FROM
sys.foreign_keys AS fk
INNER JOIN sys.foreign_key_columns AS fkc
ON fk.object_id = fkc.constraint_object_id
WHERE
OBJECT_NAME(fk.referenced_object_id) = 'Companies';
```
### Step 7: Test Login
Test each user account:
1. **SuperAdmin Login**
- URL: https://localhost:5001/Identity/Account/Login
- Email: superadmin@powdercoating.com
- Password: SuperAdmin123!
- Should see: "Companies" menu in Platform Management
2. **Company Admin Login**
- URL: https://localhost:5001/Identity/Account/Login
- Email: admin@demo.com
- Password: CompanyAdmin123!
- Should see: "Manage Users" menu in Company Settings
- Should see: "Demo Company" badge in header
3. **Manager Login**
- URL: https://localhost:5001/Identity/Account/Login
- Email: manager@demo.com
- Password: Manager123!
- Should see: "Demo Company" badge in header
- Should NOT see: "Manage Users" menu
### Step 8: Verify Data Isolation
1. As **SuperAdmin**, create a second company:
- Navigate to Companies → Create New Company
- Company Name: "Test Company"
- Create admin user for Test Company
2. Login as **Test Company Admin**
- Verify you only see Test Company data
- Create a test customer
3. Login as **Demo Company Admin**
- Verify you CANNOT see Test Company customer
- Verify you only see Demo Company data
4. Login as **SuperAdmin**
- Navigate to Customers
- Should see customers from BOTH companies
## Post-Deployment Verification
### Functional Tests
- [ ] SuperAdmin can create new companies
- [ ] SuperAdmin can see all companies' data
- [ ] Company Admin can create users in their company
- [ ] Company Admin cannot see other companies' data
- [ ] Users can only see data from their own company
- [ ] New entities automatically get CompanyId assigned
- [ ] Query filters work correctly
### Performance Tests
- [ ] Page load times are acceptable
- [ ] No N+1 query issues
- [ ] Indexes are being used (check query plans)
### Security Tests
- [ ] Cannot access other company's data by changing IDs in URL
- [ ] Authorization policies enforce correctly
- [ ] Query filters cannot be bypassed by regular users
## Troubleshooting
### Issue: Migration Fails
**Error: Foreign key constraint conflicts**
This means existing data has CompanyId=0 which doesn't exist.
**Solution:**
```sql
-- Check if default company exists
SELECT * FROM Companies WHERE Id = 1;
-- If no company exists, seed data didn't run
-- Run application to trigger seed, OR manually:
INSERT INTO Companies (CompanyName, CompanyCode, PrimaryContactName, PrimaryContactEmail,
IsActive, SubscriptionStartDate, CreatedAt, CompanyId)
VALUES ('Demo Company', 'DEMO', 'Admin', 'admin@demo.com',
1, GETUTCDATE(), GETUTCDATE(), 0);
-- Update CompanyId to self-reference
UPDATE Companies SET CompanyId = Id WHERE Id = 1;
-- Update existing data
UPDATE Customers SET CompanyId = 1 WHERE CompanyId = 0;
UPDATE Jobs SET CompanyId = 1 WHERE CompanyId = 0;
-- ... repeat for all entities
```
### Issue: Users Can't Login
**Error: "Unable to determine your company"**
This means the user's CompanyId is not set or CompanyId claim is missing.
**Solution:**
```sql
-- Check user's CompanyId
SELECT Id, Email, CompanyId FROM AspNetUsers WHERE Email = 'user@example.com';
-- Update if needed
UPDATE AspNetUsers SET CompanyId = 1 WHERE CompanyId = 0 OR CompanyId IS NULL;
```
Then ensure user re-logs in to get fresh claims.
### Issue: Seeing Other Company's Data
This means query filters aren't working.
**Check:**
1. Is `ITenantContext` registered in DI?
2. Is `ApplicationDbContext` receiving `IHttpContextAccessor` and `IServiceProvider`?
3. Are query filters being applied in `OnModelCreating`?
4. Is the user authenticated and has CompanyId claim?
## Rollback Procedure
If critical issues occur:
```bash
# Rollback migration
cd src/PowderCoating.Web
dotnet ef database update 20260205163837_InitialCreate --project ../PowderCoating.Infrastructure
# Restore database backup
RESTORE DATABASE PowderCoatingDb
FROM DISK = 'C:\Backups\PowderCoatingDb_PreMultiTenancy.bak'
WITH REPLACE;
# Revert code changes
git revert <commit-hash>
```
## Support Contacts
- Lead Developer: [Your Name]
- Database Admin: [DBA Name]
- DevOps: [DevOps Contact]
## Post-Deployment Tasks
- [ ] Monitor application logs for errors
- [ ] Monitor database performance
- [ ] Update documentation
- [ ] Train users on new multi-tenancy features
- [ ] Schedule follow-up review (1 week)