Files
auditly/functions/migrations/migrate.js
2025-09-22 20:05:51 -07:00

131 lines
4.2 KiB
JavaScript

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
};