Files
snake-python/server/blueprints/dashboard.py
T
2026-04-06 19:35:53 +02:00

118 lines
3.7 KiB
Python

from typing import TYPE_CHECKING
import asyncio, json, os
from quart import (
Blueprint,
render_template,
send_from_directory,
request,
websocket,
)
if TYPE_CHECKING:
from server.Server import Server
def create_dashboard_blueprint(server:'Server') -> Blueprint:
blueprint = Blueprint('dashboard', __name__)
@blueprint.get('/dashboard')
async def dashboard_view():
initial_game_id = request.args.get('game_id', '')
initial_summary = await server.dashboard_query.get_dashboard_summary()
initial_games = await server.dashboard_query.get_dashboard_games(limit=100)
return await render_template(
'dashboard.htm',
initial_game_id=initial_game_id,
initial_summary=initial_summary,
initial_games=initial_games,
)
@blueprint.get('/dashboard/customizations/<path:asset_path>')
async def dashboard_customizations_asset(asset_path:str):
customization_root = os.path.join(
server.app.static_folder,
'customizations',
)
return await send_from_directory(customization_root, asset_path)
@blueprint.websocket('/dashboard/ws/games')
async def dashboard_games_ws():
ws_hub = server.dashboard_ws_hub
websocket_task = asyncio.current_task()
if websocket_task is not None:
await ws_hub.register_task(websocket_task)
subscriber_queue:asyncio.Queue[str] = asyncio.Queue(maxsize=20)
await ws_hub.register_subscriber(subscriber_queue)
try:
initial_payload = await server.dashboard_query.build_dashboard_games_event()
await asyncio.wait_for(
websocket.send(json.dumps(initial_payload)), timeout=1.5
)
while True:
queue_task = asyncio.create_task(subscriber_queue.get())
receive_task = asyncio.create_task(websocket.receive())
try:
done, _ = await asyncio.wait(
{queue_task, receive_task},
timeout=1.0,
return_when=asyncio.FIRST_COMPLETED,
)
if len(done) == 0:
if ws_hub.shutdown_event.is_set():
await asyncio.wait_for(
websocket.send(ws_hub.shutdown_message),
timeout=1.5,
)
break
continue
if receive_task in done:
try:
request_payload_raw = receive_task.result()
except Exception:
break
response_event = await server.dashboard_query.handle_dashboard_ws_request(request_payload_raw)
if response_event is not None:
await asyncio.wait_for(
websocket.send(json.dumps(response_event)),
timeout=1.5,
)
if queue_task in done:
event_payload = queue_task.result()
if event_payload == ws_hub.shutdown_message:
await asyncio.wait_for(
websocket.send(event_payload), timeout=1.5
)
break
await asyncio.wait_for(
websocket.send(event_payload), timeout=1.5
)
except asyncio.TimeoutError:
if ws_hub.shutdown_event.is_set():
await asyncio.wait_for(
websocket.send(ws_hub.shutdown_message),
timeout=1.5,
)
break
finally:
for pending_task in (queue_task, receive_task):
if not pending_task.done():
pending_task.cancel()
await asyncio.gather(
queue_task, receive_task, return_exceptions=True
)
except asyncio.CancelledError:
pass
except Exception:
pass
finally:
await ws_hub.unregister_subscriber(subscriber_queue)
if websocket_task is not None:
await ws_hub.unregister_task(websocket_task)
return blueprint