feat(db): add lightweight migration system
Introduce server-migrations.ts with a named-migration runner that tracks applied migrations in a _migrations table. runMigrations(db) is called at startup before routes, so additive schema changes (ALTER TABLE, new settings) are applied once and skipped on subsequent restarts. Update ARCHITECTURE.md: five inline edits + new §4.4 documenting the convention.
This commit is contained in:
40
server-migrations.ts
Normal file
40
server-migrations.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import Database from 'better-sqlite3';
|
||||
|
||||
interface Migration {
|
||||
id: string; // unique, immutable — format: NNNN_short_description
|
||||
up: (db: InstanceType<typeof Database>) => void;
|
||||
}
|
||||
|
||||
// Append only. Never reorder or remove entries — that would corrupt tracking.
|
||||
// Each `up` function receives the open DB handle inside an already-open transaction.
|
||||
const migrations: Migration[] = [
|
||||
// Example:
|
||||
// {
|
||||
// id: '0001_bookings_add_color',
|
||||
// up: (db) => {
|
||||
// db.exec(`ALTER TABLE bookings ADD COLUMN color TEXT NOT NULL DEFAULT 'blue'`);
|
||||
// },
|
||||
// },
|
||||
];
|
||||
|
||||
export function runMigrations(db: InstanceType<typeof Database>): void {
|
||||
db.exec(`
|
||||
CREATE TABLE IF NOT EXISTS _migrations (
|
||||
id TEXT PRIMARY KEY,
|
||||
applied_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||
)
|
||||
`);
|
||||
|
||||
const isApplied = db.prepare('SELECT 1 FROM _migrations WHERE id = ?');
|
||||
const markApplied = db.prepare('INSERT INTO _migrations (id) VALUES (?)');
|
||||
|
||||
for (const migration of migrations) {
|
||||
if (isApplied.get(migration.id)) continue;
|
||||
console.log(`[Migrations] Applying: ${migration.id}`);
|
||||
db.transaction(() => {
|
||||
migration.up(db);
|
||||
markApplied.run(migration.id);
|
||||
})();
|
||||
console.log(`[Migrations] Applied: ${migration.id}`);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user