/** * Database Migration System * Handles reading and executing SQL migrations */ import { readdir, readFile } from 'fs/promises'; import { join } from 'path'; import { query } from './connection'; /** * Run all pending migrations */ export async function runMigrations(): Promise { console.log('Starting database migrations...'); try { // Ensure schema_migrations table exists await query(` CREATE TABLE IF NOT EXISTS schema_migrations ( version INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL, executed_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ) `); // Get all migration files const migrationsDir = join(__dirname, 'migrations'); const files = await readdir(migrationsDir); const migrationFiles = files .filter((f) => f.endsWith('.sql')) .sort(); if (migrationFiles.length === 0) { console.log('No migration files found'); return; } // Get executed migrations const executedResult = await query<{ version: number }>( 'SELECT version FROM schema_migrations ORDER BY version' ); const executedVersions = new Set(executedResult.rows.map((r) => r.version)); // Execute pending migrations let executedCount = 0; for (const file of migrationFiles) { const match = file.match(/^(\d+)_/); if (!match) continue; const version = parseInt(match[1], 10); if (executedVersions.has(version)) { console.log(`✓ Migration ${version} already executed`); continue; } try { const filePath = join(migrationsDir, file); const sqlContent = await readFile(filePath, 'utf-8'); // Split into individual statements (simple approach) const statements = sqlContent .split(';') .map((s) => s.trim()) .filter((s) => s && !s.startsWith('--')); for (const statement of statements) { await query(statement + ';'); } console.log(`✓ Migration ${version} executed: ${file}`); executedCount++; } catch (error) { console.error(`✗ Migration ${version} failed:`, error); throw error; } } console.log( `✓ Database migrations completed (${executedCount} new migrations executed)` ); } catch (error) { console.error('Migration failed:', error); throw error; } } /** * Get migration status */ export async function getMigrationStatus(): Promise<{ executed: number; total: number; pending: string[]; }> { try { const migrationsDir = join(__dirname, 'migrations'); const files = await readdir(migrationsDir); const migrationFiles = files.filter((f) => f.endsWith('.sql')).sort(); const executedResult = await query<{ version: number; name: string }>( 'SELECT version, name FROM schema_migrations ORDER BY version' ); const executedVersions = new Set(executedResult.rows.map((r) => r.version)); const pending: string[] = []; for (const file of migrationFiles) { const match = file.match(/^(\d+)_/); if (!match) continue; const version = parseInt(match[1], 10); if (!executedVersions.has(version)) { pending.push(file); } } return { executed: executedResult.rows.length, total: migrationFiles.length, pending, }; } catch (error) { console.error('Failed to get migration status:', error); return { executed: 0, total: 0, pending: [], }; } }