docs: update documentation to OKF v0.1 format

This commit is contained in:
OpenVelo Agent
2026-07-22 22:18:07 +00:00
parent fff0a600df
commit edb3feb457
4 changed files with 111 additions and 9 deletions
+5 -3
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 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, the public bootstrap SSE listener, and the challenges full-replace import flow.
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, challenges, import]
timestamp: 2026-07-22T19:18:00Z timestamp: 2026-07-22T22:17:11Z
--- ---
# Backend # Backend
@@ -30,6 +30,7 @@ timestamp: 2026-07-22T19:18:00Z
| `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. |
| `tests/backend/admin-challenges-import-replace.spec.ts` | Contract tests for the confirmed-import full-replace: wipes unrelated pre-existing challenges, unlinks wiped disk files, empty archive, unknown-category skip-then-wipe. |
# Frontend # Frontend
@@ -63,6 +64,7 @@ timestamp: 2026-07-22T19:18:00Z
| `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. |
| `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. |
| `tests/frontend/admin-challenges-import-autoclose.spec.ts` | Pure helpers (`summarizeImportResult`, `importErrorMessage`) and modal auto-close branch for the confirmed-import handler. |
# See also # See also
+95
View File
@@ -0,0 +1,95 @@
---
type: guide
title: Admin — Challenges Full-Replace Import
description: How the confirmed challenges import wipes every existing challenge (with cascading files/solves and disk unlink) and inserts the archive contents in one transaction.
tags: [guide, admin, challenges, import, full-replace, tester]
timestamp: 2026-07-22T22:17:11Z
---
# What it is
The **confirmed challenges import** is the second step of the import flow
on `/admin/challenges` (after the preview). When the admin clicks
**Overwrite & Import** the client POSTs the already-validated archive to
`POST /api/v1/admin/challenges/import?confirm=overwrite`. The server
performs a **full replace** of the challenge set inside one DB
transaction, then best-effort unlinks the disk files owned by the
wiped challenges.
# When to use it
Use this when an admin wants to restore the platform challenge set to
the exact contents of a previously exported JSON archive. The user
guide for the surrounding flow lives at [Admin —
Challenges](/guides/admin-challenges.md); this concept focuses on the
import-specific behavior and its consequences.
# Behavior contract
| Phase | What happens |
|-------|--------------|
| Pre-flight | The client already validated the archive against the `IMPORT_VALIDATION_FAILED` Zod schema in [Admin Challenges DTOs](/api/admin.md#challenges). The server re-validates; preview counts (`total`, `conflicts`, `unknownCategories`, `warnings`) are surfaced by the import modal. |
| Staging | Every `dataBase64` file in the archive is decoded and staged to disk via `ChallengeFilesService.stage(...)`. If any stage fails the server rolls back every previously staged file and re-throws (mirrors the existing `rollbackStaged` safety net). |
| Transaction (DB) | The server begins a single TypeORM transaction. It loads every existing challenge, captures the disk-bound `stored_filename`s of every existing `ChallengeFile`, then `DELETE`s all existing `ChallengeEntity` rows by id. `ChallengeFileEntity` and `SolveEntity` cascade-delete via FK, so dependent rows disappear too. The archive's challenges are then inserted under their original names. |
| Post-commit (FS) | For every captured `storedFilename` the server calls `ChallengeFilesService.removeFinal(storedFilename)` (best-effort; failures are logged but do not abort). Each staged file for the imported set is committed to its final filename. |
| Response | Returns `{ imported, skipped, warnings }`. Known unknown `categorySystemKey`s are reported in `skipped[]` (still wipe everything else). |
# Side effects to expect as a tester
- **Every existing challenge disappears** from the list immediately
after the success banner — including rows whose names did not appear
in the archive. Use the Players page to confirm that pre-existing
`solve` rows for wiped challenges also vanish (they cascade).
- **Uploaded challenge files** owned by wiped rows are removed from
`./data/uploads/challenges/` post-commit. The imported archive's
files appear under the same directory using their original
filenames encoded onto the wipe.
- An empty archive (`{ challenges: [] }`) is valid and produces
`imported: 0`, an empty list, and zero on-disk challenge files.
- The **import modal auto-closes on success**; the page-level status
banner (`[data-testid="challenges-status"]`) shows the
imported/skipped/warning summary. Failures keep the modal open and
show the error inline.
# Idempotency
- Re-importing the same archive twice yields the same final DB state.
- The previous upsert-on-name semantics are gone: even challenges
whose names did not appear in the archive are removed.
# Error codes
See [Admin Endpoints](/api/admin.md) and the [Admin —
Challenges](/guides/admin-challenges.md) guide:
- `IMPORT_VALIDATION_FAILED` (400) — schema failure on preview or
confirm.
- `IMPORT_PARTIAL_FAILURE` is no longer emitted: the response is
always 200 with `{ imported, skipped, warnings }` (consistent with
partial success being the normal case when unknown categories are
present).
- Any DB transaction failure rolls back every staged file and
re-throws the original error, leaving the DB untouched.
# Wiring
| Concern | File |
|---------|------|
| Endpoint | `backend/src/modules/admin/admin-challenges.controller.ts` (`POST /api/v1/admin/challenges/import?confirm=overwrite`). |
| Orchestration | `backend/src/modules/admin/challenges.service.ts` (`importConfirmed`) — full-replace transaction + post-commit unlink. |
| File staging and disk ops | `backend/src/modules/admin/challenge-files.service.ts` (`stage`, `commit`, `removeFinal`). |
| Schema | `backend/src/modules/admin/dto/challenges.dto.ts` (`CHALLENGE_FORMAT`, `CHALLENGE_FORMAT_VERSION`, import document schema). |
| Client UI | `frontend/src/app/features/admin/challenges/challenges.component.ts` (`onImportConfirm`) — refreshes list, sets status, calls `closeImport()`. |
| Pure helpers | `frontend/src/app/features/admin/challenges/challenges.pure.ts` (`summarizeImportResult`, `importErrorMessage`). |
| Backend contract tests | `tests/backend/admin-challenges-import-replace.spec.ts` — full-replace, disk-file unlink, empty archive, unknown-category wipe. |
| Frontend contract tests | `tests/frontend/admin-challenges-import-autoclose.spec.ts` — pure helpers + modal auto-close branch. |
# Notes
- The flag is not in the archive's per-challenge `data`; only admins
can read the plaintext, and the export/import preserves the same
restriction.
- Wiping cascades remove `solve` rows for wiped challenges — the
scoreboard will recompute on the next render without those entries.
- The destroy-then-insert happens in one transaction, so a partial
failure cannot leave the platform with a mix of old and new rows.
+7 -5
View File
@@ -3,7 +3,7 @@ type: guide
title: Admin — Challenges title: Admin — Challenges
description: How an admin lists, searches, creates, edits, deletes, imports, and exports challenges from the /admin/challenges page. description: How an admin lists, searches, creates, edits, deletes, imports, and exports challenges from the /admin/challenges page.
tags: [guide, admin, challenges, import, export, files, tester] tags: [guide, admin, challenges, import, export, files, tester]
timestamp: 2026-07-22T21:25:29Z timestamp: 2026-07-22T22:17:11Z
--- ---
# When this view is available # When this view is available
@@ -104,8 +104,10 @@ The form modal has three sections (tabs): General, Connection, Files. Fields mar
1. Click **Import challenges** → a file picker accepts `.json`. The client reads it and posts it to `POST /api/v1/admin/challenges/import`. 1. Click **Import challenges** → a file picker accepts `.json`. The client reads it and posts it to `POST /api/v1/admin/challenges/import`.
2. On validation success the import modal opens with the preview (`total`, `conflicts`, `unknownCategories`, `warnings`) and the standard copy: *"Import N challenges. Existing challenges with matching names will be overwritten. New categories referenced but missing locally will be skipped with a warning. Continue?"* 2. On validation success the import modal opens with the preview (`total`, `conflicts`, `unknownCategories`, `warnings`) and the standard copy: *"Import N challenges. Existing challenges with matching names will be overwritten. New categories referenced but missing locally will be skipped with a warning. Continue?"*
3. Click **Overwrite & Import** → the client posts the same document with `?confirm=overwrite`. The server stages every file up front (rolling back any FS failure), then upserts challenges inside one transaction by case-insensitive name. 3. Click **Overwrite & Import** → the client posts the same document with `?confirm=overwrite`. The server stages every file up front (rolling back any FS failure), then **wipes every existing challenge (cascade-deleting their `challenge_file` and `solve` rows and best-effort unlinking their disk files) and inserts the archive's challenges** inside one transaction.
4. The response renders imported/skipped/warning counts; the list refreshes automatically. 4. The modal auto-closes on success and the list refreshes automatically; the page-level status banner shows imported/skipped/warning counts.
See [Admin — Challenges Full-Replace Import](/guides/admin-challenges-import.md) for the full transactional semantics, side effects, and the auto-close contract.
# Validation & error codes (server) # Validation & error codes (server)
@@ -127,7 +129,7 @@ The form modal has three sections (tabs): General, Connection, Files. Fields mar
| 3 | `frontend/src/app/core/services/admin.service.ts` | Typed `listChallenges`, `getChallengeDetail`, `createChallenge`, `updateChallenge`, `deleteChallenge`, `exportChallenges`, `previewChallengeImport`, `confirmChallengeImport`, `stageChallengeFile`, `discardChallengeFile`. | | 3 | `frontend/src/app/core/services/admin.service.ts` | Typed `listChallenges`, `getChallengeDetail`, `createChallenge`, `updateChallenge`, `deleteChallenge`, `exportChallenges`, `previewChallengeImport`, `confirmChallengeImport`, `stageChallengeFile`, `discardChallengeFile`. |
| 4 | `frontend/src/app/features/admin/challenges/challenge-form.pure.ts` | Defaults/prefill, points/port validation, first-invalid-tab selection, file-size partitioning, payload mapping. | | 4 | `frontend/src/app/features/admin/challenges/challenge-form.pure.ts` | Defaults/prefill, points/port validation, first-invalid-tab selection, file-size partitioning, payload mapping. |
| 5 | `backend/src/modules/admin/admin-challenges.controller.ts` | `AdminGuard` + `@Roles('admin')` on every handler. | | 5 | `backend/src/modules/admin/admin-challenges.controller.ts` | `AdminGuard` + `@Roles('admin')` on every handler. |
| 6 | `backend/src/modules/admin/challenges.service.ts` | CRUD, list aggregates, transactional deletion, export, import orchestration. | | 6 | `backend/src/modules/admin/challenges.service.ts` | CRUD, list aggregates, transactional deletion, export, full-replace import orchestration. |
| 7 | `backend/src/modules/admin/challenge-files.service.ts` | Staging, commit, discard, read for export, limits. | | 7 | `backend/src/modules/admin/challenge-files.service.ts` | Staging, commit, discard, read for export, limits. |
| 8 | `backend/src/modules/admin/dto/challenges.dto.ts` | Zod schemas for IDs, list query, create/update, staged-file manifest, import document with field paths. | | 8 | `backend/src/modules/admin/dto/challenges.dto.ts` | Zod schemas for IDs, list query, create/update, staged-file manifest, import document with field paths. |
@@ -136,4 +138,4 @@ The form modal has three sections (tabs): General, Connection, Files. Fields mar
* The flag is never present in any non-admin DTO. The player-facing challenges route and scoreboard remain unchanged; only admin endpoints can fetch the plaintext. * The flag is never present in any non-admin DTO. The player-facing challenges route and scoreboard remain unchanged; only admin endpoints can fetch the plaintext.
* All mutating endpoints require the standard CSRF cookie + header pair; the existing `csrfInterceptor` handles the headers automatically. * All mutating endpoints require the standard CSRF cookie + header pair; the existing `csrfInterceptor` handles the headers automatically.
* The list query enforces a deterministic `LOWER(name), name` order so the UI sort is reproducible. * The list query enforces a deterministic `LOWER(name), name` order so the UI sort is reproducible.
* Importing the same document twice is idempotent: every incoming challenge either replaces an existing row by lowercased name or inserts a new one. * Importing the same document twice is idempotent: every existing challenge (matching or not) is replaced by the archive contents.
+4 -1
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-22T21:25:29Z. they need. Last regenerated 2026-07-22T22:17:11Z.
# Architecture # Architecture
@@ -74,6 +74,9 @@ they need. Last regenerated 2026-07-22T21:25:29Z.
* [Admin — Challenges](/guides/admin-challenges.md) - How an admin lists, * [Admin — Challenges](/guides/admin-challenges.md) - How an admin lists,
searches, creates, edits, deletes, imports, and exports challenges at searches, creates, edits, deletes, imports, and exports challenges at
`/admin/challenges`, including file staging and overwrite confirm. `/admin/challenges`, including file staging and overwrite confirm.
* [Admin — Challenges Full-Replace Import](/guides/admin-challenges-import.md) -
Transactional full-replace semantics, side effects, and the auto-close
contract of the confirmed challenges import on `/admin/challenges`.
* [Authenticated Shell](/guides/authenticated-shell.md) - The header, LED * [Authenticated Shell](/guides/authenticated-shell.md) - The header, LED
+ countdown, quick-jump tabs, user menu, and SSE lifecycle that frame + countdown, quick-jump tabs, user menu, and SSE lifecycle that frame
every signed-in page. every signed-in page.