import sqlite3 from flask import g DATABASE = 'sqlite.db' def connect_db(): db = sqlite3.connect(DATABASE) db.row_factory = sqlite3.Row db.execute("PRAGMA foreign_keys=ON;") return db def get_db(): if 'db' not in g: g.db = connect_db() return g.db def close_db(e=None): db = g.pop('db', None) if db is not None: db.close() def init_db(app): with app.app_context(): db = get_db() db.execute("PRAGMA journal_mode=WAL;") db.execute("PRAGMA foreign_keys=ON;") # create inital tables db.executescript(""" CREATE TABLE IF NOT EXISTS users ( id INTEGER PRIMARY KEY AUTOINCREMENT,username TEXT UNIQUE NOT NULL, password_hash TEXT NOT NULL, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS games ( id INTEGER PRIMARY KEY AUTOINCREMENT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, final_fen TEXT, termination TEXT CHECK(termination IN ('checkmate', 'resignation', 'timeout', 'draw', 'other')), termination_detail TEXT, winner_color TEXT CHECK(winner_color IN ('white', 'black', 'draw')), move_history TEXT NOT NULL DEFAULT '[]', time_mode TEXT, started_at TIMESTAMP, ended_at TIMESTAMP ); CREATE TABLE IF NOT EXISTS game_players ( game_id INTEGER NOT NULL, player_id INTEGER NOT NULL, color TEXT NOT NULL CHECK(color IN ('white', 'black')), PRIMARY KEY (game_id, player_id), UNIQUE (game_id, color), FOREIGN KEY (game_id) REFERENCES games(id) ON DELETE CASCADE, FOREIGN KEY (player_id) REFERENCES users(id) ON DELETE CASCADE ); CREATE INDEX IF NOT EXISTS idx_game_players_game_id ON game_players(game_id); CREATE INDEX IF NOT EXISTS idx_game_players_player_id ON game_players(player_id); CREATE TABLE IF NOT EXISTS friendships ( requester_id INTEGER NOT NULL, addressee_id INTEGER NOT NULL, status TEXT NOT NULL CHECK(status IN ('pending','accepted','blocked')), PRIMARY KEY (requester_id, addressee_id), FOREIGN KEY (requester_id) REFERENCES users(id) ON DELETE CASCADE, FOREIGN KEY (addressee_id) REFERENCES users(id) ON DELETE CASCADE ); """ ) # Lightweight migration support for existing databases. user_columns = { row["name"] for row in db.execute("PRAGMA table_info(users)").fetchall() } if "last_seen_at" not in user_columns: db.execute("ALTER TABLE users ADD COLUMN last_seen_at TIMESTAMP") game_columns = { row["name"] for row in db.execute("PRAGMA table_info(games)").fetchall() } if "termination_detail" not in game_columns: db.execute("ALTER TABLE games ADD COLUMN termination_detail TEXT") if "winner_color" not in game_columns: db.execute( "ALTER TABLE games ADD COLUMN winner_color TEXT " "CHECK(winner_color IN ('white', 'black', 'draw'))" ) if "move_history" not in game_columns: db.execute( "ALTER TABLE games ADD COLUMN move_history TEXT NOT NULL DEFAULT '[]'" ) if "time_mode" not in game_columns: db.execute("ALTER TABLE games ADD COLUMN time_mode TEXT") if "started_at" not in game_columns: db.execute("ALTER TABLE games ADD COLUMN started_at TIMESTAMP") if "ended_at" not in game_columns: db.execute("ALTER TABLE games ADD COLUMN ended_at TIMESTAMP") db.commit()