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
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]
timestamp: 2026-07-22T12:00:00Z
timestamp: 2026-07-22T14:24:08Z
---
# Endpoints
@@ -46,11 +46,13 @@ removes the row.
* `logo` up to 2048 chars (a public URL — uploaded separately)
* `welcomeMarkdown` up to 64 000 chars
* `themeKey` is one of `THEME_IDS`
* `eventStartUtc` / `eventEndUtc` are validated as ISO-8601 datetimes
(zod `string().datetime(...)`) — empty strings are explicitly allowed
to keep the event window unconfigured. `superRefine` then enforces
`eventEndUtc > eventStartUtc` and reports the issue on `eventEndUtc`
when both fields are non-empty.
* `eventStartUtc` / `eventEndUtc` must each be non-empty ISO-8601
datetimes. Empty and malformed strings produce a field-specific
`400 VALIDATION_FAILED` issue (`eventStartUtc must be a valid ISO-8601
datetime` or `eventEndUtc must be a valid ISO-8601 datetime`).
`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
* `registrationsEnabled` boolean
+8 -5
View File
@@ -1,9 +1,9 @@
---
type: architecture
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.
tags: [architecture, index, key-files]
timestamp: 2026-07-22T13:40:00Z
description: One-line responsibility for important source and contract-test files, including strict event-window validation.
tags: [architecture, key-files, event-window, validation]
timestamp: 2026-07-22T14:24:08Z
---
# 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.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/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
@@ -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/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.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/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
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]
timestamp: 2026-07-22T14:07:58Z
timestamp: 2026-07-22T14:24:08Z
---
# 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 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`. |
| 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 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 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` | 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. |
| 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`. |
@@ -74,15 +74,12 @@ The page is a single reactive form with these controls (every
strictly after the start, the form becomes invalid and
`general-endBeforeStart` appears under the end input. Save remains
disabled.
* **Per-field datetime validation:** if the user types (or leaves) a value
that is not parseable as an ISO-8601 datetime in either the Event start
or Event end input, the affected input flips `aria-invalid="true"` and
its inline error element (`general-eventStart-error` or
`general-eventEnd-error`) renders the message
`"<Field> must be a valid ISO-8601 datetime."`. Empty strings are
considered valid (the event window may be unconfigured), so clearing
both inputs removes the error. Save remains disabled while either input
is invalid.
* **Per-field datetime validation:** clearing a field or supplying a value
that cannot be parsed as an ISO-8601 datetime makes that control invalid.
The affected input receives `aria-invalid="true"`, references its inline
error through `aria-describedby`, and renders
`"<Field> must be a valid ISO-8601 datetime."`. Save remains disabled
until both timestamps are valid and ordered correctly.
* **Save:** clicking Save sends `PUT /api/v1/admin/general/settings`
with all fields. The Page title input is validated client-side for
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`:
* `isoDatetimeValidator(ctrl)` — an Angular reactive-forms validator
that returns `null` for an empty value and `{ invalidDatetime: true }`
for any non-empty value that `Date.parse` cannot parse. Empty is
intentionally valid so the event window can be cleared.
that returns `null` only when `Date.parse` accepts the value. Empty and
malformed values return `{ invalidDatetime: true }`.
* `datetimeMessage(fieldLabel, value, controlErrors)` — renders the
human-readable message `"<Field> must be a valid ISO-8601 datetime."`
either when `controlErrors.invalidDatetime` is set, or as a fallback
when the raw value itself is non-empty and unparseable.
when `invalidDatetime` is set or when the raw value is unparseable.
The component wires both controls (`eventStartUtc`, `eventEndUtc`) with
this validator and keeps three local `signal`s per field — `Value`,
@@ -156,19 +151,20 @@ The `computed` signals `showEventStartError` / `showEventEndError` and
| Condition | Message |
|------------------------------------------------------------|--------------------------------------------------------|
| Empty value (`''`) | `null` (no error; the event window may be unconfigured). |
| Non-empty value that `Date.parse` accepts | `null` (no error). |
| 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). |
| Empty value (`''`) | `"<Field> must be a valid ISO-8601 datetime."` |
| Value that `Date.parse` accepts | `null` (no error). |
| 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). |
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
a successful Save, both controls are reset to untouched/pristine via
`resetTouchedState()` and their `TouchedOrDirty` signals are cleared.
The cross-field `endBeforeStart` error (`general-endBeforeStart`) is
still emitted from the form-level `superRefine`/validator chain and is
unchanged — it now sits next to the new per-field messages when the
user supplies two non-empty values that are out of order.
emitted by the form-level validator whenever both valid timestamps are
present and end is not strictly after start. The Event end input also
receives `aria-invalid="true"`, references both possible error elements,
and exposes `Event end must be after event start.` as its title.
# 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`. |
| 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`. |
| 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
@@ -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.
* The "Event controls" toggle is intentionally a derived display, not
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.
* 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
be non-empty; the form is `invalid` and Save stays disabled until
they are.
+3 -4
View File
@@ -11,7 +11,7 @@ scoreboard, an event window with a public countdown, theming, and admin
controls.
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
@@ -62,9 +62,8 @@ they need. Last regenerated 2026-07-22T13:40:00Z.
navigate the post-login admin area side-nav (General, Challenges,
Players, Blog, System) and reach the enabled child pages.
* [Admin — General Settings](/guides/admin-general-settings.md) - How an
admin edits platform-wide settings (page title, logo, theme, event
window, default challenge IP, registrations, welcome Markdown) at
`/admin/general`.
admin edits platform-wide settings, including the required and strictly
ordered event window, at `/admin/general`.
* [Admin — Categories](/guides/admin-categories.md) - How an admin lists,
creates, edits, and deletes challenge categories at `/admin/categories`,
including system-row and challenge-attached protection.