57 lines
1.6 KiB
TypeScript
57 lines
1.6 KiB
TypeScript
import { readdir, readFile } from 'node:fs/promises';
|
|
import path from 'node:path';
|
|
import { fileURLToPath } from 'node:url';
|
|
import { closePool, query } from './pool.js';
|
|
|
|
const migrationsDir = path.resolve(process.cwd(), 'migrations');
|
|
|
|
async function ensureMigrationTable(): Promise<void> {
|
|
await query(`
|
|
CREATE TABLE IF NOT EXISTS schema_migrations (
|
|
filename VARCHAR(255) PRIMARY KEY,
|
|
applied_at TIMESTAMP NOT NULL DEFAULT now()
|
|
)
|
|
`);
|
|
}
|
|
|
|
async function appliedMigrations(): Promise<Set<string>> {
|
|
const result = await query<{ filename: string }>('SELECT filename FROM schema_migrations');
|
|
return new Set(result.rows.map((row) => row.filename));
|
|
}
|
|
|
|
export async function runMigrations(): Promise<void> {
|
|
await ensureMigrationTable();
|
|
const applied = await appliedMigrations();
|
|
const files = (await readdir(migrationsDir))
|
|
.filter((file) => file.endsWith('.sql'))
|
|
.sort();
|
|
|
|
for (const file of files) {
|
|
if (applied.has(file)) continue;
|
|
|
|
const sql = await readFile(path.join(migrationsDir, file), 'utf8');
|
|
await query('BEGIN');
|
|
try {
|
|
await query(sql);
|
|
await query('INSERT INTO schema_migrations (filename) VALUES ($1)', [file]);
|
|
await query('COMMIT');
|
|
console.log(`Applied migration ${file}`);
|
|
} catch (error) {
|
|
await query('ROLLBACK');
|
|
throw error;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
runMigrations()
|
|
.then(async () => {
|
|
await closePool();
|
|
})
|
|
.catch(async (error: unknown) => {
|
|
console.error(error);
|
|
await closePool();
|
|
process.exitCode = 1;
|
|
});
|
|
}
|