chess-pressure/src/chess_pressure/app.py
2026-04-06 15:54:20 -06:00

124 lines
2.6 KiB
Python

"""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.responses import FileResponse
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 ---
app.mount("/static", StaticFiles(directory=str(STATIC)), name="static")
@app.get("/")
def index():
return FileResponse(
str(STATIC / "index.html"),
headers={"Cache-Control": "no-cache, must-revalidate"},
)
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()