|
2 | 2 |
|
3 | 3 | import uuid |
4 | 4 | from pathlib import Path |
| 5 | +from typing import Any |
5 | 6 |
|
6 | 7 | from fastapi import FastAPI, Request, Response |
7 | 8 | from fastapi.responses import HTMLResponse |
8 | 9 | from fastapi.staticfiles import StaticFiles |
9 | 10 | from fastapi.templating import Jinja2Templates |
10 | 11 |
|
11 | | -from app.game_service import get_session |
| 12 | +from app.game_service import GameSession, get_session |
12 | 13 | from app.models import GameState |
13 | 14 |
|
14 | 15 | BASE_DIR = Path(__file__).resolve().parent |
|
21 | 22 | SESSION_COOKIE = "soc_ops_session" |
22 | 23 |
|
23 | 24 |
|
24 | | -def _get_session_id(request: Request, response: Response) -> str: |
25 | | - """Get or create a session ID from cookies.""" |
| 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() |
26 | 30 | session_id = request.cookies.get(SESSION_COOKIE) |
27 | 31 | if not session_id: |
28 | 32 | session_id = uuid.uuid4().hex |
29 | 33 | response.set_cookie(SESSION_COOKIE, session_id, httponly=True, samesite="lax") |
30 | | - return session_id |
| 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 |
31 | 49 |
|
32 | 50 |
|
33 | 51 | @app.get("/", response_class=HTMLResponse) |
34 | 52 | async def home(request: Request) -> Response: |
35 | | - response = Response() |
36 | | - session_id = _get_session_id(request, response) |
37 | | - session = get_session(session_id) |
38 | | - |
39 | | - content = templates.TemplateResponse( |
| 53 | + session, response = _session_from_request(request) |
| 54 | + return _render( |
40 | 55 | request, |
| 56 | + response, |
41 | 57 | "home.html", |
42 | 58 | {"session": session, "GameState": GameState}, |
43 | 59 | ) |
44 | | - # Copy cookies from our response to the template response |
45 | | - for key, morsel in response.headers.items(): |
46 | | - if key.lower() == "set-cookie": |
47 | | - content.headers.append(key, morsel) |
48 | | - return content |
49 | 60 |
|
50 | 61 |
|
51 | 62 | @app.post("/start", response_class=HTMLResponse) |
52 | 63 | async def start_game(request: Request) -> Response: |
53 | | - response = Response() |
54 | | - session_id = _get_session_id(request, response) |
55 | | - session = get_session(session_id) |
| 64 | + session, response = _session_from_request(request) |
56 | 65 | session.start_game() |
57 | | - |
58 | | - content = templates.TemplateResponse( |
59 | | - request, |
60 | | - "components/game_screen.html", |
61 | | - {"session": session}, |
| 66 | + return _render( |
| 67 | + request, response, "components/game_screen.html", {"session": session} |
62 | 68 | ) |
63 | | - for key, morsel in response.headers.items(): |
64 | | - if key.lower() == "set-cookie": |
65 | | - content.headers.append(key, morsel) |
66 | | - return content |
67 | 69 |
|
68 | 70 |
|
69 | 71 | @app.post("/toggle/{square_id}", response_class=HTMLResponse) |
70 | 72 | async def toggle_square(request: Request, square_id: int) -> Response: |
71 | | - response = Response() |
72 | | - session_id = _get_session_id(request, response) |
73 | | - session = get_session(session_id) |
| 73 | + session, response = _session_from_request(request) |
74 | 74 | session.handle_square_click(square_id) |
75 | | - |
76 | | - content = templates.TemplateResponse( |
77 | | - request, |
78 | | - "components/game_screen.html", |
79 | | - {"session": session}, |
| 75 | + return _render( |
| 76 | + request, response, "components/game_screen.html", {"session": session} |
80 | 77 | ) |
81 | | - for key, morsel in response.headers.items(): |
82 | | - if key.lower() == "set-cookie": |
83 | | - content.headers.append(key, morsel) |
84 | | - return content |
85 | 78 |
|
86 | 79 |
|
87 | 80 | @app.post("/reset", response_class=HTMLResponse) |
88 | 81 | async def reset_game(request: Request) -> Response: |
89 | | - response = Response() |
90 | | - session_id = _get_session_id(request, response) |
91 | | - session = get_session(session_id) |
| 82 | + session, response = _session_from_request(request) |
92 | 83 | session.reset_game() |
93 | | - |
94 | | - content = templates.TemplateResponse( |
| 84 | + return _render( |
95 | 85 | request, |
| 86 | + response, |
96 | 87 | "components/start_screen.html", |
97 | 88 | {"session": session, "GameState": GameState}, |
98 | 89 | ) |
99 | | - for key, morsel in response.headers.items(): |
100 | | - if key.lower() == "set-cookie": |
101 | | - content.headers.append(key, morsel) |
102 | | - return content |
103 | 90 |
|
104 | 91 |
|
105 | 92 | @app.post("/dismiss-modal", response_class=HTMLResponse) |
106 | 93 | async def dismiss_modal(request: Request) -> Response: |
107 | | - response = Response() |
108 | | - session_id = _get_session_id(request, response) |
109 | | - session = get_session(session_id) |
| 94 | + session, response = _session_from_request(request) |
110 | 95 | session.dismiss_modal() |
111 | | - |
112 | | - content = templates.TemplateResponse( |
113 | | - request, |
114 | | - "components/game_screen.html", |
115 | | - {"session": session}, |
| 96 | + return _render( |
| 97 | + request, response, "components/game_screen.html", {"session": session} |
116 | 98 | ) |
117 | | - for key, morsel in response.headers.items(): |
118 | | - if key.lower() == "set-cookie": |
119 | | - content.headers.append(key, morsel) |
120 | | - return content |
121 | 99 |
|
122 | 100 |
|
123 | 101 | def run() -> None: |
|
0 commit comments