v0.11.0 harden persistence and prepare game replays
This commit is contained in:
@@ -24,17 +24,26 @@ type SessionRecord struct {
|
||||
|
||||
// GameRecord represents a row in the games table
|
||||
type GameRecord struct {
|
||||
GameID string `db:"game_id"`
|
||||
InitialFEN string `db:"initial_fen"`
|
||||
WhitePlayerID string `db:"white_player_id"`
|
||||
WhiteType int `db:"white_type"`
|
||||
WhiteLevel int `db:"white_level"`
|
||||
WhiteSearchTime int `db:"white_search_time"`
|
||||
BlackPlayerID string `db:"black_player_id"`
|
||||
BlackType int `db:"black_type"`
|
||||
BlackLevel int `db:"black_level"`
|
||||
BlackSearchTime int `db:"black_search_time"`
|
||||
StartTimeUTC time.Time `db:"start_time_utc"`
|
||||
GameID string `db:"game_id"`
|
||||
InitialFEN string `db:"initial_fen"`
|
||||
WhitePlayerID string `db:"white_player_id"`
|
||||
WhiteType int `db:"white_type"`
|
||||
WhiteLevel int `db:"white_level"`
|
||||
WhiteSearchTime int `db:"white_search_time"`
|
||||
WhiteClaimedBy string `db:"white_claimed_by"`
|
||||
BlackPlayerID string `db:"black_player_id"`
|
||||
BlackType int `db:"black_type"`
|
||||
BlackLevel int `db:"black_level"`
|
||||
BlackSearchTime int `db:"black_search_time"`
|
||||
BlackClaimedBy string `db:"black_claimed_by"`
|
||||
Result string `db:"result"`
|
||||
StartTimeUTC time.Time `db:"start_time_utc"`
|
||||
EndTimeUTC *time.Time `db:"end_time_utc"`
|
||||
}
|
||||
|
||||
type GameSummaryRecord struct {
|
||||
GameRecord
|
||||
MoveCount int `db:"move_count"`
|
||||
}
|
||||
|
||||
// MoveRecord represents a row in the moves table
|
||||
@@ -48,7 +57,19 @@ type MoveRecord struct {
|
||||
MoveTimeUTC time.Time `db:"move_time_utc"`
|
||||
}
|
||||
|
||||
// Schema defines the SQLite database structure
|
||||
// MovePersistence groups changes caused by one accepted move so the move,
|
||||
// first-move slot claim, and terminal result commit in one transaction.
|
||||
type MovePersistence struct {
|
||||
Move MoveRecord
|
||||
ClaimColor string
|
||||
ClaimedBy string
|
||||
Result string
|
||||
EndTimeUTC *time.Time
|
||||
}
|
||||
|
||||
// Schema defines tables only. Indexes are applied after legacy column
|
||||
// migrations so upgrading an older games table never references a missing
|
||||
// column.
|
||||
const Schema = `
|
||||
CREATE TABLE IF NOT EXISTS users (
|
||||
user_id TEXT PRIMARY KEY,
|
||||
@@ -61,12 +82,6 @@ CREATE TABLE IF NOT EXISTS users (
|
||||
last_login_at DATETIME
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_users_username ON users(username);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_email ON users(email);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_account_type ON users(account_type);
|
||||
CREATE INDEX IF NOT EXISTS idx_users_expires_at ON users(expires_at);
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_unique ON users(email) WHERE email IS NOT NULL AND email != '';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS sessions (
|
||||
session_id TEXT PRIMARY KEY,
|
||||
user_id TEXT NOT NULL UNIQUE,
|
||||
@@ -75,9 +90,6 @@ CREATE TABLE IF NOT EXISTS sessions (
|
||||
FOREIGN KEY (user_id) REFERENCES users(user_id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_user_id ON sessions(user_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS games (
|
||||
game_id TEXT PRIMARY KEY,
|
||||
initial_fen TEXT NOT NULL,
|
||||
@@ -89,7 +101,11 @@ CREATE TABLE IF NOT EXISTS games (
|
||||
black_type INTEGER NOT NULL,
|
||||
black_level INTEGER NOT NULL DEFAULT 0,
|
||||
black_search_time INTEGER NOT NULL DEFAULT 1000,
|
||||
start_time_utc DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP
|
||||
start_time_utc DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
result TEXT CHECK(result IS NULL OR result IN ('white_wins', 'black_wins', 'draw', 'stalemate')),
|
||||
end_time_utc DATETIME,
|
||||
white_claimed_by TEXT,
|
||||
black_claimed_by TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS moves (
|
||||
@@ -103,8 +119,20 @@ CREATE TABLE IF NOT EXISTS moves (
|
||||
FOREIGN KEY (game_id) REFERENCES games(game_id) ON DELETE CASCADE,
|
||||
UNIQUE(game_id, move_number)
|
||||
);
|
||||
`
|
||||
|
||||
CREATE INDEX IF NOT EXISTS idx_moves_game_id ON moves(game_id);
|
||||
const Indexes = `
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS idx_users_email_unique
|
||||
ON users(email) WHERE email IS NOT NULL AND email != '';
|
||||
CREATE INDEX IF NOT EXISTS idx_users_temp_created_at
|
||||
ON users(created_at) WHERE account_type = 'temp';
|
||||
CREATE INDEX IF NOT EXISTS idx_users_temp_expires_at
|
||||
ON users(expires_at) WHERE account_type = 'temp' AND expires_at IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_sessions_expires_at ON sessions(expires_at);
|
||||
CREATE INDEX IF NOT EXISTS idx_games_white_player ON games(white_player_id);
|
||||
CREATE INDEX IF NOT EXISTS idx_games_black_player ON games(black_player_id);
|
||||
`
|
||||
CREATE INDEX IF NOT EXISTS idx_games_white_claimed ON games(white_claimed_by)
|
||||
WHERE white_claimed_by IS NOT NULL;
|
||||
CREATE INDEX IF NOT EXISTS idx_games_black_claimed ON games(black_claimed_by)
|
||||
WHERE black_claimed_by IS NOT NULL;
|
||||
`
|
||||
|
||||
Reference in New Issue
Block a user