import Database from 'better-sqlite3'; import fs from 'node:fs'; import path from 'node:path'; import { env } from '$env/dynamic/private'; let _db: Database.Database | null = null; export function getDb(): Database.Database { if (_db) return _db; const DB_PATH = env.DB_PATH ?? path.join(process.cwd(), 'data', 'game-grid.db'); const dir = path.dirname(DB_PATH); if (!fs.existsSync(dir)) { fs.mkdirSync(dir, { recursive: true }); } _db = new Database(DB_PATH); _db.pragma('journal_mode = WAL'); _db.pragma('foreign_keys = ON'); initSchema(_db); return _db; } function initSchema(db: Database.Database): void { db.exec(` CREATE TABLE IF NOT EXISTS series ( id INTEGER PRIMARY KEY AUTOINCREMENT, slug TEXT NOT NULL UNIQUE, title TEXT NOT NULL, library TEXT NOT NULL CHECK(library IN ('public','private')), folder_path TEXT NOT NULL, has_cover INTEGER NOT NULL DEFAULT 0, last_scanned_at TEXT NOT NULL DEFAULT (datetime('now')), created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS games ( id INTEGER PRIMARY KEY AUTOINCREMENT, slug TEXT NOT NULL UNIQUE, title TEXT NOT NULL, library TEXT NOT NULL CHECK(library IN ('public','private')), folder_path TEXT NOT NULL, description TEXT NOT NULL DEFAULT '', genre TEXT NOT NULL DEFAULT '', has_cover INTEGER NOT NULL DEFAULT 0, has_wide INTEGER NOT NULL DEFAULT 0, last_scanned_at TEXT NOT NULL DEFAULT (datetime('now')), created_at TEXT NOT NULL DEFAULT (datetime('now')) ); CREATE TABLE IF NOT EXISTS game_files ( id INTEGER PRIMARY KEY AUTOINCREMENT, game_id INTEGER NOT NULL REFERENCES games(id) ON DELETE CASCADE, filename TEXT NOT NULL, rel_path TEXT NOT NULL, platform TEXT NOT NULL, is_dir INTEGER NOT NULL DEFAULT 0, file_size INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS screenshots ( id INTEGER PRIMARY KEY AUTOINCREMENT, game_id INTEGER NOT NULL REFERENCES games(id) ON DELETE CASCADE, filename TEXT NOT NULL, rel_path TEXT NOT NULL, sort_order INTEGER NOT NULL DEFAULT 0 ); CREATE TABLE IF NOT EXISTS tags ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE COLLATE NOCASE ); CREATE TABLE IF NOT EXISTS game_tags ( game_id INTEGER NOT NULL REFERENCES games(id) ON DELETE CASCADE, tag_id INTEGER NOT NULL REFERENCES tags(id) ON DELETE CASCADE, PRIMARY KEY (game_id, tag_id) ); `); const cols = db.prepare('PRAGMA table_info(games)').all() as Array<{ name: string }>; if (!cols.some((c) => c.name === 'series_id')) { db.exec( 'ALTER TABLE games ADD COLUMN series_id INTEGER REFERENCES series(id) ON DELETE SET NULL' ); } }