Added auth functionality, initial work with views and field types
This commit is contained in:
181
backend/scripts/check-migration-status.ts
Normal file
181
backend/scripts/check-migration-status.ts
Normal file
@@ -0,0 +1,181 @@
|
||||
import { PrismaClient as CentralPrismaClient } from '.prisma/central';
|
||||
import knex, { Knex } from 'knex';
|
||||
import { createDecipheriv } from 'crypto';
|
||||
|
||||
// Encryption configuration
|
||||
const ALGORITHM = 'aes-256-cbc';
|
||||
|
||||
/**
|
||||
* Decrypt a tenant's database password
|
||||
*/
|
||||
function decryptPassword(encryptedPassword: string): string {
|
||||
try {
|
||||
// Check if password is already plaintext (for legacy/development)
|
||||
if (!encryptedPassword.includes(':')) {
|
||||
return encryptedPassword;
|
||||
}
|
||||
|
||||
const key = Buffer.from(process.env.ENCRYPTION_KEY, 'hex');
|
||||
const parts = encryptedPassword.split(':');
|
||||
if (parts.length !== 2) {
|
||||
throw new Error('Invalid encrypted password format');
|
||||
}
|
||||
|
||||
const iv = Buffer.from(parts[0], 'hex');
|
||||
const encrypted = parts[1];
|
||||
|
||||
const decipher = createDecipheriv(ALGORITHM, key, iv);
|
||||
|
||||
let decrypted = decipher.update(encrypted, 'hex', 'utf8');
|
||||
decrypted += decipher.final('utf8');
|
||||
|
||||
return decrypted;
|
||||
} catch (error) {
|
||||
console.error('Error decrypting password:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a Knex connection for a specific tenant
|
||||
*/
|
||||
function createTenantKnexConnection(tenant: any): Knex {
|
||||
const decryptedPassword = decryptPassword(tenant.dbPassword);
|
||||
|
||||
return knex({
|
||||
client: 'mysql2',
|
||||
connection: {
|
||||
host: tenant.dbHost,
|
||||
port: tenant.dbPort,
|
||||
user: tenant.dbUsername,
|
||||
password: decryptedPassword,
|
||||
database: tenant.dbName,
|
||||
},
|
||||
migrations: {
|
||||
tableName: 'knex_migrations',
|
||||
directory: './migrations/tenant',
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Get migration status for a specific tenant
|
||||
*/
|
||||
async function getTenantMigrationStatus(tenant: any): Promise<{
|
||||
completed: string[];
|
||||
pending: string[];
|
||||
}> {
|
||||
const tenantKnex = createTenantKnexConnection(tenant);
|
||||
|
||||
try {
|
||||
const [completed, pending] = await tenantKnex.migrate.list();
|
||||
return {
|
||||
completed: completed[1] || [],
|
||||
pending: pending || [],
|
||||
};
|
||||
} catch (error) {
|
||||
throw error;
|
||||
} finally {
|
||||
await tenantKnex.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check migration status across all tenants
|
||||
*/
|
||||
async function checkMigrationStatus() {
|
||||
console.log('🔍 Checking migration status for all tenants...\n');
|
||||
|
||||
const centralPrisma = new CentralPrismaClient();
|
||||
|
||||
try {
|
||||
// Fetch all active tenants
|
||||
const tenants = await centralPrisma.tenant.findMany({
|
||||
where: {
|
||||
status: 'ACTIVE',
|
||||
},
|
||||
orderBy: {
|
||||
name: 'asc',
|
||||
},
|
||||
});
|
||||
|
||||
if (tenants.length === 0) {
|
||||
console.log('⚠️ No active tenants found.');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log(`📋 Found ${tenants.length} active tenant(s)\n`);
|
||||
console.log('='.repeat(80));
|
||||
|
||||
let allUpToDate = true;
|
||||
const tenantsWithPending: { name: string; pending: string[] }[] = [];
|
||||
|
||||
// Check each tenant
|
||||
for (const tenant of tenants) {
|
||||
try {
|
||||
const status = await getTenantMigrationStatus(tenant);
|
||||
|
||||
console.log(`\n📦 ${tenant.name} (${tenant.slug})`);
|
||||
console.log(` Database: ${tenant.dbName}`);
|
||||
console.log(` Completed: ${status.completed.length} migration(s)`);
|
||||
|
||||
if (status.pending.length > 0) {
|
||||
allUpToDate = false;
|
||||
console.log(` ⚠️ Pending: ${status.pending.length} migration(s)`);
|
||||
status.pending.forEach((migration) => {
|
||||
console.log(` - ${migration}`);
|
||||
});
|
||||
tenantsWithPending.push({
|
||||
name: tenant.name,
|
||||
pending: status.pending,
|
||||
});
|
||||
} else {
|
||||
console.log(` ✅ Up to date`);
|
||||
}
|
||||
|
||||
// Show last 3 completed migrations
|
||||
if (status.completed.length > 0) {
|
||||
const recent = status.completed.slice(-3);
|
||||
console.log(` Recent migrations:`);
|
||||
recent.forEach((migration) => {
|
||||
console.log(` - ${migration}`);
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(`\n❌ ${tenant.name}: Failed to check status`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
allUpToDate = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Print summary
|
||||
console.log('\n' + '='.repeat(80));
|
||||
console.log('📊 Summary');
|
||||
console.log('='.repeat(80));
|
||||
|
||||
if (allUpToDate) {
|
||||
console.log('✅ All tenants are up to date!');
|
||||
} else {
|
||||
console.log(`⚠️ ${tenantsWithPending.length} tenant(s) have pending migrations:\n`);
|
||||
tenantsWithPending.forEach(({ name, pending }) => {
|
||||
console.log(` ${name}: ${pending.length} pending`);
|
||||
});
|
||||
console.log('\n💡 Run: npm run migrate:all-tenants');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Fatal error:', error);
|
||||
process.exit(1);
|
||||
} finally {
|
||||
await centralPrisma.$disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Run the status check
|
||||
checkMigrationStatus()
|
||||
.then(() => {
|
||||
process.exit(0);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Unhandled error:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
Reference in New Issue
Block a user