303 lines
7.9 KiB
Markdown
303 lines
7.9 KiB
Markdown
# Tenant Migration Guide
|
|
|
|
## Quick Start
|
|
|
|
### Create a New Migration
|
|
```bash
|
|
cd backend
|
|
npm run migrate:make add_your_feature_name
|
|
```
|
|
|
|
Edit the generated file in `backend/migrations/tenant/`
|
|
|
|
### Test on Single Tenant
|
|
```bash
|
|
npm run migrate:tenant acme-corp
|
|
```
|
|
|
|
### Apply to All Tenants
|
|
```bash
|
|
npm run migrate:all-tenants
|
|
```
|
|
|
|
## Available Commands
|
|
|
|
| Command | Description |
|
|
|---------|-------------|
|
|
| `npm run migrate:make <name>` | Create a new migration file |
|
|
| `npm run migrate:tenant <slug>` | Run migrations for a specific tenant |
|
|
| `npm run migrate:all-tenants` | Run migrations for all active tenants |
|
|
| `npm run migrate:latest` | Run migrations (default DB - rarely used) |
|
|
| `npm run migrate:rollback` | Rollback last migration (default DB) |
|
|
|
|
## Architecture
|
|
|
|
### Multi-Tenant Database Structure
|
|
|
|
```
|
|
┌─────────────────────────┐
|
|
│ Central Database │
|
|
│ │
|
|
│ - tenants table │
|
|
│ - users table │
|
|
│ - (encrypted creds) │
|
|
└─────────────────────────┘
|
|
│
|
|
│ manages
|
|
│
|
|
┌───────┴────────┐
|
|
│ │
|
|
┌───▼────┐ ┌───▼────┐
|
|
│ Tenant │ │ Tenant │
|
|
│ DB1 │ ... │ DBN │
|
|
│ │ │ │
|
|
│ - users│ │ - users│
|
|
│ - roles│ │ - roles│
|
|
│ - apps │ │ - apps │
|
|
│ - ... │ │ - ... │
|
|
└────────┘ └────────┘
|
|
```
|
|
|
|
### How Migrations Work
|
|
|
|
1. **New Tenant Provisioning** (Automatic)
|
|
- User creates tenant via API
|
|
- `TenantProvisioningService.provisionTenant()` is called
|
|
- Database is created
|
|
- All migrations in `migrations/tenant/` are automatically run
|
|
- Tenant status set to ACTIVE
|
|
|
|
2. **Existing Tenants** (Manual)
|
|
- Developer creates new migration file
|
|
- Tests on single tenant: `npm run migrate:tenant test-tenant`
|
|
- Applies to all: `npm run migrate:all-tenants`
|
|
- Each tenant database is updated independently
|
|
|
|
### Migration Scripts
|
|
|
|
#### `migrate-tenant.ts`
|
|
- Accepts tenant slug or ID as argument
|
|
- Fetches tenant from central database
|
|
- Decrypts database password
|
|
- Creates Knex connection to tenant DB
|
|
- Runs pending migrations
|
|
- Reports success/failure
|
|
|
|
#### `migrate-all-tenants.ts`
|
|
- Fetches all ACTIVE tenants from central DB
|
|
- Iterates through each tenant
|
|
- Runs migrations sequentially
|
|
- Collects success/failure results
|
|
- Provides comprehensive summary
|
|
- Exits with error if any tenant fails
|
|
|
|
## Security
|
|
|
|
### Password Encryption
|
|
|
|
Tenant database passwords are encrypted using **AES-256-CBC** and stored in the central database.
|
|
|
|
**Required Environment Variable:**
|
|
```bash
|
|
DB_ENCRYPTION_KEY=your-32-character-secret-key!!
|
|
```
|
|
|
|
This key must:
|
|
- Be exactly 32 characters (256 bits)
|
|
- Match the key used by backend services
|
|
- Be kept secure (never commit to git)
|
|
- Be the same across all environments accessing tenant DBs
|
|
|
|
### Encryption Flow
|
|
|
|
```
|
|
Tenant Creation:
|
|
Plain Password → Encrypt → Store in Central DB
|
|
|
|
Migration Time:
|
|
Encrypted Password → Decrypt → Connect to Tenant DB → Run Migrations
|
|
```
|
|
|
|
## Example Workflow
|
|
|
|
### Adding a New Field to All Tenants
|
|
|
|
```bash
|
|
# 1. Create migration
|
|
cd backend
|
|
npm run migrate:make add_priority_to_tasks
|
|
|
|
# 2. Edit the migration file
|
|
# migrations/tenant/20250127120000_add_priority_to_tasks.js
|
|
|
|
# 3. Test on staging tenant
|
|
npm run migrate:tenant staging-company
|
|
|
|
# 4. Verify it worked
|
|
# Connect to staging DB and check schema
|
|
|
|
# 5. Apply to all tenants
|
|
npm run migrate:all-tenants
|
|
```
|
|
|
|
Expected output:
|
|
```
|
|
🚀 Starting migration for all tenants...
|
|
|
|
📋 Found 5 active tenant(s)
|
|
|
|
🔄 Migrating tenant: Acme Corp (acme_corp_db)
|
|
✅ Acme Corp: Ran 1 migrations:
|
|
- 20250127120000_add_priority_to_tasks.js
|
|
|
|
🔄 Migrating tenant: TechStart (techstart_db)
|
|
✅ TechStart: Ran 1 migrations:
|
|
- 20250127120000_add_priority_to_tasks.js
|
|
|
|
...
|
|
|
|
============================================================
|
|
📊 Migration Summary
|
|
============================================================
|
|
✅ Successful: 5
|
|
❌ Failed: 0
|
|
|
|
🎉 All tenant migrations completed successfully!
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Error: "Cannot find module '../prisma/generated-central/client'"
|
|
|
|
**Solution:** Generate Prisma client
|
|
```bash
|
|
cd backend
|
|
npx prisma generate --schema=prisma/schema-central.prisma
|
|
```
|
|
|
|
### Error: "Invalid encrypted password format"
|
|
|
|
**Solution:** Check `DB_ENCRYPTION_KEY` environment variable matches the one used for encryption.
|
|
|
|
### Error: "Migration failed: Table already exists"
|
|
|
|
**Cause:** Migration was partially applied or run manually
|
|
|
|
**Solution:**
|
|
```bash
|
|
# Check migration status in tenant DB
|
|
mysql -h <host> -u <user> -p<pass> <dbname> -e "SELECT * FROM knex_migrations"
|
|
|
|
# If migration is listed, it's already applied
|
|
# If not, investigate why table exists and fix manually
|
|
```
|
|
|
|
### Migration Hangs
|
|
|
|
**Possible causes:**
|
|
- Network connection to database lost
|
|
- Database server down
|
|
- Migration has long-running query
|
|
|
|
**Solution:** Add timeout to migration and check database connectivity
|
|
|
|
## Best Practices
|
|
|
|
1. ✅ **Test first**: Always test migrations on a single tenant before applying to all
|
|
2. ✅ **Rollback ready**: Write `down()` functions for every migration
|
|
3. ✅ **Idempotent**: Use `IF NOT EXISTS` clauses where possible
|
|
4. ✅ **Backup**: Take database backups before major migrations
|
|
5. ✅ **Monitor**: Watch the output of `migrate:all-tenants` carefully
|
|
6. ✅ **Version control**: Commit migration files to git
|
|
7. ✅ **Document**: Add comments explaining complex migrations
|
|
|
|
8. ❌ **Don't skip testing**: Never run untested migrations on production
|
|
9. ❌ **Don't modify**: Never modify existing migration files after they're deployed
|
|
10. ❌ **Don't forget down()**: Always implement rollback logic
|
|
|
|
## Integration with TenantProvisioningService
|
|
|
|
The migrations are also used during tenant provisioning:
|
|
|
|
```typescript
|
|
// src/tenant/tenant-provisioning.service.ts
|
|
|
|
async provisionTenant(tenantId: string): Promise<void> {
|
|
// ... create database ...
|
|
|
|
// Run migrations automatically
|
|
await this.runTenantMigrations(tenant);
|
|
|
|
// ... update tenant status ...
|
|
}
|
|
|
|
async runTenantMigrations(tenant: any): Promise<void> {
|
|
const knexConfig = {
|
|
client: 'mysql2',
|
|
connection: {
|
|
host: tenant.dbHost,
|
|
port: tenant.dbPort,
|
|
user: tenant.dbUser,
|
|
password: decryptedPassword,
|
|
database: tenant.dbName,
|
|
},
|
|
migrations: {
|
|
directory: './migrations/tenant',
|
|
},
|
|
};
|
|
|
|
const knexInstance = knex(knexConfig);
|
|
await knexInstance.migrate.latest();
|
|
await knexInstance.destroy();
|
|
}
|
|
```
|
|
|
|
This ensures every new tenant starts with the complete schema.
|
|
|
|
## CI/CD Integration
|
|
|
|
### Docker Compose
|
|
```yaml
|
|
services:
|
|
backend:
|
|
image: your-backend:latest
|
|
command: sh -c "npm run migrate:all-tenants && npm run start:prod"
|
|
environment:
|
|
- DB_ENCRYPTION_KEY=${DB_ENCRYPTION_KEY}
|
|
```
|
|
|
|
### Kubernetes Job
|
|
```yaml
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: tenant-migrations
|
|
spec:
|
|
template:
|
|
spec:
|
|
containers:
|
|
- name: migrate
|
|
image: your-backend:latest
|
|
command: ["npm", "run", "migrate:all-tenants"]
|
|
env:
|
|
- name: DB_ENCRYPTION_KEY
|
|
valueFrom:
|
|
secretKeyRef:
|
|
name: db-secrets
|
|
key: encryption-key
|
|
restartPolicy: OnFailure
|
|
```
|
|
|
|
## Further Documentation
|
|
|
|
- [Backend Scripts README](backend/scripts/README.md) - Detailed script documentation
|
|
- [Multi-Tenant Implementation](MULTI_TENANT_IMPLEMENTATION.md) - Architecture overview
|
|
- [Multi-Tenant Migration](MULTI_TENANT_MIGRATION.md) - Migration strategy
|
|
|
|
## Support
|
|
|
|
For questions or issues:
|
|
1. Check the [Backend Scripts README](backend/scripts/README.md)
|
|
2. Review existing migration files in `backend/migrations/tenant/`
|
|
3. Check Knex documentation: https://knexjs.org/guide/migrations.html
|