"""FastAPI application for chess pressure visualization.""" from __future__ import annotations from functools import lru_cache from pathlib import Path import uvicorn from fastapi import FastAPI, HTTPException from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from .engine import make_move, parse_pgn from .games import get_game_list, get_game_pgn STATIC = Path(__file__).resolve().parent / "static" app = FastAPI(title="Chess Pressure", docs_url=None, redoc_url=None) # --- Models --- class PGNUpload(BaseModel): pgn: str class MoveRequest(BaseModel): fen: str uci: str # --- Health --- @app.get("/health", include_in_schema=False) @app.get("/up", include_in_schema=False) @app.get("/status", include_in_schema=False) @app.get("/health-check", include_in_schema=False) def health(): return {"status": "ok"} # --- API --- @app.get("/api/games") def list_games(): return get_game_list() @app.get("/api/games/{game_id}") @lru_cache(maxsize=16) def load_game(game_id: str): pgn = get_game_pgn(game_id) if pgn is None: raise HTTPException(404, "Game not found") return parse_pgn(pgn) @app.post("/api/parse") def parse_uploaded_pgn(body: PGNUpload): try: return parse_pgn(body.pgn) except ValueError as e: raise HTTPException(400, str(e)) @app.get("/api/legal") def legal_moves(fen: str): import chess board = chess.Board(fen) return [m.uci() for m in board.legal_moves] @app.post("/api/move") def do_move(body: MoveRequest): try: return make_move(body.fen, body.uci) except ValueError as e: raise HTTPException(400, str(e)) # --- Static files --- # The app is fully client-side; production is served statically (see Dockerfile). # This Python server is kept as an optional fallback / parity reference and the # /api routes above remain available, but the UI no longer depends on them. # Mounted last so /api and /health take precedence. app.mount("/", StaticFiles(directory=str(STATIC), html=True), name="static") def main(): import argparse parser = argparse.ArgumentParser(description="Chess Pressure server") parser.add_argument( "--host", default="0.0.0.0", help="Bind address (default: 0.0.0.0)" ) parser.add_argument( "--port", "-p", type=int, default=8888, help="Port (default: 8888)" ) parser.add_argument( "--workers", "-w", type=int, default=2, help="Worker processes (default: 2)" ) args = parser.parse_args() uvicorn.run( "chess_pressure.app:app", host=args.host, port=args.port, workers=args.workers, ) if __name__ == "__main__": main()