docs: update documentation to OKF v0.1 format
This commit is contained in:
+14
-1
@@ -3,7 +3,7 @@ type: api
|
|||||||
title: Uploads Endpoints
|
title: Uploads Endpoints
|
||||||
description: Admin-only multipart upload endpoints for site logos, category icons, and challenge files.
|
description: Admin-only multipart upload endpoints for site logos, category icons, and challenge files.
|
||||||
tags: [api, uploads, multipart, admin]
|
tags: [api, uploads, multipart, admin]
|
||||||
timestamp: 2026-07-22T13:05:30Z
|
timestamp: 2026-07-22T19:18:00Z
|
||||||
---
|
---
|
||||||
|
|
||||||
# Endpoints
|
# Endpoints
|
||||||
@@ -37,6 +37,19 @@ valid admin session and the standard CSRF cookie/header pair.
|
|||||||
* An accepted logo keeps its sanitized filename, is written directly under
|
* An accepted logo keeps its sanitized filename, is written directly under
|
||||||
`UPLOAD_DIR`, and returns `{ publicUrl, originalFilename }`, with `publicUrl`
|
`UPLOAD_DIR`, and returns `{ publicUrl, originalFilename }`, with `publicUrl`
|
||||||
in the form `/uploads/{safeFilename}`.
|
in the form `/uploads/{safeFilename}`.
|
||||||
|
* Category icons are decoded with Sharp and normalized to a 128-by-128 PNG.
|
||||||
|
When `categoryId` is supplied, the file is written to
|
||||||
|
`UPLOAD_DIR/icons/{categoryId}.png` (overwriting any previous icon for that
|
||||||
|
category) and the response carries `{ id: "{categoryId}.png", publicUrl,
|
||||||
|
width: 128, height: 128, mimeType: "image/png", storedPath, size,
|
||||||
|
originalFilename }`.
|
||||||
|
* Category-icon validation is strict: a missing `file`, an oversized upload,
|
||||||
|
a corrupt/non-image payload, or a buffer that Sharp cannot decode returns
|
||||||
|
`400 Bad Request`. Invalid category-icon payloads use the message
|
||||||
|
`Category icon must be a valid PNG, JPEG, GIF, or WebP image.` and **are
|
||||||
|
not persisted** — the existing icon file (if any) on disk is left untouched.
|
||||||
|
The multipart MIME type, the filename extension, and any browser `accept`
|
||||||
|
metadata are not trusted as proof of validity.
|
||||||
* A missing `file` part, an oversized upload, a corrupt image, or a decoded logo
|
* A missing `file` part, an oversized upload, a corrupt image, or a decoded logo
|
||||||
in another format returns `400 Bad Request`. Invalid logo payloads use the
|
in another format returns `400 Bad Request`. Invalid logo payloads use the
|
||||||
message `Logo must be a valid PNG, JPEG, GIF, or WebP image.` and are not
|
message `Logo must be a valid PNG, JPEG, GIF, or WebP image.` and are not
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ type: architecture
|
|||||||
title: Key Files Index
|
title: Key Files Index
|
||||||
description: One-line responsibility for important source and contract-test files, including strict event-window validation and the public bootstrap SSE listener.
|
description: One-line responsibility for important source and contract-test files, including strict event-window validation and the public bootstrap SSE listener.
|
||||||
tags: [architecture, key-files, event-window, validation, default-challenge-ip, sse, bootstrap, migrations]
|
tags: [architecture, key-files, event-window, validation, default-challenge-ip, sse, bootstrap, migrations]
|
||||||
timestamp: 2026-07-22T18:37:00Z
|
timestamp: 2026-07-22T19:18:00Z
|
||||||
---
|
---
|
||||||
|
|
||||||
# Backend
|
# Backend
|
||||||
@@ -25,7 +25,7 @@ timestamp: 2026-07-22T18:37:00Z
|
|||||||
| `backend/src/modules/admin/general.service.ts` | Reads/writes global settings, filters available theme files, and publishes the `general` SSE notification after updates. |
|
| `backend/src/modules/admin/general.service.ts` | Reads/writes global settings, filters available theme files, and publishes the `general` SSE notification after updates. |
|
||||||
| `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 and strict category-icon decode (rejects undecodable / non-image buffers with HTTP 400 before any filesystem write so existing icons are preserved). |
|
||||||
| `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. |
|
| `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-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. |
|
| `tests/backend/admin-general-service.spec.ts` | General-settings schema and service tests, including per-field empty datetime failures and settings-event emission. |
|
||||||
@@ -52,7 +52,7 @@ timestamp: 2026-07-22T18:37: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/categories/category-form-modal.component.ts` | Standalone OnPush modal for create + edit; owns the `CategoryFormGroup`, exposes the pure `syncCategoryForm` helper, and reacts to `open` / `mode` / `category` signal inputs via a `markForCheck` effect so edit prefill reaches the DOM. |
|
| `frontend/src/app/features/admin/categories/category-form-modal.component.ts` | Standalone OnPush modal for create + edit; owns the `CategoryFormGroup`, exposes the pure `syncCategoryForm` helper, binds `[formGroup]` on its template `<form>`, accepts `errorMessage` + `saving` parent inputs (renders `cf-error` and disables OK while saving), and reacts to `open` / `mode` / `category` signal inputs via a `markForCheck` effect so edit prefill reaches the DOM. |
|
||||||
| `frontend/src/app/features/admin/general.pure.ts` | Pure General Settings helpers, including required datetime validation, field messages, UTC conversion, end-after-start validation, and the default-challenge-address IPv4/hostname validator/normalizer/message trio. |
|
| `frontend/src/app/features/admin/general.pure.ts` | Pure General Settings helpers, including required datetime validation, field messages, UTC conversion, end-after-start validation, and the default-challenge-address IPv4/hostname validator/normalizer/message trio. |
|
||||||
| `tests/frontend/admin-general-pure.spec.ts` | Pure client-contract tests for required event timestamps, datetime messaging, UTC conversion, event-window ordering, and default-challenge-address validation, error mapping, and normalization. |
|
| `tests/frontend/admin-general-pure.spec.ts` | Pure client-contract tests for required event timestamps, datetime messaging, UTC conversion, event-window ordering, and default-challenge-address validation, error mapping, and normalization. |
|
||||||
| `tests/frontend/admin-categories-form-modal.spec.ts` | Tests the pure `syncCategoryForm` helper that drives the edit/create prefill in `CategoryFormModalComponent`: system-row abbreviation lock, user-row unlock, re-population on second invocation, clearing on create, and iconPreview passthrough. |
|
| `tests/frontend/admin-categories-form-modal.spec.ts` | Tests the pure `syncCategoryForm` helper that drives the edit/create prefill in `CategoryFormModalComponent`: system-row abbreviation lock, user-row unlock, re-population on second invocation, clearing on create, and iconPreview passthrough. |
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ type: guide
|
|||||||
title: Admin — Categories
|
title: Admin — Categories
|
||||||
description: How an admin lists, creates, edits, and deletes challenge categories from the /admin/categories page, including system-row protection and challenge-attached protection.
|
description: How an admin lists, creates, edits, and deletes challenge categories from the /admin/categories page, including system-row protection and challenge-attached protection.
|
||||||
tags: [guide, admin, categories, tester]
|
tags: [guide, admin, categories, tester]
|
||||||
timestamp: 2026-07-22T18:37:00Z
|
timestamp: 2026-07-22T19:18:00Z
|
||||||
---
|
---
|
||||||
|
|
||||||
# When this view is available
|
# When this view is available
|
||||||
@@ -45,6 +45,7 @@ side-nav (categories are also embedded inside the General settings page).
|
|||||||
| Form modal | `[data-testid="cat-form-modal"]` | Create/edit dialog (`cat-form-backdrop` is the click-outside dismiss layer). |
|
| Form modal | `[data-testid="cat-form-modal"]` | Create/edit dialog (`cat-form-backdrop` is the click-outside dismiss layer). |
|
||||||
| Delete modal | `[data-testid="cat-delete-modal"]` | Confirmation dialog (`cat-delete-backdrop` is the dismiss layer). |
|
| Delete modal | `[data-testid="cat-delete-modal"]` | Confirmation dialog (`cat-delete-backdrop` is the dismiss layer). |
|
||||||
| Delete error | `[data-testid="cat-delete-error"]` | Inline error message after a failed delete. |
|
| Delete error | `[data-testid="cat-delete-error"]` | Inline error message after a failed delete. |
|
||||||
|
| Form error | `[data-testid="cf-error"]` | Inline error rendered inside the form modal after a failed create/update/upload. |
|
||||||
|
|
||||||
# Form modal fields
|
# Form modal fields
|
||||||
|
|
||||||
@@ -54,7 +55,7 @@ side-nav (categories are also embedded inside the General settings page).
|
|||||||
| Abbreviation (uppercase) | `cf-abbr` | Required, 2–6 chars; server upper-cases on save. `readonly` when editing a system row. |
|
| Abbreviation (uppercase) | `cf-abbr` | Required, 2–6 chars; server upper-cases on save. `readonly` when editing a system row. |
|
||||||
| Description | `cf-desc` | Optional, max 2000 chars. |
|
| Description | `cf-desc` | Optional, max 2000 chars. |
|
||||||
| Icon (file picker) | `cf-icon` | Optional image; uploaded to `POST /api/v1/uploads/category-icon` and resized/normalized server-side. |
|
| Icon (file picker) | `cf-icon` | Optional image; uploaded to `POST /api/v1/uploads/category-icon` and resized/normalized server-side. |
|
||||||
| Cancel / OK | `cf-cancel`, `cf-ok` | OK disabled while form invalid or already submitting. |
|
| Cancel / OK | `cf-cancel`, `cf-ok` | OK disabled while form invalid, already submitting, or while the parent is performing the async save (bound `saving` input). |
|
||||||
|
|
||||||
# Expected behavior
|
# Expected behavior
|
||||||
|
|
||||||
@@ -84,10 +85,23 @@ side-nav (categories are also embedded inside the General settings page).
|
|||||||
|
|
||||||
1. Click `cat-edit-{ABBR}` → modal opens in edit mode, pre-filled with
|
1. Click `cat-edit-{ABBR}` → modal opens in edit mode, pre-filled with
|
||||||
the row's values.
|
the row's values.
|
||||||
2. For system rows, the abbreviation field is `readonly`.
|
2. For system rows, the abbreviation field is `readonly`. For user
|
||||||
3. On save the component issues `updateCategory(id, {...})`. If an icon
|
(non-system) rows the abbreviation is editable and is uppercased
|
||||||
file is selected, the new icon is uploaded first and the returned
|
server-side on save (the existing category row is re-sorted
|
||||||
`publicUrl` replaces `iconPath` in the same update.
|
alphabetically by the defensive client sort after a successful
|
||||||
|
update).
|
||||||
|
3. On save the component issues `updateCategory(id, {...})` with
|
||||||
|
`name`, `abbreviation`, `description`, and (if an icon file was
|
||||||
|
chosen) `iconPath`. When an icon file is selected, the new icon is
|
||||||
|
uploaded first via `POST /api/v1/uploads/category-icon` and the
|
||||||
|
returned `publicUrl` replaces `iconPath` in the same update.
|
||||||
|
4. On either an upload rejection (HTTP 400) or an update rejection
|
||||||
|
(HTTP 400/404/409), the modal stays open, the OK button is disabled
|
||||||
|
while the request is in flight via the bound `saving` flag, and the
|
||||||
|
server message is rendered inside the modal at
|
||||||
|
`data-testid="cf-error"`. The list is not refreshed and any
|
||||||
|
previously-valid icon on disk is preserved (the upload endpoint
|
||||||
|
does not overwrite when Sharp decode fails).
|
||||||
|
|
||||||
### Edit prefill mechanics
|
### Edit prefill mechanics
|
||||||
|
|
||||||
@@ -112,6 +126,29 @@ input. `syncCategoryForm` is exported from the modal file so it can be
|
|||||||
exercised in isolation by
|
exercised in isolation by
|
||||||
`tests/frontend/admin-categories-form-modal.spec.ts`.
|
`tests/frontend/admin-categories-form-modal.spec.ts`.
|
||||||
|
|
||||||
|
### Save error and loading state
|
||||||
|
|
||||||
|
`CategoryFormModalComponent` is purely presentational and does not own
|
||||||
|
HTTP state. It exposes two additional signal inputs:
|
||||||
|
|
||||||
|
* `errorMessage: string | null` — when truthy, an inline error paragraph
|
||||||
|
is rendered at `data-testid="cf-error"` showing the supplied message.
|
||||||
|
* `saving: boolean` — when `true`, the OK button is disabled in addition
|
||||||
|
to the existing `form.invalid` and `submitting` guards.
|
||||||
|
|
||||||
|
The parent `AdminCategoriesComponent` owns both signals:
|
||||||
|
|
||||||
|
* `formError` — set from the server's `error.message` (or a generic
|
||||||
|
fallback) when create/update/upload fails, and cleared every time the
|
||||||
|
modal is opened or a submission begins.
|
||||||
|
* `saving` — set to `true` at the start of `onFormSubmit` and reset to
|
||||||
|
`false` in `finally`, so the modal can never get stuck in a
|
||||||
|
"submitting" state if the request rejects.
|
||||||
|
|
||||||
|
This separation matters because `deleteError` (rendered by the delete
|
||||||
|
modal at `data-testid="cat-delete-error"`) is a different state machine
|
||||||
|
and must not be reused for create/update failures.
|
||||||
|
|
||||||
## Delete behavior
|
## Delete behavior
|
||||||
|
|
||||||
* **System rows:** the delete modal renders
|
* **System rows:** the delete modal renders
|
||||||
|
|||||||
+1
-1
@@ -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-22T18:37:00Z.
|
they need. Last regenerated 2026-07-22T19:18:00Z.
|
||||||
|
|
||||||
# Architecture
|
# Architecture
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user