feat: record bot games results

This commit is contained in:
Henri Bourcereau 2026-06-24 21:47:57 +02:00
parent 592dfe52af
commit 9c13630c7d
9 changed files with 103 additions and 19 deletions

View file

@ -321,6 +321,34 @@ pub async fn insert_participant(
Ok(())
}
/// Records a completed bot game for a logged-in user in a single transaction.
pub async fn insert_bot_game(
pool: &Pool,
user_id: i64,
result: &str,
outcome: &str,
) -> Result<(), DbError> {
let mut client = pool.get().await?;
let tx = client.transaction().await?;
let now = now_unix();
let row = tx
.query_one(
"INSERT INTO game_records (game_id, room_code, started_at, ended_at, result) \
VALUES ('trictrac', 'bot', $1, $1, $2) RETURNING id",
&[&now, &result],
)
.await?;
let record_id: i64 = row.get(0);
tx.execute(
"INSERT INTO game_participants (game_record_id, user_id, player_id, outcome) \
VALUES ($1, $2, 0, $3)",
&[&record_id, &user_id, &outcome],
)
.await?;
tx.commit().await?;
Ok(())
}
/// Returns win/loss/draw counts for a user. All values are 0 when the user has no games.
pub async fn get_user_stats(pool: &Pool, user_id: i64) -> Result<UserStats, DbError> {
let client = pool.get().await?;

View file

@ -13,6 +13,7 @@
//! GET /users/:username/games?page=0&per_page=20
//! GET /games/:id
//! POST /games/result
//! POST /games/bot-result
use axum::{
Json, Router,
@ -52,6 +53,7 @@ pub fn router() -> Router<Arc<AppState>> {
.route("/users/{username}", get(user_profile))
.route("/users/{username}/games", get(user_games))
.route("/games/result", post(game_result))
.route("/games/bot-result", post(bot_game_result))
.route("/games/{id}", get(game_detail))
.route("/pages/{slug}", get(get_page))
}
@ -548,6 +550,26 @@ async fn game_result(
Ok(Json(GameResultResponse { game_record_id }))
}
// ── Bot game result ───────────────────────────────────────────────────────────
#[derive(Deserialize)]
struct BotGameResultBody {
result: String,
outcome: String,
}
/// Called by the WASM client when a logged-in user finishes a bot game.
async fn bot_game_result(
auth_session: AuthSession<AuthBackend>,
State(state): State<Arc<AppState>>,
Json(body): Json<BotGameResultBody>,
) -> Result<impl IntoResponse, AppError> {
let user = auth_session.user.ok_or(AppError::Unauthorized)?;
db::insert_bot_game(&state.db, user.id, &body.result, &body.outcome).await?;
tracing::info!(user_id = user.id, outcome = body.outcome, "Bot game recorded");
Ok(StatusCode::OK)
}
// ── Static content pages ──────────────────────────────────────────────────────
#[derive(Deserialize)]