satanic-security/migrations.js

70 lines
2.2 KiB
JavaScript

const fs = require('fs');
const path = require('path');
module.exports = async function runMigrations(db) {
// Promisify db.run and db.get for easier async/await usage
function run(sql, params = []) {
return new Promise((resolve, reject) => {
db.run(sql, params, function (err) {
if (err) reject(err);
else resolve(this);
});
});
}
function get(sql, params = []) {
return new Promise((resolve, reject) => {
db.get(sql, params, function (err, row) {
if (err) reject(err);
else resolve(row);
});
});
}
function all(sql, params = []) {
return new Promise((resolve, reject) => {
db.all(sql, params, function (err, rows) {
if (err) reject(err);
else resolve(rows);
});
});
}
// 1. Ensure migrations table exists
await run(`
CREATE TABLE IF NOT EXISTS migrations (
name TEXT PRIMARY KEY,
run_at DATETIME DEFAULT CURRENT_TIMESTAMP
)
`);
// 2. Read all migration files
const migrationsDir = path.join(__dirname, 'migrations');
let files = [];
try {
files = fs.readdirSync(migrationsDir)
.filter(f => f.endsWith('.js'))
.sort();
} catch (e) {
// If directory doesn't exist, skip
if (e.code !== 'ENOENT') throw e;
}
// 3. Get already run migrations
const appliedRows = await all('SELECT name FROM migrations');
const applied = new Set(appliedRows.map(row => row.name));
// 4. Run pending migrations
for (const file of files) {
if (!applied.has(file)) {
const migration = require(path.join(migrationsDir, file));
if (typeof migration !== 'function') {
throw new Error(`Migration file ${file} does not export a function`);
}
console.log(`Running migration: ${file}`);
await migration(db, run, get, all);
// Mark migration as applied
await run('INSERT INTO migrations (name) VALUES (?)', [file]);
console.log(`Migration applied: ${file}`);
}
}
};