Dealer_Onboarding_Backend/scripts/migrate.ts

99 lines
3.4 KiB
TypeScript

/**
* Database Migration Script — destructive fresh sync.
*
* Drops every table and recreates the schema from Sequelize model definitions
* in `src/database/models/`. After the fresh schema is in place, every
* versioned migration file under `scripts/migrations/` is automatically
* stamped into the `migrations` table as "already applied" so subsequent
* `npm run migrate:up` runs on this DB will be no-ops until a newer
* migration is added.
*
* For incremental schema changes on environments that already hold data,
* use `npm run migrate:up` instead.
*
* Run: npx tsx scripts/migrate.ts
* Flags:
* --no-baseline Skip stamping migration files as applied (advanced).
*/
import 'dotenv/config';
import { promises as fs } from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { createHash } from 'crypto';
import db from '../src/database/models/index.js';
const MIGRATIONS_DIR = path.resolve(
path.dirname(fileURLToPath(import.meta.url)),
'migrations'
);
async function discoverMigrations(): Promise<string[]> {
try {
const entries = await fs.readdir(MIGRATIONS_DIR);
return entries
.filter((name) => name.endsWith('.ts') && !name.startsWith('_'))
.sort();
} catch {
return [];
}
}
async function fileChecksum(file: string): Promise<string> {
const buf = await fs.readFile(path.join(MIGRATIONS_DIR, file));
return createHash('sha256').update(buf).digest('hex');
}
async function baselineMigrationsTable(): Promise<void> {
const files = await discoverMigrations();
if (files.length === 0) {
console.log('No versioned migrations to baseline.');
return;
}
console.log(`📌 Stamping ${files.length} migration(s) as already-applied:`);
for (const file of files) {
const name = file.replace(/\.ts$/, '');
const checksum = await fileChecksum(file);
await db.Migration.create({ name, checksum });
console.log(` + ${name}`);
}
}
async function runMigrations() {
console.log('🔄 Starting database synchronization (Fresh Startup)...\n');
console.log('⚠️ WARNING: This will drop all existing tables in the database.\n');
const skipBaseline = process.argv.includes('--no-baseline');
try {
await db.sequelize.authenticate();
console.log('📡 Connected to the database successfully.');
// force: true drops existing tables — schema is rebuilt exactly from
// Sequelize models, so every enum / column / index matches code.
await db.sequelize.sync({ force: true });
console.log('\n✅ All tables created and synchronized successfully!');
console.log('----------------------------------------------------');
const modelNames = Object.keys(db).filter((k) => k !== 'sequelize' && k !== 'Sequelize');
console.log(`Available Models (${modelNames.length}): ${modelNames.join(', ')}`);
console.log('----------------------------------------------------\n');
if (!skipBaseline) {
await baselineMigrationsTable();
} else {
console.log('Skipping migration baseline (--no-baseline).');
}
process.exit(0);
} catch (error: any) {
console.error('\n❌ Migration failed:', error.message);
if (error.stack) {
console.error('\nStack Trace:\n', error.stack);
}
process.exit(1);
}
}
runMigrations();