diff --git a/src/lib/server/db/index.ts b/src/lib/server/db/index.ts index e3e63c7..ff8fc5d 100644 --- a/src/lib/server/db/index.ts +++ b/src/lib/server/db/index.ts @@ -6,24 +6,33 @@ import schemaSql from './schema.sql?raw'; let _db: Database.Database | null = null; -// Columns that were added to `servers` after v0.1 shipped. Applied on every -// boot; better-sqlite3 has no `ADD COLUMN IF NOT EXISTS`, so we run each and -// swallow the "duplicate column" error. -const SERVERS_MIGRATIONS: Array<{ column: string; ddl: string }> = [ - { column: 'last_map', ddl: 'ALTER TABLE servers ADD COLUMN last_map TEXT' }, - { column: 'last_gametype', ddl: 'ALTER TABLE servers ADD COLUMN last_gametype TEXT' }, - { column: 'last_roster', ddl: 'ALTER TABLE servers ADD COLUMN last_roster TEXT' }, - { column: 'last_seen_at', ddl: 'ALTER TABLE servers ADD COLUMN last_seen_at TEXT' }, - { column: 'last_error', ddl: 'ALTER TABLE servers ADD COLUMN last_error TEXT' } +// Desired shape of the `servers` table. Every column here is added to the +// table on boot if missing — introspection via PRAGMA table_info handles +// both fresh DBs and legacy DBs that predate any given column. CREATE TABLE +// IF NOT EXISTS in schema.sql is a no-op on existing tables, so it can't +// backfill columns; this can. +const SERVERS_COLUMNS: Array<{ name: string; ddl: string }> = [ + // Base columns that shipped in the very first schema stay here for parity + // with fresh installs — no ALTER is issued if they're already present. + { name: 'komodo_server', ddl: 'ALTER TABLE servers ADD COLUMN komodo_server TEXT' }, + { name: 'game_port', ddl: 'ALTER TABLE servers ADD COLUMN game_port INTEGER' }, + { name: 'rcon_host', ddl: 'ALTER TABLE servers ADD COLUMN rcon_host TEXT' }, + { name: 'last_players', ddl: 'ALTER TABLE servers ADD COLUMN last_players INTEGER' }, + { name: 'last_checked_at', ddl: 'ALTER TABLE servers ADD COLUMN last_checked_at TEXT' }, + // Post-v0.1 additions from the perk-a-cola/diagnostics work + { name: 'last_map', ddl: 'ALTER TABLE servers ADD COLUMN last_map TEXT' }, + { name: 'last_gametype', ddl: 'ALTER TABLE servers ADD COLUMN last_gametype TEXT' }, + { name: 'last_roster', ddl: 'ALTER TABLE servers ADD COLUMN last_roster TEXT' }, + { name: 'last_seen_at', ddl: 'ALTER TABLE servers ADD COLUMN last_seen_at TEXT' }, + { name: 'last_error', ddl: 'ALTER TABLE servers ADD COLUMN last_error TEXT' } ]; function migrateServers(handle: Database.Database): void { - for (const m of SERVERS_MIGRATIONS) { - try { - handle.exec(m.ddl); - } catch (err) { - const msg = (err as Error).message ?? ''; - if (!/duplicate column name/i.test(msg)) throw err; + const rows = handle.prepare('PRAGMA table_info(servers)').all() as Array<{ name: string }>; + const existing = new Set(rows.map((r) => r.name)); + for (const col of SERVERS_COLUMNS) { + if (!existing.has(col.name)) { + handle.exec(col.ddl); } } }