diff --git a/CHANGELOG.md b/CHANGELOG.md index 2d49b1f..648ac17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,9 @@ this project adheres to [Semantic Versioning](https://semver.org/). - Schedule ingestion (`SchedulePoller`): real leagues and matches are upserted into the DB around the clock — the API serves real data even when no game is live. +- Built-in test dashboard at `/dashboard` (static, zero build): overview stats, + leagues, paginated matches, game inspector (event timeline + gold-diff + sparkline), live WebSocket console and request log. - Pydantic response models and a generic `Page` pagination envelope. - `limit` / `offset` pagination on `/matches`, `/games/{id}/events`, `/games/{id}/frames`. diff --git a/README.md b/README.md index d8b2106..38b709c 100644 --- a/README.md +++ b/README.md @@ -45,9 +45,11 @@ make dev # venv + install + pre-commit (or: pip install -e ".[d cp .env.example .env make run # API → http://localhost:8000/docs -make worker # worker → ingests when a match is live +make worker # worker → schedule (24/7) + live games ingestion ``` +Then open the **test dashboard** at — overview, leagues, matches, game inspector (events timeline + gold sparkline) and a live WebSocket console. + ## Endpoints | Method | Route | Returns | @@ -59,6 +61,7 @@ make worker # worker → ingests when a match is live | GET | `/games/{id}/events` | derived events (KILL/TOWER/DRAGON/BARON/INHIB) | | GET | `/games/{id}/frames` | raw frames | | WS | `/live/{id}` | live push: `subscribed` ack, then `event` / `score` messages | +| GET | `/dashboard` | built-in test dashboard (leagues, matches, game inspector, live WS console) | ## Prod (Docker, Postgres) diff --git a/app/main.py b/app/main.py index 90a0f4c..4137bcb 100644 --- a/app/main.py +++ b/app/main.py @@ -5,9 +5,12 @@ import asyncio import contextlib from contextlib import asynccontextmanager +from pathlib import Path import httpx from fastapi import FastAPI +from fastapi.responses import RedirectResponse +from fastapi.staticfiles import StaticFiles from app.api.routes import router from app.bus import get_bus @@ -74,3 +77,13 @@ async def _run_ingestion() -> None: @app.get("/health", tags=["catalog"], summary="Health check") async def health(): return {"status": "ok"} + + +# Test dashboard (static, no build step) — served at /dashboard if web/ exists. +_WEB_DIR = Path(__file__).resolve().parent.parent / "web" +if _WEB_DIR.is_dir(): + app.mount("/dashboard", StaticFiles(directory=_WEB_DIR, html=True), name="dashboard") + + @app.get("/", include_in_schema=False) + async def root() -> RedirectResponse: + return RedirectResponse(url="/dashboard/") diff --git a/tests/test_dashboard.py b/tests/test_dashboard.py new file mode 100644 index 0000000..befc65b --- /dev/null +++ b/tests/test_dashboard.py @@ -0,0 +1,19 @@ +"""The static test dashboard is served at /dashboard.""" + +from fastapi.testclient import TestClient + +from app.main import app + + +def test_dashboard_is_served(): + with TestClient(app) as client: + r = client.get("/dashboard/") + assert r.status_code == 200 + assert "control room" in r.text + + +def test_root_redirects_to_dashboard(): + with TestClient(app) as client: + r = client.get("/", follow_redirects=False) + assert r.status_code == 307 + assert r.headers["location"] == "/dashboard/" diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..c9917b0 --- /dev/null +++ b/web/index.html @@ -0,0 +1,537 @@ + + + + + +esport-api — control room + + + + + + +
+
+
+
esport—api
+
control room // test dashboard
+
+
+ checking… + + OpenAPI ↗ +
+ + +
+
+

Overview

live data +
+ +
+
+
+
leagues
+
matches
+
in progress
+
upcoming
+
+
+
+
+ + +
+

Leagues

+
+
+ + +
+

Live console

websocket
+
+
+ + +
+
connect to /live/{game_id} to stream events & score snapshots
+
+
+ + +
+

Matches

+
+
+ + +
+ + +
+
+ + + +
scheduled (local)leaguematchbostatusid
+ +
+
+ + +
+

Game inspector

+
+
+ + +
+
+
+
+
event timeline
+
no game selected
+
+
+
gold difference (blue − red)
+ +
latest frame
+
+
+
+
+
+ + +
+

Request log

+
+
+ + +
+

Coming next

roadmap
+
+
+
doneREST API — schemas, pagination, migrations
+
doneWebSocket live push — events & score
+
doneSchedule ingestion — real leagues & matches 24/7
+
nextAPI keys & rate limiting — auth headers will appear here
+
laterMore games — adapter per title (see ROADMAP.md)
+
laterComputer-vision ingestion for feed-less titles
+
+
+
+
+
+ + + +