Files
mini/src/lib/db/migrate.ts

101 lines
2.5 KiB
TypeScript

import { pool } from '../db';
/**
* Migration System for God Mode
* Handles transactional execution of SQL migration files
*/
export interface MigrationResult {
success: boolean;
migrationsRun: number;
error?: string;
rolledBack?: boolean;
}
/**
* Run multiple SQL commands in a single transaction
* Automatically rolls back if ANY command fails
*/
export async function runMigrations(sqlCommands: string[]): Promise<MigrationResult> {
const client = await pool.connect();
let migrationsRun = 0;
try {
await client.query('BEGIN');
console.log('🔱 [Migration] Starting transaction...');
for (const command of sqlCommands) {
// Skip empty commands or comments
const trimmed = command.trim();
if (!trimmed || trimmed.startsWith('--')) {
continue;
}
console.log(`[Migration] Executing: ${trimmed.substring(0, 100)}...`);
await client.query(trimmed);
migrationsRun++;
}
await client.query('COMMIT');
console.log(`✅ [Migration] Successfully committed ${migrationsRun} migrations`);
return {
success: true,
migrationsRun
};
} catch (error: any) {
await client.query('ROLLBACK');
console.error('❌ [Migration] Error - Rolling back all changes:', error.message);
return {
success: false,
migrationsRun,
error: error.message,
rolledBack: true
};
} finally {
client.release();
}
}
/**
* Run a single large SQL file (like migrations)
* Splits by semicolon and runs each statement in transaction
*/
export async function runMigrationFile(sqlContent: string): Promise<MigrationResult> {
// Split by semicolon, but be smart about it
const statements = sqlContent
.split(';')
.map(s => s.trim())
.filter(s => s.length > 0);
return runMigrations(statements);
}
/**
* Check if migrations have been run
*/
export async function getMigrationStatus(): Promise<{
tables: string[];
lastMigration?: Date;
}> {
try {
const result = await pool.query(`
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'public'
ORDER BY table_name
`);
return {
tables: result.rows.map((r: { table_name: string }) => r.table_name)
};
} catch (error) {
return {
tables: []
};
}
}