const { QueryInterface } = require('sequelize'); const fs = require('fs').promises; const path = require('path'); /** * Simple migration runner for Sequelize * Checks which migrations have been run and executes pending ones */ class MigrationRunner { constructor(sequelize) { this.sequelize = sequelize; this.migrationsPath = path.join(__dirname); } async ensureMigrationsTable() { const queryInterface = this.sequelize.getQueryInterface(); // Check if migrations table exists const [results] = await this.sequelize.query(` SELECT EXISTS ( SELECT FROM information_schema.tables WHERE table_schema = 'public' AND table_name = 'SequelizeMeta' ); `); if (!results[0].exists) { // Create migrations table await queryInterface.createTable('SequelizeMeta', { name: { type: require('sequelize').DataTypes.STRING, allowNull: false, primaryKey: true } }); } } async getExecutedMigrations() { await this.ensureMigrationsTable(); const [results] = await this.sequelize.query( 'SELECT name FROM "SequelizeMeta" ORDER BY name' ); return results.map(row => row.name); } async getAllMigrations() { const files = await fs.readdir(this.migrationsPath); return files .filter(file => file.endsWith('.js') && file !== 'runner.js') .sort(); } async runMigrations() { const executed = await this.getExecutedMigrations(); const allMigrations = await this.getAllMigrations(); const pending = allMigrations.filter(m => !executed.includes(m)); if (pending.length === 0) { console.log('No pending migrations'); return; } console.log(`Running ${pending.length} pending migration(s)...`); for (const migrationFile of pending) { const migration = require(path.join(this.migrationsPath, migrationFile)); if (!migration.up || typeof migration.up !== 'function') { throw new Error(`Migration ${migrationFile} does not export an 'up' function`); } const queryInterface = this.sequelize.getQueryInterface(); const transaction = await this.sequelize.transaction(); try { console.log(`Running migration: ${migrationFile}`); await migration.up(queryInterface, this.sequelize.constructor); // Record migration as executed await this.sequelize.query( `INSERT INTO "SequelizeMeta" (name) VALUES (:name)`, { replacements: { name: migrationFile }, transaction } ); await transaction.commit(); console.log(`Completed migration: ${migrationFile}`); } catch (error) { await transaction.rollback(); throw new Error(`Migration ${migrationFile} failed: ${error.message}`); } } console.log('All migrations completed successfully'); } } module.exports = MigrationRunner;