import Database, { Database as DatabaseType } from 'better-sqlite3'; import { readFileSync, mkdirSync } from 'fs'; import { dirname, join } from 'path'; import { fileURLToPath } from 'url'; const __dirname = dirname(fileURLToPath(import.meta.url)); const DB_PATH = process.env.DATABASE_PATH || join(__dirname, '../../../data/kaboot.db'); mkdirSync(dirname(DB_PATH), { recursive: true }); export const db: DatabaseType = new Database(DB_PATH); db.pragma('journal_mode = WAL'); db.pragma('foreign_keys = ON'); const schema = readFileSync(join(__dirname, 'schema.sql'), 'utf-8'); db.exec(schema); const runMigrations = () => { const tableInfo = db.prepare("PRAGMA table_info(quizzes)").all() as { name: string }[]; const hasGameConfig = tableInfo.some(col => col.name === 'game_config'); if (!hasGameConfig) { db.exec("ALTER TABLE quizzes ADD COLUMN game_config TEXT"); console.log("Migration: Added game_config to quizzes"); } const userTableInfo = db.prepare("PRAGMA table_info(users)").all() as { name: string }[]; const hasDefaultConfig = userTableInfo.some(col => col.name === 'default_game_config'); if (!hasDefaultConfig) { db.exec("ALTER TABLE users ADD COLUMN default_game_config TEXT"); console.log("Migration: Added default_game_config to users"); } const tables = db.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='game_sessions'").get(); if (!tables) { db.exec(` CREATE TABLE game_sessions ( pin TEXT PRIMARY KEY, host_peer_id TEXT NOT NULL, host_secret TEXT NOT NULL, quiz_data TEXT NOT NULL, game_config TEXT NOT NULL, game_state TEXT NOT NULL DEFAULT 'LOBBY', current_question_index INTEGER NOT NULL DEFAULT 0, players_data TEXT NOT NULL DEFAULT '[]', first_correct_player_id TEXT, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX idx_game_sessions_updated ON game_sessions(updated_at); `); console.log("Migration: Created game_sessions table"); } const sessionTableInfo = db.prepare("PRAGMA table_info(game_sessions)").all() as { name: string }[]; const hasFirstCorrect = sessionTableInfo.some(col => col.name === "first_correct_player_id"); if (!hasFirstCorrect) { db.exec("ALTER TABLE game_sessions ADD COLUMN first_correct_player_id TEXT"); console.log("Migration: Added first_correct_player_id to game_sessions"); } const userTableInfo2 = db.prepare("PRAGMA table_info(users)").all() as { name: string }[]; const hasColorScheme = userTableInfo2.some(col => col.name === "color_scheme"); if (!hasColorScheme) { db.exec("ALTER TABLE users ADD COLUMN color_scheme TEXT DEFAULT 'blue'"); console.log("Migration: Added color_scheme to users"); } const hasGeminiKey = userTableInfo2.some(col => col.name === "gemini_api_key"); if (!hasGeminiKey) { db.exec("ALTER TABLE users ADD COLUMN gemini_api_key TEXT"); console.log("Migration: Added gemini_api_key to users"); } const hasAiProvider = userTableInfo2.some(col => col.name === "ai_provider"); if (!hasAiProvider) { db.exec("ALTER TABLE users ADD COLUMN ai_provider TEXT DEFAULT 'gemini'"); console.log("Migration: Added ai_provider to users"); } const hasOpenRouterKey = userTableInfo2.some(col => col.name === "openrouter_api_key"); if (!hasOpenRouterKey) { db.exec("ALTER TABLE users ADD COLUMN openrouter_api_key TEXT"); console.log("Migration: Added openrouter_api_key to users"); } const hasOpenRouterModel = userTableInfo2.some(col => col.name === "openrouter_model"); if (!hasOpenRouterModel) { db.exec("ALTER TABLE users ADD COLUMN openrouter_model TEXT"); console.log("Migration: Added openrouter_model to users"); } const hasOpenAIKey = userTableInfo2.some(col => col.name === "openai_api_key"); if (!hasOpenAIKey) { db.exec("ALTER TABLE users ADD COLUMN openai_api_key TEXT"); console.log("Migration: Added openai_api_key to users"); } const hasOpenAIModel = userTableInfo2.some(col => col.name === "openai_model"); if (!hasOpenAIModel) { db.exec("ALTER TABLE users ADD COLUMN openai_model TEXT"); console.log("Migration: Added openai_model to users"); } const hasGeminiModel = userTableInfo2.some(col => col.name === "gemini_model"); if (!hasGeminiModel) { db.exec("ALTER TABLE users ADD COLUMN gemini_model TEXT"); console.log("Migration: Added gemini_model to users"); } const quizTableInfo = db.prepare("PRAGMA table_info(quizzes)").all() as { name: string }[]; const hasShareToken = quizTableInfo.some(col => col.name === "share_token"); if (!hasShareToken) { db.exec("ALTER TABLE quizzes ADD COLUMN share_token TEXT"); console.log("Migration: Added share_token to quizzes"); } const hasIsShared = quizTableInfo.some(col => col.name === "is_shared"); if (!hasIsShared) { db.exec("ALTER TABLE quizzes ADD COLUMN is_shared INTEGER DEFAULT 0"); console.log("Migration: Added is_shared to quizzes"); } const shareTokenIndex = db.prepare("SELECT name FROM sqlite_master WHERE type='index' AND name='idx_quizzes_share_token'").get(); if (!shareTokenIndex) { db.exec("CREATE UNIQUE INDEX idx_quizzes_share_token ON quizzes(share_token)"); console.log("Migration: Created unique index on quizzes.share_token"); } }; runMigrations(); console.log(`Database initialized at ${DB_PATH}`);