docs: update documentation to OKF v0.1 format
This commit is contained in:
+71
-18
@@ -1,9 +1,9 @@
|
||||
---
|
||||
type: guide
|
||||
title: Event Window
|
||||
description: How the live event countdown is computed and surfaced via REST + SSE.
|
||||
tags: [guide, event, countdown, sse]
|
||||
timestamp: 2026-07-21T18:28:00Z
|
||||
description: How the event window state machine is computed and surfaced via REST + SSE.
|
||||
tags: [guide, event, countdown, sse, state-machine]
|
||||
timestamp: 2026-07-21T22:19:08Z
|
||||
---
|
||||
|
||||
# Configuration
|
||||
@@ -13,33 +13,86 @@ Two `setting` rows control the window:
|
||||
| Setting key | Default (seeded) | Description |
|
||||
|-------------------|------------------|--------------------------------------------|
|
||||
| `eventStartUtc` | `now + 60s` | ISO 8601 instant the event becomes live. |
|
||||
| `eventEndUtc` | `now + 7d` | ISO 8601 instant the event ends. |
|
||||
| `eventEndUtc` | `now + 7d` | ISO 8601 instant the event ends. |
|
||||
|
||||
Both are seeded by `SeedSystemData1700000000100` and editable through
|
||||
`SettingsService`.
|
||||
|
||||
# State computation
|
||||
# State machine
|
||||
|
||||
`EventStatusService.getStatus(now)` (`backend/src/common/services/event-status.service.ts`)
|
||||
returns one of three branches:
|
||||
`EventStatusService.getState(now)`
|
||||
(`backend/src/common/services/event-status.service.ts`) returns one of
|
||||
four branches:
|
||||
|
||||
| Condition | `status` | `countdownMs` |
|
||||
|-------------------------|----------|----------------------|
|
||||
| `now < eventStartUtc` | `Stopped` (countdown) | `eventStartUtc - now` |
|
||||
| `now ∈ [start, end]` | `Running` | `eventEndUtc - now` |
|
||||
| `now >= eventEndUtc` | `Stopped` (ended) | `0` |
|
||||
| Condition | `state` | `secondsToStart` | `secondsToEnd` |
|
||||
|----------------------------------------|------------------|--------------------------|--------------------------|
|
||||
| `eventStartUtc` / `eventEndUtc` missing or unparseable | `unconfigured` | `null` | `null` |
|
||||
| `now < eventStartUtc` | `countdown` | `floor((start - now)/s)` | `null` |
|
||||
| `start <= now <= end` | `running` | `null` | `floor((end - now)/s)` |
|
||||
| `now > eventEndUtc` | `stopped` | `null` | `null` |
|
||||
|
||||
The legacy `EventStatusService.getStatus(now)` shape
|
||||
(`{status: 'Stopped'|'Running', countdownMs, startUtc, endUtc, serverNowUtc}`)
|
||||
is preserved for backward compatibility. `Stopped` is overloaded to
|
||||
mean "still counting down" (`countdownMs = eventStartUtc - now`) when
|
||||
`state === 'countdown'`, and "ended" (`countdownMs = 0`) when
|
||||
`state === 'stopped'` / `unconfigured`.
|
||||
|
||||
# Endpoints
|
||||
|
||||
* `GET /api/v1/event/status` — one-shot read (see
|
||||
[System Endpoints](/api/system.md)).
|
||||
* `GET /api/v1/event/stream` — SSE stream that pushes the same payload
|
||||
whenever `SseHubService.publish()` is called (typically by an admin
|
||||
cron tick). The SPA subscribes to keep the countdown ticking without
|
||||
polling.
|
||||
| Endpoint | Auth | Shape |
|
||||
|--------------------------------------------|-------------|-------------------------------------------|
|
||||
| `GET /api/v1/event/status` | `@Public()` | Legacy `{status, countdownMs, startUtc, endUtc, serverNowUtc}`. |
|
||||
| `GET /api/v1/event/stream` | `@Public()` | SSE that emits the legacy payload on a 1-second tick + on hub pushes. |
|
||||
| `GET /api/v1/settings/event` | `@Public()` | Raw `{eventStartUtc, eventEndUtc}` (null when unset). |
|
||||
| `GET /api/v1/events/status` | Authenticated (JWT) | SSE that emits the full `EventStatePayload` on connect + on a 60-second tick + on hub pushes; consecutive identical payloads suppressed via `distinctUntilChanged(JSON.stringify)`. |
|
||||
|
||||
See [System Endpoints](/api/system.md) for the full request/response
|
||||
shape.
|
||||
|
||||
# Frontend wiring
|
||||
|
||||
The authenticated shell subscribes to `/api/v1/events/status` from
|
||||
`HomeComponent.ngOnInit()` via `EventStatusStore.start(() => new EventSource('/api/v1/events/status', { withCredentials: true }))`
|
||||
(`frontend/src/app/features/home/home.component.ts`,
|
||||
`frontend/src/app/core/services/event-status.store.ts`). The store:
|
||||
|
||||
1. Calls `applyServerStatus(payload)` on every `message` frame (parses
|
||||
JSON, sets `state`, `serverNowUtc`, `eventStartUtc`, `eventEndUtc`,
|
||||
and stores the clock-skew anchor `Date.now() - serverNow`).
|
||||
2. Runs a local 1-second tick so the `secondsToStart` / `secondsToEnd`
|
||||
computed signals update between SSE pushes.
|
||||
3. Exposes `countdownText` (computed): `""` for `unconfigured`,
|
||||
`"Event ended"` for `stopped`, and `formatDdHhMm(seconds)` for
|
||||
`countdown` / `running`. The format helper lives in the pure module
|
||||
`event-status.pure.ts` (`DD:HH:mm`, zero-padded).
|
||||
|
||||
The header (`ShellHeaderComponent`) renders an LED with the
|
||||
`led-{state}` class so the colour reflects the current state (the
|
||||
exact theme colours are wired in `styles.css` against the existing
|
||||
`--color-success` / `--color-warning` / `--color-danger` tokens).
|
||||
|
||||
`HomeComponent.ngOnDestroy()` (and the `DestroyRef` hook in the store
|
||||
itself) close the SSE source and clear the tick interval, so leaving
|
||||
the shell (e.g. by logging out and landing on `/login`) tears the
|
||||
stream down cleanly.
|
||||
|
||||
# Push path
|
||||
|
||||
1. The admin cron / settings update writes to `setting.eventStartUtc` /
|
||||
`setting.eventEndUtc`.
|
||||
2. `SseHubService.publish('event', payload)` fans the new payload out
|
||||
to every subscriber of `/api/v1/event/stream` and (via the
|
||||
`flat()` mapping) `/api/v1/events/status`.
|
||||
3. Each connected client receives the new state immediately; otherwise
|
||||
it picks up the next 60-second tick.
|
||||
|
||||
The hub is in-process; multi-replica deployments need a shared pub/sub
|
||||
to fan out across pods (not in scope for this repo).
|
||||
|
||||
# See also
|
||||
|
||||
- [Auth and Settings Tables](/database/auth-settings.md)
|
||||
- [System Endpoints](/api/system.md)
|
||||
- [Scoreboard Stream](/guides/scoreboard-stream.md)
|
||||
- [Authenticated Shell](/guides/authenticated-shell.md)
|
||||
|
||||
Reference in New Issue
Block a user