docs: update documentation to OKF v0.1 format

This commit is contained in:
OpenVelo Agent
2026-07-22 14:27:40 +00:00
parent c52a736ae1
commit f04c1517b5
4 changed files with 46 additions and 43 deletions
+9 -7
View File
@@ -1,9 +1,9 @@
--- ---
type: api type: api
title: Admin Endpoints title: Admin Endpoints
description: Admin-only endpoints for user management, general settings, categories, and supporting uploads. description: Admin-only endpoints for user management, required general settings, categories, and supporting uploads.
tags: [api, admin, users, general, categories] tags: [api, admin, users, general, categories]
timestamp: 2026-07-22T12:00:00Z timestamp: 2026-07-22T14:24:08Z
--- ---
# Endpoints # Endpoints
@@ -46,11 +46,13 @@ removes the row.
* `logo` up to 2048 chars (a public URL — uploaded separately) * `logo` up to 2048 chars (a public URL — uploaded separately)
* `welcomeMarkdown` up to 64 000 chars * `welcomeMarkdown` up to 64 000 chars
* `themeKey` is one of `THEME_IDS` * `themeKey` is one of `THEME_IDS`
* `eventStartUtc` / `eventEndUtc` are validated as ISO-8601 datetimes * `eventStartUtc` / `eventEndUtc` must each be non-empty ISO-8601
(zod `string().datetime(...)`) — empty strings are explicitly allowed datetimes. Empty and malformed strings produce a field-specific
to keep the event window unconfigured. `superRefine` then enforces `400 VALIDATION_FAILED` issue (`eventStartUtc must be a valid ISO-8601
`eventEndUtc > eventStartUtc` and reports the issue on `eventEndUtc` datetime` or `eventEndUtc must be a valid ISO-8601 datetime`).
when both fields are non-empty. `superRefine` additionally requires `eventEndUtc > eventStartUtc` and
reports `eventEndUtc must be strictly after eventStartUtc` on the end
field when both values parse but are out of order.
* `defaultChallengeIp` 1255 chars * `defaultChallengeIp` 1255 chars
* `registrationsEnabled` boolean * `registrationsEnabled` boolean
+8 -5
View File
@@ -1,9 +1,9 @@
--- ---
type: architecture type: architecture
title: Key Files Index title: Key Files Index
description: One-line responsibility for important source files, including bootstrap payloads, runtime theme application, authenticated event streaming, and validated site-logo uploads. description: One-line responsibility for important source and contract-test files, including strict event-window validation.
tags: [architecture, index, key-files] tags: [architecture, key-files, event-window, validation]
timestamp: 2026-07-22T13:40:00Z timestamp: 2026-07-22T14:24:08Z
--- ---
# Backend # Backend
@@ -25,7 +25,9 @@ timestamp: 2026-07-22T13:40:00Z
| `backend/src/modules/auth/auth.controller.ts` | Registers authentication and account endpoints. | | `backend/src/modules/auth/auth.controller.ts` | Registers authentication and account endpoints. |
| `backend/src/modules/auth/auth.service.ts` | Handles sessions, authentication, registration, and password changes. | | `backend/src/modules/auth/auth.service.ts` | Handles sessions, authentication, registration, and password changes. |
| `backend/src/modules/uploads/uploads.controller.ts` | Registers admin-only multipart uploads, including Sharp-backed site-logo format validation. | | `backend/src/modules/uploads/uploads.controller.ts` | Registers admin-only multipart uploads, including Sharp-backed site-logo format validation. |
| `backend/src/modules/admin/dto/general.dto.ts` | Zod schema for `PUT /api/v1/admin/general/settings` — string-length rules, theme-key enum, ISO-8601 datetime check on the event-window fields (empty string allowed), and the `endAfterStart` super-refine. | | `backend/src/modules/admin/dto/general.dto.ts` | Zod contract for `PUT /api/v1/admin/general/settings`; both event timestamps are required ISO-8601 values and end must be strictly after start. |
| `tests/backend/admin-general-event-window.spec.ts` | Focused contract tests for valid, empty, malformed, equal, and reversed event-window timestamps. |
| `tests/backend/admin-general-service.spec.ts` | General-settings schema and service tests, including per-field empty datetime failures and settings-event emission. |
# Frontend # Frontend
@@ -47,7 +49,8 @@ timestamp: 2026-07-22T13:40:00Z
| `frontend/src/app/features/shell/tabs/quick-tabs.component.ts` | Main shell navigation tabs. | | `frontend/src/app/features/shell/tabs/quick-tabs.component.ts` | Main shell navigation tabs. |
| `frontend/src/app/features/shell/change-password/change-password-modal.component.ts` | Change-password form modal. | | `frontend/src/app/features/shell/change-password/change-password-modal.component.ts` | Change-password form modal. |
| `frontend/src/app/features/admin/general.component.ts` | `AdminGeneralComponent` reactive form for `/admin/general` — per-field inline error rendering (page-title + event-start + event-end), logo upload wiring, welcome Markdown preview, event-state derivation, and SSE `general` event handling. | | `frontend/src/app/features/admin/general.component.ts` | `AdminGeneralComponent` reactive form for `/admin/general` — per-field inline error rendering (page-title + event-start + event-end), logo upload wiring, welcome Markdown preview, event-state derivation, and SSE `general` event handling. |
| `frontend/src/app/features/admin/general.pure.ts` | Pure helpers used by `AdminGeneralComponent``deriveEventState`, `endAfterStartValidator`, `normalizePageTitle`, `toIsoUtc`, `toDatetimeLocal`, `pageTitleError` / `pageTitleMessage`, `isoDatetimeValidator`, and `datetimeMessage`. | | `frontend/src/app/features/admin/general.pure.ts` | Pure General Settings helpers, including required datetime validation, field messages, UTC conversion, and end-after-start validation. |
| `tests/frontend/admin-general-pure.spec.ts` | Pure client-contract tests for required event timestamps, datetime messaging, UTC conversion, and event-window ordering. |
| `tests/frontend/authenticated-event-source.spec.ts` | Tests SSE authorization, frame transport behavior, and the `401`/`403` unauthorized path. | | `tests/frontend/authenticated-event-source.spec.ts` | Tests SSE authorization, frame transport behavior, and the `401`/`403` unauthorized path. |
| `tests/frontend/auth-session-events.spec.ts` | Pure tests for cross-tab invalidation message encoding, payload validation, and `storage`-event filtering. | | `tests/frontend/auth-session-events.spec.ts` | Pure tests for cross-tab invalidation message encoding, payload validation, and `storage`-event filtering. |
+26 -27
View File
@@ -1,9 +1,9 @@
--- ---
type: guide type: guide
title: Admin — General Settings title: Admin — General Settings
description: How an admin edits global platform settings (page title, logo, theme, event window, default challenge IP, registrations, welcome Markdown) from the /admin/general page. description: How an admin edits required platform-wide settings, including the validated event window, from the /admin/general page.
tags: [guide, admin, settings, general, tester, datetime, validation] tags: [guide, admin, settings, general, tester, datetime, validation]
timestamp: 2026-07-22T14:07:58Z timestamp: 2026-07-22T14:24:08Z
--- ---
# When this view is available # When this view is available
@@ -42,8 +42,8 @@ The page is a single reactive form with these controls (every
| Logo (file picker) | `general-logo-file` | `logo` (public URL) | Uploads via `POST /api/v1/uploads/logo`; the returned `publicUrl` is bound to a hidden input `general-logo`. | | Logo (file picker) | `general-logo-file` | `logo` (public URL) | Uploads via `POST /api/v1/uploads/logo`; the returned `publicUrl` is bound to a hidden input `general-logo`. |
| Logo upload status | `general-logo-uploading` / `general-logo-error` / `general-logo-current` | — | Inline status text under the file picker. | | Logo upload status | `general-logo-uploading` / `general-logo-error` / `general-logo-current` | — | Inline status text under the file picker. |
| Global theme | `general-themeKey` | `themeKey` | `<select>` populated from `/themes`. Value is one of `THEME_IDS`. | | Global theme | `general-themeKey` | `themeKey` | `<select>` populated from `/themes`. Value is one of `THEME_IDS`. |
| Event start (UTC) | `general-eventStart` | `eventStartUtc` | `datetime-local` input. Empty string is allowed; any non-empty value must be a valid ISO-8601 datetime — invalid input renders `general-eventStart-error`. | | Event start (UTC) | `general-eventStart` | `eventStartUtc` | Required valid ISO-8601 datetime. Empty or malformed input renders `general-eventStart-error` and disables Save. |
| Event end (UTC) | `general-eventEnd` | `eventEndUtc` | `datetime-local` input. Empty string is allowed; any non-empty value must be a valid ISO-8601 datetime — invalid input renders `general-eventEnd-error`. Must also be strictly after Event start (`general-endBeforeStart`). | | Event end (UTC) | `general-eventEnd` | `eventEndUtc` | Required valid ISO-8601 datetime. Empty or malformed input renders `general-eventEnd-error`; it must also be strictly after Event start (`general-endBeforeStart`). |
| Default challenge IP | `general-defaultIp` | `defaultChallengeIp` | Required, max 255 chars. | | Default challenge IP | `general-defaultIp` | `defaultChallengeIp` | Required, max 255 chars. |
| Enable registrations | `general-registrations` | `registrationsEnabled` | Boolean checkbox. When `false`, the public register endpoint returns `REGISTRATIONS_DISABLED`. | | Enable registrations | `general-registrations` | `registrationsEnabled` | Boolean checkbox. When `false`, the public register endpoint returns `REGISTRATIONS_DISABLED`. |
| Welcome description | `general-welcome` | `welcomeMarkdown` | Multi-line textarea; a live preview is rendered into `general-welcome-preview`. | | Welcome description | `general-welcome` | `welcomeMarkdown` | Multi-line textarea; a live preview is rendered into `general-welcome-preview`. |
@@ -74,15 +74,12 @@ The page is a single reactive form with these controls (every
strictly after the start, the form becomes invalid and strictly after the start, the form becomes invalid and
`general-endBeforeStart` appears under the end input. Save remains `general-endBeforeStart` appears under the end input. Save remains
disabled. disabled.
* **Per-field datetime validation:** if the user types (or leaves) a value * **Per-field datetime validation:** clearing a field or supplying a value
that is not parseable as an ISO-8601 datetime in either the Event start that cannot be parsed as an ISO-8601 datetime makes that control invalid.
or Event end input, the affected input flips `aria-invalid="true"` and The affected input receives `aria-invalid="true"`, references its inline
its inline error element (`general-eventStart-error` or error through `aria-describedby`, and renders
`general-eventEnd-error`) renders the message `"<Field> must be a valid ISO-8601 datetime."`. Save remains disabled
`"<Field> must be a valid ISO-8601 datetime."`. Empty strings are until both timestamps are valid and ordered correctly.
considered valid (the event window may be unconfigured), so clearing
both inputs removes the error. Save remains disabled while either input
is invalid.
* **Save:** clicking Save sends `PUT /api/v1/admin/general/settings` * **Save:** clicking Save sends `PUT /api/v1/admin/general/settings`
with all fields. The Page title input is validated client-side for with all fields. The Page title input is validated client-side for
non-blank content; an empty or whitespace-only Page title disables the non-blank content; an empty or whitespace-only Page title disables the
@@ -134,13 +131,11 @@ two pure helpers exported from
`frontend/src/app/features/admin/general.pure.ts`: `frontend/src/app/features/admin/general.pure.ts`:
* `isoDatetimeValidator(ctrl)` — an Angular reactive-forms validator * `isoDatetimeValidator(ctrl)` — an Angular reactive-forms validator
that returns `null` for an empty value and `{ invalidDatetime: true }` that returns `null` only when `Date.parse` accepts the value. Empty and
for any non-empty value that `Date.parse` cannot parse. Empty is malformed values return `{ invalidDatetime: true }`.
intentionally valid so the event window can be cleared.
* `datetimeMessage(fieldLabel, value, controlErrors)` — renders the * `datetimeMessage(fieldLabel, value, controlErrors)` — renders the
human-readable message `"<Field> must be a valid ISO-8601 datetime."` human-readable message `"<Field> must be a valid ISO-8601 datetime."`
either when `controlErrors.invalidDatetime` is set, or as a fallback when `invalidDatetime` is set or when the raw value is unparseable.
when the raw value itself is non-empty and unparseable.
The component wires both controls (`eventStartUtc`, `eventEndUtc`) with The component wires both controls (`eventStartUtc`, `eventEndUtc`) with
this validator and keeps three local `signal`s per field — `Value`, this validator and keeps three local `signal`s per field — `Value`,
@@ -156,19 +151,20 @@ The `computed` signals `showEventStartError` / `showEventEndError` and
| Condition | Message | | Condition | Message |
|------------------------------------------------------------|--------------------------------------------------------| |------------------------------------------------------------|--------------------------------------------------------|
| Empty value (`''`) | `null` (no error; the event window may be unconfigured). | | Empty value (`''`) | `"<Field> must be a valid ISO-8601 datetime."` |
| Non-empty value that `Date.parse` accepts | `null` (no error). | | Value that `Date.parse` accepts | `null` (no error). |
| Non-empty value that `Date.parse` rejects | `"<Field> must be a valid ISO-8601 datetime."` | | Non-empty value that `Date.parse` rejects | `"<Field> must be a valid ISO-8601 datetime."` |
| Control carries `invalidDatetime` error | Same message (covered by the validator path). | | Control carries `invalidDatetime` error | Same message (covered by the validator path). |
The error is shown only when the control is both `touched || dirty` The error is shown only when the control is both `touched || dirty`
AND `invalid`, so the inputs do not flash errors on initial load. After AND `invalid`, so the inputs do not flash errors on initial load. After
a successful Save, both controls are reset to untouched/pristine via a successful Save, both controls are reset to untouched/pristine via
`resetTouchedState()` and their `TouchedOrDirty` signals are cleared. `resetTouchedState()` and their `TouchedOrDirty` signals are cleared.
The cross-field `endBeforeStart` error (`general-endBeforeStart`) is The cross-field `endBeforeStart` error (`general-endBeforeStart`) is
still emitted from the form-level `superRefine`/validator chain and is emitted by the form-level validator whenever both valid timestamps are
unchanged — it now sits next to the new per-field messages when the present and end is not strictly after start. The Event end input also
user supplies two non-empty values that are out of order. receives `aria-invalid="true"`, references both possible error elements,
and exposes `Event end must be after event start.` as its title.
# Visual elements # Visual elements
@@ -193,7 +189,7 @@ user supplies two non-empty values that are out of order.
| 3 | `frontend/src/app/core/services/admin.service.ts` | `getGeneralSettings()``GET /api/v1/admin/general/settings`; `listAdminThemes()``GET /api/v1/admin/general/themes`; `updateGeneralSettings()``PUT .../settings`; `uploadLogo()``POST /api/v1/uploads/logo`. | | 3 | `frontend/src/app/core/services/admin.service.ts` | `getGeneralSettings()``GET /api/v1/admin/general/settings`; `listAdminThemes()``GET /api/v1/admin/general/themes`; `updateGeneralSettings()``PUT .../settings`; `uploadLogo()``POST /api/v1/uploads/logo`. |
| 4 | `backend/src/modules/admin/admin-general.controller.ts` | `AdminGuard` + `@Roles('admin')` on every handler. | | 4 | `backend/src/modules/admin/admin-general.controller.ts` | `AdminGuard` + `@Roles('admin')` on every handler. |
| 5 | `backend/src/modules/admin/general.service.ts` | `getSettings` reads 8 keys via `SettingsService`; `updateSettings` writes all 8, emits `{ topic: 'general', themeKey }` via `SseHubService`. | | 5 | `backend/src/modules/admin/general.service.ts` | `getSettings` reads 8 keys via `SettingsService`; `updateSettings` writes all 8, emits `{ topic: 'general', themeKey }` via `SseHubService`. |
| 6 | `backend/src/modules/admin/dto/general.dto.ts` | `GeneralSettingsSchema` enforces string lengths, `themeKey` enum, per-field ISO-8601 datetime check on `eventStartUtc`/`eventEndUtc` (empty string allowed), and `eventEndUtc > eventStartUtc` via `superRefine`. | | 6 | `backend/src/modules/admin/dto/general.dto.ts` | `GeneralSettingsSchema` requires ISO-8601 values for both event-window fields and enforces `eventEndUtc > eventStartUtc` via `superRefine`. |
# Notes # Notes
@@ -202,8 +198,11 @@ user supplies two non-empty values that are out of order.
without polling. Other tabs do not auto-refresh settings values. without polling. Other tabs do not auto-refresh settings values.
* The "Event controls" toggle is intentionally a derived display, not * The "Event controls" toggle is intentionally a derived display, not
an editable control — adjust the UTC timestamps to change state. an editable control — adjust the UTC timestamps to change state.
* All timestamps are stored as ISO-8601 UTC strings in the `setting` * All valid timestamps are stored as ISO-8601 UTC strings in the `setting`
table; the UI converts to/from `datetime-local` for display. table; the UI converts to/from `datetime-local` for display.
* Both timestamps are required by the General Settings form and API.
The `UNCONFIGURED` derived display remains a defensive state for missing
or invalid data, but clearing either input makes the form unsavable.
* Saving the form requires the page title and default challenge IP to * Saving the form requires the page title and default challenge IP to
be non-empty; the form is `invalid` and Save stays disabled until be non-empty; the form is `invalid` and Save stays disabled until
they are. they are.
+3 -4
View File
@@ -11,7 +11,7 @@ scoreboard, an event window with a public countdown, theming, and admin
controls. controls.
The docs below are organized by purpose so agents can pull just the slice The docs below are organized by purpose so agents can pull just the slice
they need. Last regenerated 2026-07-22T13:40:00Z. they need. Last regenerated 2026-07-22T14:24:08Z.
# Architecture # Architecture
@@ -62,9 +62,8 @@ they need. Last regenerated 2026-07-22T13:40:00Z.
navigate the post-login admin area side-nav (General, Challenges, navigate the post-login admin area side-nav (General, Challenges,
Players, Blog, System) and reach the enabled child pages. Players, Blog, System) and reach the enabled child pages.
* [Admin — General Settings](/guides/admin-general-settings.md) - How an * [Admin — General Settings](/guides/admin-general-settings.md) - How an
admin edits platform-wide settings (page title, logo, theme, event admin edits platform-wide settings, including the required and strictly
window, default challenge IP, registrations, welcome Markdown) at ordered event window, at `/admin/general`.
`/admin/general`.
* [Admin — Categories](/guides/admin-categories.md) - How an admin lists, * [Admin — Categories](/guides/admin-categories.md) - How an admin lists,
creates, edits, and deletes challenge categories at `/admin/categories`, creates, edits, and deletes challenge categories at `/admin/categories`,
including system-row and challenge-attached protection. including system-row and challenge-attached protection.