import type { DatabaseSync } from "node:sqlite"; import { up as up0001CommentLikes } from "./migrations/0001_comment_likes.ts"; import { up as up0002DumpThumbnail } from "./migrations/0002_dump_thumbnail.ts"; interface Migration { name: string; up: (db: DatabaseSync) => void; } // Append-only — never edit or reorder a migration once it has shipped. // Each `up` must be idempotent: it also runs against fresh databases created // straight from schema.sql, where the change it makes already exists. const MIGRATIONS: Migration[] = [ { name: "0001_comment_likes", up: up0001CommentLikes }, { name: "0002_dump_thumbnail", up: up0002DumpThumbnail }, ]; export function runMigrations(db: DatabaseSync): void { db.exec(`CREATE TABLE IF NOT EXISTS schema_migrations ( name TEXT PRIMARY KEY, applied_at TEXT NOT NULL );`); const applied = new Set( (db.prepare(`SELECT name FROM schema_migrations;`).all() as { name: string; }[]).map((r) => r.name), ); for (const { name, up } of MIGRATIONS) { if (applied.has(name)) continue; db.exec("BEGIN;"); try { up(db); db.prepare( `INSERT INTO schema_migrations (name, applied_at) VALUES (?, ?);`, ).run(name, new Date().toISOString()); db.exec("COMMIT;"); console.log(`[migrate] applied ${name}`); } catch (err) { db.exec("ROLLBACK;"); throw err; } } }