113 lines
3.8 KiB
Python
113 lines
3.8 KiB
Python
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()
|