diff --git a/lib/db.ts b/lib/db.ts index 80ce274..01bdb67 100644 --- a/lib/db.ts +++ b/lib/db.ts @@ -5,9 +5,15 @@ import { randomUUID } from "node:crypto"; const dir = process.env.DATA_DIR ?? "data"; mkdirSync(dir, { recursive: true }); -const g = globalThis as { __qaiDb?: DatabaseSync }; -export const db = (g.__qaiDb ??= (() => { +// Open on first query, not at import: `next build` imports every page through lib/auth -> lib/db in ~6 parallel +// workers, and six processes running DDL against one file at once fails with "database is locked". +let _db: DatabaseSync | null = null; +function openDb(): DatabaseSync { + if (_db) return _db; const d = new DatabaseSync(`${dir}/qai.db`); + d.exec(` + PRAGMA journal_mode = WAL; + PRAGMA busy_timeout = 5000;`); d.exec(` PRAGMA journal_mode = WAL; CREATE TABLE IF NOT EXISTS users ( @@ -25,8 +31,17 @@ export const db = (g.__qaiDb ??= (() => { status TEXT NOT NULL DEFAULT 'running', error TEXT, created_at INTEGER NOT NULL, updated_at INTEGER NOT NULL); CREATE INDEX IF NOT EXISTS ix_analyses_user ON analyses(user_id, created_at DESC);`); + _db = d; return d; -})()); +} +// Proxy keeps the terse call sites (db.prepare) while deferring the open; methods must be bound to the real +// instance or node:sqlite rejects the receiver ("Illegal invocation"). +export const db: DatabaseSync = new Proxy({} as DatabaseSync, { + get: (_t, k) => { + const v = (openDb() as any)[k]; + return typeof v === "function" ? v.bind(openDb()) : v; + }, +}); const now = () => Date.now(); type Row = string | number | null;