Add telemetry dashboard and automatic server names; record Workshop 0.0.137
continuous-integration/drone/push Build is passing

This commit is contained in:
2026-11-01 19:29:54 +01:00
parent b8dfa9184e
commit 6ba3036de6
23 changed files with 1509 additions and 16 deletions
+67 -5
View File
@@ -69,13 +69,57 @@ set, administration and detailed reads fail closed. Public aggregate summaries
remain available. Public ingestion enrolls servers
independently of this admin credential. Wrangler prints the Worker URL, for example
`https://co10escape-telemetry.YOUR-SUBDOMAIN.workers.dev`. `/health` is a public liveness
check, not a database readiness test. The service does not enable CORS; a future
browser dashboard should use a backend to keep API credentials private.
check, not a database readiness test. The service does not enable CORS. The dashboard is served from the same Worker
and uses your entered read credential without embedding any service secret.
Deployment uses Cloudflare's documented [D1 migrations](https://developers.cloudflare.com/d1/reference/migrations/),
[Worker secrets](https://developers.cloudflare.com/workers/configuration/secrets/),
and [transactional D1 batches](https://developers.cloudflare.com/d1/worker-api/d1-database/#batch).
## Statistics dashboard
Open `https://co10escape-telemetry.zacekjakub.workers.dev/dashboard` after deploying
this Worker. Enter the existing `READ_TOKEN` (recommended) or `ADMIN_TOKEN`.
The static login page is public; every detailed data request is authenticated.
Tokens remain in tab memory, never in URLs, browser storage or bundled assets.
Refreshing the page or signing out clears the token. There are no third-party
scripts, fonts or analytics and no server-administration buttons.
- **Overview:** total servers/reports/missions, wins, losses, win rate, outcome
breakdown and terrain records. Counts cover all stored records, not just the page.
- **Servers:** per-installation mission totals, last stored report and its event
type, latest reported terrain/player count, registration and ingestion status.
Search by saved name or installation ID. Unnamed servers retain their UUID.
Updated missions automatically report the configured game-server name; automatic
installation labels follow the newest nonblank reported name. Blank/older queued
reports cannot erase it. Explicitly managed registration labels remain unchanged.
- **Missions:** page through history, filter by server/outcome/exact terrain,
and open players, mods, numeric settings, coverage flags, selected snapshot JSON
and paginated raw event history. Download the selected snapshot as JSON.
- **Players:** personal escapes and combat totals, including counter coverage.
Coverage counts are not counts of missions with friendly-fire incidents.
Selecting a server scopes Overview, Missions and Players. The Servers tab returns
to all installations. Overview shows at most 100 terrains with an explicit notice
if truncated; all missions remain accessible through filters and pagination.
Lists show 25 records per page. Automatic refresh is disabled. Use **Refresh**
to update the current view; navigation and filters fetch their requested records.
**Last received** is the collector's timestamp of the latest newly stored event.
It includes late queued events that do not replace a completed mission snapshot;
idempotent retries do not advance it. The separately shown server-reported time
may have clock skew. Game heartbeats normally arrive every five minutes. Neither
fresh receipts nor silence prove online/offline status. A server's Enabled/Revoked
label controls ingestion and is not an online indicator. No-report servers show
“No reports yet”; absent counters and undefined win rates show a dash.
Run `npm run migrate:remote` before `npm run deploy`. Migration 0003 adds a
receipt lookup index; 0004 maintains automatic server labels from accepted events
and recovers any existing reported names without rewriting event history.
The webpage requires no mission update. Automatic name discovery requires the
updated mission release; no operator configuration is needed. Until such a server
reports, blank historical names cannot be reconstructed from its installation UUID.
## Automatic community-server enrollment
The updated addon saves a random server ID in its telemetry state and generates
@@ -179,18 +223,20 @@ they are not invented as zero. This feature requires the updated Workshop build.
## API
All responses are JSON with `Cache-Control: no-store`. Administrative endpoints
API responses are JSON with `Cache-Control: no-store`. Administrative endpoints
require `Authorization: Bearer ADMIN_TOKEN`. Read endpoints accept ADMIN_TOKEN or
the optional READ_TOKEN. Public summaries and `/health` do not require a token;
ingestion uses each installation's own credential.
| Method and path | Purpose |
| --- | --- |
| `GET /dashboard` | Browser statistics dashboard; details require a read/admin token |
| `GET /api/dashboard` | Authenticated overview, optional `serverId`; totals, outcomes and up to 100 terrains |
| `GET /health` | Public liveness check |
| `GET /`, `GET /api/summary`, `GET /api/mission-events` | Public map win/failure summaries; optional `terrain` and `serverId` filters |
| `POST /api/mission-events` | Ingest `{ "token": "...", "event": {...} }`; first valid event enrolls an unknown server |
| `POST /api/servers` | Register `{serverId, name}` and return a new token |
| `GET /api/servers` | List server labels, IDs, registration origin, creation times and active state; never token hashes |
| `GET /api/servers` | Read/admin access: labels, IDs, registration, mission totals, last received report and active ingestion state; optional `q` name/ID search; never token hashes |
| `POST /api/servers/:serverId/token` | Rotate token and reactivate server |
| `DELETE /api/servers/:serverId` | Revoke ingestion; retain history |
| `GET /api/missions` | Mission summaries, newest-created first |
@@ -200,7 +246,7 @@ ingestion uses each installation's own credential.
| `GET /api/players` | Authenticated participants' attempts, personal escapes and combat totals |
`/api/missions`, `/api/stats` and `/api/players` accept an optional `serverId` filter.
`/api/missions` also accepts `outcome` (`victory`, `defeat`, `preparation_failed`,
`/api/missions` also accepts an exact, case-insensitive `terrain` filter and `outcome` (`victory`, `defeat`, `preparation_failed`,
`restarted_empty`, `interrupted`, or `unfinished`). Mission, server and event lists
accept `limit` (1–100, default 50) and `before` from the preceding `nextBefore`.
Player lists sort by stable account ID and use `after` from `nextAfter`, plus
@@ -251,3 +297,19 @@ policy should preserve deduplication tombstones if raw payloads are removed.
Keep read credentials private: mission details contain player names and account
IDs. Worker request logging is disabled in the supplied configuration. Cloudflare
platform logging/settings are managed separately in the account.
## Dashboard verification (2026-11-01)
20 local tests pass, including real Miniflare/D1 ingestion and aggregate queries,
read-only authorization, late receipt/final-result separation, pagination and
client auth/navigation/detail/error behavior using a minimal DOM boundary.
`npm run check` also exercises the exact Wrangler-bundled browser script; keep
`keep_names: false` so function serialization has no Worker-only helper references.
Migration 0003 was applied and Worker version
`8fa09cd0-f148-43d4-a6d8-685528d73618` deployed. Live dashboard HTML/JS/CSS returned
200 and matched the tested bundle byte-for-byte; health/public summary returned
200 and unauthenticated detailed routes returned 401. No production test events
were submitted. Native browser visual/accessibility verification was unavailable
in this session; the DOM tests do not establish layout quality. Authenticated
production data rendering was not exercised with the operator's private token.