const fs = require('fs'); const path = require('path'); const { executeQuery, executeTransaction } = require('../database'); /** * Run all database migrations */ async function runMigrations() { console.log('🚀 Starting database migrations...'); try { // Create migrations table to track what's been run await executeQuery(` CREATE TABLE IF NOT EXISTS migrations ( id SERIAL PRIMARY KEY, filename VARCHAR(255) NOT NULL UNIQUE, executed_at BIGINT NOT NULL DEFAULT EXTRACT(epoch FROM NOW()) * 1000 ); `); // Get list of executed migrations const executedMigrations = await executeQuery( 'SELECT filename FROM migrations ORDER BY executed_at' ); const executedFiles = new Set(executedMigrations.map(m => m.filename)); // Read migration files const migrationsDir = path.join(__dirname); const migrationFiles = fs.readdirSync(migrationsDir) .filter(file => file.endsWith('.sql')) .sort(); console.log(`📁 Found ${migrationFiles.length} migration files`); for (const file of migrationFiles) { if (executedFiles.has(file)) { console.log(`⏭️ Skipping ${file} (already executed)`); continue; } console.log(`🔄 Executing ${file}...`); const filePath = path.join(migrationsDir, file); const sql = fs.readFileSync(filePath, 'utf8'); await executeTransaction(async (client) => { // Execute the migration SQL if (process.env.USE_NEON_SERVERLESS === 'true') { // For Neon serverless, split by statements and execute individually const statements = sql .split(';') .map(s => s.trim()) .filter(s => s.length > 0); for (const statement of statements) { if (statement.trim()) { await client(statement); } } } else { await client.query(sql); } // Record that this migration was executed if (process.env.USE_NEON_SERVERLESS === 'true') { await client('INSERT INTO migrations (filename) VALUES ($1)', [file]); } else { await client.query('INSERT INTO migrations (filename) VALUES ($1)', [file]); } }); console.log(`✅ Completed ${file}`); } console.log('🎉 All migrations completed successfully!'); } catch (error) { console.error('❌ Migration failed:', error); throw error; } } /** * Create a new migration file * @param {string} name - Migration name */ function createMigration(name) { const timestamp = new Date().toISOString().replace(/[:.]/g, '-').split('T')[0]; const filename = `${timestamp}_${name}.sql`; const filepath = path.join(__dirname, filename); const template = `-- Migration: ${filename} -- Description: ${name} -- Add your SQL here `; fs.writeFileSync(filepath, template); console.log(`📝 Created migration: ${filename}`); return filename; } // Run migrations if this file is executed directly if (require.main === module) { const command = process.argv[2]; if (command === 'create') { const name = process.argv[3]; if (!name) { console.error('❌ Please provide a migration name: npm run db:migrate create migration_name'); process.exit(1); } createMigration(name); } else { runMigrations() .then(() => { console.log('🏁 Migration process completed'); process.exit(0); }) .catch(error => { console.error('💥 Migration process failed:', error); process.exit(1); }); } } module.exports = { runMigrations, createMigration };