Add persistence layer: TOTP auth, game history, restore

- db/ package: async SQLAlchemy engine + Player/Game/Guess models
- api/auth.py: passwordless TOTP login (pyotp), session token via socket auth
- api/history.py: record guesses/points, DB-backed standings, restore
  unfinished games on startup, host-only end_game
- api/__init__.py: auth-gated handlers, accounts map, rejoin via account
- frontend: Auth (QR + code) and History pages, resume/end-game in lobby/table
- docker-compose: real PostgreSQL service wired via DATABASE_URL
- tests_history.py for the persistence/auth layer; refresh CLAUDE.md

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tim
2026-06-23 23:09:50 +02:00
co-authored by Claude Opus 4.8
parent beaf142ee4
commit 30c32b7714
24 changed files with 1446 additions and 87 deletions
+42
View File
@@ -10,6 +10,7 @@ export interface PlayerInfo {
order: number;
name: string;
connected: boolean;
player_id?: number;
}
export interface MyPlayer {
@@ -51,3 +52,44 @@ export interface GameInfo {
}
export type Hand = Record<string, Card>;
// --- authentication (TOTP) ---
export interface Account {
player_id: number;
username: string;
}
export interface Registration {
username: string;
secret: string;
otpauth_uri: string;
}
// --- history ---
export interface HistoryGame {
gid: string;
created_at: string | null;
ended_at: string | null;
players: string[];
my_points: number;
}
export interface GameDetailRound {
series_number: number;
round_number: number;
player_id: number;
username: string | null;
guess: number;
points: number;
won: boolean;
}
export interface GameDetail {
gid: string;
created_at: string | null;
ended_at: string | null;
players: { player_id: number; username: string }[];
rounds: GameDetailRound[];
}