NODEDC_TASKMANAGER_CODEXAPI/src/db/migrations.ts

62 lines
1.7 KiB
TypeScript

import { promises as fs } from "node:fs";
import path from "node:path";
import type { Pool } from "pg";
export type AppliedMigration = {
id: string;
};
type MigrationFile = {
id: string;
sql: string;
};
async function loadMigrationFiles(migrationsDir: string): Promise<MigrationFile[]> {
const files = (await fs.readdir(migrationsDir)).filter((file) => file.endsWith(".sql")).sort();
return Promise.all(
files.map(async (file) => ({
id: file,
sql: await fs.readFile(path.join(migrationsDir, file), "utf8"),
}))
);
}
export async function runMigrations(pool: Pool, migrationsDir = path.resolve(process.cwd(), "migrations")): Promise<AppliedMigration[]> {
const migrations = await loadMigrationFiles(migrationsDir);
const client = await pool.connect();
try {
await client.query("BEGIN");
await client.query(`
CREATE TABLE IF NOT EXISTS schema_migrations (
id text PRIMARY KEY,
applied_at timestamptz NOT NULL DEFAULT now()
)
`);
const appliedRows = await client.query<{ id: string }>("SELECT id FROM schema_migrations");
const appliedIds = new Set(appliedRows.rows.map((row) => row.id));
const newlyApplied: AppliedMigration[] = [];
for (const migration of migrations) {
if (appliedIds.has(migration.id)) {
continue;
}
await client.query(migration.sql);
await client.query("INSERT INTO schema_migrations(id) VALUES ($1)", [migration.id]);
newlyApplied.push({ id: migration.id });
}
await client.query("COMMIT");
return newlyApplied;
} catch (error) {
await client.query("ROLLBACK");
throw error;
} finally {
client.release();
}
}