|
2 | 2 |
|
3 | 3 | import uuid |
4 | 4 | from pathlib import Path |
5 | | -from typing import Any |
6 | 5 |
|
7 | 6 | from fastapi import FastAPI, Request, Response |
8 | 7 | from fastapi.responses import HTMLResponse |
9 | 8 | from fastapi.staticfiles import StaticFiles |
10 | 9 | from fastapi.templating import Jinja2Templates |
| 10 | +from starlette.middleware.sessions import SessionMiddleware |
11 | 11 |
|
12 | | -from app.game_service import GameSession, get_session |
| 12 | +from app.game_service import get_session |
13 | 13 | from app.models import GameState |
14 | 14 |
|
15 | 15 | BASE_DIR = Path(__file__).resolve().parent |
16 | 16 |
|
17 | 17 | app = FastAPI(title="Soc Ops - Social Bingo") |
| 18 | +app.add_middleware(SessionMiddleware, secret_key="soc-ops-secret-key") |
18 | 19 | app.mount("/static", StaticFiles(directory=BASE_DIR / "static"), name="static") |
19 | 20 |
|
20 | 21 | templates = Jinja2Templates(directory=BASE_DIR / "templates") |
21 | 22 |
|
22 | | -SESSION_COOKIE = "soc_ops_session" |
23 | 23 |
|
24 | | - |
25 | | -def _session_from_request( |
26 | | - request: Request, |
27 | | -) -> tuple[GameSession, Response]: |
28 | | - """Get or create session, returning the session and a Response for cookies.""" |
29 | | - response = Response() |
30 | | - session_id = request.cookies.get(SESSION_COOKIE) |
31 | | - if not session_id: |
32 | | - session_id = uuid.uuid4().hex |
33 | | - response.set_cookie(SESSION_COOKIE, session_id, httponly=True, samesite="lax") |
34 | | - return get_session(session_id), response |
35 | | - |
36 | | - |
37 | | -def _render( |
38 | | - request: Request, |
39 | | - response: Response, |
40 | | - template: str, |
41 | | - context: dict[str, Any], |
42 | | -) -> Response: |
43 | | - """Render a template, copying any set-cookie headers from response.""" |
44 | | - content = templates.TemplateResponse(request, template, context) |
45 | | - for key, value in response.headers.items(): |
46 | | - if key.lower() == "set-cookie": |
47 | | - content.headers.append(key, value) |
48 | | - return content |
| 24 | +def _get_game_session(request: Request): |
| 25 | + """Get or create a game session using Starlette SessionMiddleware.""" |
| 26 | + if "session_id" not in request.session: |
| 27 | + request.session["session_id"] = uuid.uuid4().hex |
| 28 | + return get_session(request.session["session_id"]) |
49 | 29 |
|
50 | 30 |
|
51 | 31 | @app.get("/", response_class=HTMLResponse) |
52 | 32 | async def home(request: Request) -> Response: |
53 | | - session, response = _session_from_request(request) |
54 | | - return _render( |
| 33 | + session = _get_game_session(request) |
| 34 | + return templates.TemplateResponse( |
55 | 35 | request, |
56 | | - response, |
57 | 36 | "home.html", |
58 | 37 | {"session": session, "GameState": GameState}, |
59 | 38 | ) |
60 | 39 |
|
61 | 40 |
|
62 | 41 | @app.post("/start", response_class=HTMLResponse) |
63 | 42 | async def start_game(request: Request) -> Response: |
64 | | - session, response = _session_from_request(request) |
| 43 | + session = _get_game_session(request) |
65 | 44 | session.start_game() |
66 | | - return _render( |
67 | | - request, response, "components/game_screen.html", {"session": session} |
| 45 | + return templates.TemplateResponse( |
| 46 | + request, "components/game_screen.html", {"session": session} |
68 | 47 | ) |
69 | 48 |
|
70 | 49 |
|
71 | 50 | @app.post("/toggle/{square_id}", response_class=HTMLResponse) |
72 | 51 | async def toggle_square(request: Request, square_id: int) -> Response: |
73 | | - session, response = _session_from_request(request) |
| 52 | + session = _get_game_session(request) |
74 | 53 | session.handle_square_click(square_id) |
75 | | - return _render( |
76 | | - request, response, "components/game_screen.html", {"session": session} |
| 54 | + return templates.TemplateResponse( |
| 55 | + request, "components/game_screen.html", {"session": session} |
77 | 56 | ) |
78 | 57 |
|
79 | 58 |
|
80 | 59 | @app.post("/reset", response_class=HTMLResponse) |
81 | 60 | async def reset_game(request: Request) -> Response: |
82 | | - session, response = _session_from_request(request) |
| 61 | + session = _get_game_session(request) |
83 | 62 | session.reset_game() |
84 | | - return _render( |
| 63 | + return templates.TemplateResponse( |
85 | 64 | request, |
86 | | - response, |
87 | 65 | "components/start_screen.html", |
88 | 66 | {"session": session, "GameState": GameState}, |
89 | 67 | ) |
90 | 68 |
|
91 | 69 |
|
92 | 70 | @app.post("/dismiss-modal", response_class=HTMLResponse) |
93 | 71 | async def dismiss_modal(request: Request) -> Response: |
94 | | - session, response = _session_from_request(request) |
| 72 | + session = _get_game_session(request) |
95 | 73 | session.dismiss_modal() |
96 | | - return _render( |
97 | | - request, response, "components/game_screen.html", {"session": session} |
| 74 | + return templates.TemplateResponse( |
| 75 | + request, "components/game_screen.html", {"session": session} |
98 | 76 | ) |
99 | 77 |
|
100 | 78 |
|
|
0 commit comments