docs: update documentation to OKF v0.1 format
This commit is contained in:
@@ -3,7 +3,7 @@ type: architecture
|
||||
title: Backend Module Map
|
||||
description: NestJS modules, controllers, services, and how they are wired together.
|
||||
tags: [architecture, backend, nestjs, modules]
|
||||
timestamp: 2026-07-23T15:46:55Z
|
||||
timestamp: 2026-07-23T16:10:00Z
|
||||
---
|
||||
|
||||
# Module Map
|
||||
@@ -86,6 +86,11 @@ also registers two global providers:
|
||||
→ `DatabaseInitService.init()` → `FilesystemTransactionService.recoverTransactions()`
|
||||
(resolves interrupted restore manifests before TypeORM opens the live SQLite file;
|
||||
ambiguous generations abort startup) → stale unowned swap-artifact sweep → migrations + WAL
|
||||
→ `verifySeed()` (best-effort)
|
||||
→ `ensureSystemCategoryIcons()` (best-effort — writes
|
||||
`CRY/HW/MSC/PWN/REV/WEB.png` into `<UPLOAD_DIR>/icons/` if missing so
|
||||
freshly bootstrapped instances never serve 404 icons; see
|
||||
[System Category Icon Seed](/architecture/system-category-icons.md))
|
||||
→ `app.use(...)` middlewares (helmet, parsers, CSRF, static)
|
||||
→ `SwaggerModule.setup(...)` (OpenAPI 3.1)
|
||||
→ `SpaFallbackMiddleware` → `app.listen()`.
|
||||
@@ -94,6 +99,7 @@ ambiguous generations abort startup) → stale unowned swap-artifact sweep → m
|
||||
|
||||
- [System Overview](/architecture/overview.md)
|
||||
- [Key Files Index](/architecture/key-files.md)
|
||||
- [System Category Icon Seed](/architecture/system-category-icons.md)
|
||||
- [REST API Overview](/api/rest-overview.md)
|
||||
- [Blog API](/api/blog.md)
|
||||
- [Admin System API](/api/admin-system.md)
|
||||
|
||||
@@ -3,7 +3,7 @@ type: architecture
|
||||
title: Key Files Index
|
||||
description: One-line responsibility for important source and contract-test files, including event-window, challenge, scoreboard, session, notification, and blog flows.
|
||||
tags: [architecture, key-files, event-window, validation, default-challenge-ip, sse, bootstrap, migrations, challenges, import, board, notifications, per-user, session, blog]
|
||||
timestamp: 2026-07-23T15:46:55Z
|
||||
timestamp: 2026-07-23T16:10:00Z
|
||||
---
|
||||
|
||||
# Backend
|
||||
@@ -14,7 +14,8 @@ timestamp: 2026-07-23T15:46:55Z
|
||||
| `backend/src/app.module.ts` | Wires feature modules and global providers. |
|
||||
| `backend/src/config/env.schema.ts` | Zod-validated runtime config; canonical defaults for `DATABASE_PATH` (`./data/db.sqlite`) and `UPLOAD_DIR` (`./data/uploads`). |
|
||||
| `backend/src/database/database.module.ts` | Configures TypeORM with SQLite and imports the shared `FilesystemTransactionModule` used by pre-TypeORM startup recovery. |
|
||||
| `backend/src/database/database-init.service.ts` | Before opening SQLite, resolves durable swap manifests to a coherent old or verified-new database/uploads generation, fails startup closed on ambiguity, best-effort sweeps stale unowned swap artifacts, then runs migrations, enforces WAL, and verifies seeds. |
|
||||
| `backend/src/database/database-init.service.ts` | Before opening SQLite, resolves durable swap manifests to a coherent old or verified-new database/uploads generation, fails startup closed on ambiguity, best-effort sweeps stale unowned swap artifacts, then runs migrations, enforces WAL, verifies seeds, and best-effort seeds the canonical system-category icon PNGs into `UPLOAD_DIR/icons` so a fresh clone never serves 404 icons. |
|
||||
| `backend/src/database/system-category-icons.ts` | Exports `CANONICAL_SYSTEM_ICON_KEYS` (`['CRY','HW','MSC','PWN','REV','WEB']`), `SYSTEM_ICON_SIZE` (`128`), and `seedSystemCategoryIcons(uploadDir)`: creates `<uploadDir>/icons/` recursively and writes a deterministic sharp 128×128 PNG for every missing canonical key (palette-coloured background + white abbreviation overlay). Idempotent — existing non-empty files are reported as `skipped` so admin uploads are preserved across restarts. |
|
||||
| `backend/src/modules/admin/system/filesystem-transaction.module.ts` | Exports one shared `FilesystemTransactionService` provider to `DatabaseModule` and `AdminSystemModule`, avoiding a circular dependency. |
|
||||
| `backend/src/database/migrations/1700000000400-RepairCategorySchemaAndSystemCategories.ts` | Forward-only repair migration: adds `created_at`/`updated_at` to legacy six-column `category` tables, deletes obsolete `FOR`/`OSI` system rows, rewrites canonical row metadata, inserts any missing canonical key, and re-asserts unique indexes. Exports `CANONICAL_SYSTEM_CATEGORIES`. |
|
||||
| `backend/src/common/guards/jwt-auth.guard.ts` | Global JWT authorization guard. |
|
||||
@@ -145,6 +146,7 @@ timestamp: 2026-07-23T15:46:55Z
|
||||
| `tests/backend/admin-system-restore-validation.spec.ts` | Archive schema validation, base64 size mismatch, sha256 mismatch, safe-path rejection, and duplicate-path rejection. |
|
||||
| `tests/backend/admin-system-restore-commit.spec.ts` | Controller forwarding, real-file restore, trigger preservation, admin-less archive rejection, and kill-during-swap regression proving restart recovers one coherent database/uploads generation. |
|
||||
| `tests/backend/database-init-recovery.spec.ts` | Startup recovery contracts: restores old generations from interrupted manifests, fails closed without mutation on hybrid state, and removes stale unowned artifacts. |
|
||||
| `tests/backend/system-category-icons.spec.ts` | Pure helper contract for `seedSystemCategoryIcons`: writes six valid 128×128 PNGs in a fresh temp dir; second invocation reports all six as `skipped` and leaves an admin-uploaded `CRY.png` `mtimeMs` unchanged; creates the `icons/` subdirectory when the parent is empty. Real `sharp` + `os.tmpdir()`, no mocks. |
|
||||
| `tests/backend/filesystem-transaction.spec.ts` | Durable manifest phase, commit/rollback, old/new generation recovery, ambiguous fail-closed, and stale artifact sweep contracts. |
|
||||
| `frontend/src/app/features/admin/system/system.component.ts` | `/admin/system` smart page: two panels (Database + Danger Zone), backup download, restore pick + validate, confirm modal trigger, and per-operation success/error surfacing + cross-store invalidation. |
|
||||
| `frontend/src/app/features/admin/system/system-confirm-modal.component.ts` | Re-authentication + confirmation-phrase modal with operation-specific copy from `system.pure.ts`. |
|
||||
@@ -162,6 +164,7 @@ timestamp: 2026-07-23T15:46:55Z
|
||||
|
||||
- [Frontend Structure](/architecture/frontend-structure.md)
|
||||
- [Authenticated Shell](/guides/authenticated-shell.md)
|
||||
- [System Category Icon Seed](/architecture/system-category-icons.md)
|
||||
- [System Endpoints](/api/system.md)
|
||||
- [Challenges Endpoints](/api/challenges.md)
|
||||
- [Scoreboard Page Guide](/guides/scoreboard-page.md)
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
---
|
||||
type: architecture
|
||||
title: System Category Icon Seed
|
||||
description: Idempotent startup hook that writes deterministic 128×128 PNG icons for the canonical six system categories (CRY/HW/MSC/PWN/REV/WEB) into UPLOAD_DIR/icons, so freshly bootstrapped instances never serve 404 icons.
|
||||
tags: [architecture, startup, categories, icons, seed, sharp, migrations, tester]
|
||||
timestamp: 2026-07-23T16:10:00Z
|
||||
---
|
||||
|
||||
# Purpose
|
||||
|
||||
The canonical six system categories are seeded by the
|
||||
`UpdateSystemCategoryKeys1700000000300` and
|
||||
`RepairCategorySchemaAndSystemCategories1700000000400` migrations, which
|
||||
insert database rows whose `icon_path` column references
|
||||
`/uploads/icons/{CRY,HW,MSC,PWN,REV,WEB}.png`. The migrations create the
|
||||
DB rows but never write the PNG assets to disk, and `setup.sh` only
|
||||
`mkdir -p`s the upload directory — it does not populate it.
|
||||
|
||||
As a result, `GET /uploads/icons/CRY.png` returns `404` on a freshly
|
||||
cloned instance, and the challenges board and admin categories page
|
||||
show broken images for every system row until an admin re-uploads each
|
||||
icon.
|
||||
|
||||
This module closes that gap by generating a deterministic 128×128 PNG
|
||||
for each canonical key on every backend startup, idempotently. Admin
|
||||
uploads written through `UploadsController` are never overwritten.
|
||||
|
||||
# Files
|
||||
|
||||
| File | Responsibility |
|
||||
|---|---|
|
||||
| `backend/src/database/system-category-icons.ts` | Exports `CANONICAL_SYSTEM_ICON_KEYS` (`['CRY','HW','MSC','PWN','REV','WEB']`), `SYSTEM_ICON_SIZE` (`128`), and the async `seedSystemCategoryIcons(uploadDir)` helper. Creates `<uploadDir>/icons/` recursively, generates a sharp 128×128 PNG (palette-coloured background + white 2–6-letter abbreviation overlay) for every key whose `<KEY>.png` does not yet exist or is zero-bytes, and returns `{ written, skipped }`. |
|
||||
| `backend/src/database/database-init.service.ts` | Calls `seedSystemCategoryIcons(path.resolve(config.get('UPLOAD_DIR', './data/uploads')))` from `init()` after migrations and `verifySeed()`. Skips when `UPLOAD_DIR` is unset or contains `:memory:`. Failures log a `WARN` and never abort startup. |
|
||||
| `tests/backend/system-category-icons.spec.ts` | Pure helper contract: writes six 128×128 PNGs in a fresh temp dir; second run reports all six as `skipped` and leaves `mtimeMs` of an existing admin-uploaded icon unchanged; creates the `icons/` subdirectory when the parent is empty. |
|
||||
|
||||
# How the helper works
|
||||
|
||||
`seedSystemCategoryIcons(uploadDir)`:
|
||||
|
||||
1. Resolves `iconsDir = path.join(uploadDir, 'icons')` and creates it
|
||||
recursively when missing — covers the fresh-clone case where the
|
||||
directory does not yet exist.
|
||||
2. For each `key` in `CANONICAL_SYSTEM_ICON_KEYS`:
|
||||
* `target = path.join(iconsDir, `${key}.png`)`.
|
||||
* If the file exists **and** its size is `> 0`, push `target` onto
|
||||
`skipped` and continue. This is the contract that protects
|
||||
admin-uploaded icons: a later restart must never overwrite them.
|
||||
* Otherwise build a sharp PNG with:
|
||||
* 128×128 RGBA buffer,
|
||||
* background colour from `PALETTE[hash(key) % PALETTE.length]`
|
||||
(8-entry palette, deterministic hash so every icon is visually
|
||||
distinct but reproducible across machines/containers),
|
||||
* white abbreviation overlay drawn from
|
||||
`renderLabel(key)` (one white pixel per letter, centred).
|
||||
* Write the buffer with `fs.writeFileSync(target, buf)` and push
|
||||
`target` onto `written`.
|
||||
3. Return `{ written, skipped }`.
|
||||
|
||||
The palette and overlay are intentionally simple — these are
|
||||
fallback/placeholder icons. An admin who wants branded artwork uploads
|
||||
a real image through `POST /api/v1/uploads/category-icon`, which writes
|
||||
to the same `<uploadDir>/icons/<KEY>.png` path and is then preserved on
|
||||
subsequent restarts.
|
||||
|
||||
# Startup wiring
|
||||
|
||||
Inside `DatabaseInitService.init()` the new step runs after the
|
||||
existing `runMigrations` block and `verifySeed()`:
|
||||
|
||||
```
|
||||
verifySeed() // best-effort WARN on failure
|
||||
ensureSystemCategoryIcons() // best-effort WARN on failure
|
||||
```
|
||||
|
||||
`ensureSystemCategoryIcons()` short-circuits with a `debug` log when
|
||||
`UPLOAD_DIR` is unset or contains `:memory:` (matches how the module
|
||||
already guards in-memory test databases). Otherwise it logs:
|
||||
|
||||
```
|
||||
System category icons: written=<n> skipped=<n>
|
||||
```
|
||||
|
||||
A failure logs `ensureSystemCategoryIcons skipped: <message>` as a
|
||||
`WARN` and **does not abort startup** — mirroring the
|
||||
`verifySeed` "best-effort" pattern. Missing icons are recoverable (they
|
||||
only affect image rendering on a few pages) and never block HTTP
|
||||
traffic.
|
||||
|
||||
# Source-of-truth coupling
|
||||
|
||||
`CANONICAL_SYSTEM_ICON_KEYS` is duplicated from
|
||||
`CANONICAL_SYSTEM_CATEGORIES` in
|
||||
`backend/src/database/migrations/1700000000400-RepairCategorySchemaAndSystemCategories.ts:13`
|
||||
to avoid a migration → runtime import cycle (the migration is
|
||||
registered after `database.module.ts` is built, and re-importing from
|
||||
the migration would be circular). The duplication is **intentional**;
|
||||
the helper file carries a top-of-file comment pointing at the
|
||||
canonical migration as the source of truth, and the helper, the
|
||||
migration, and the hard-coded `iconPath` URLs in
|
||||
`1700000000300-UpdateSystemCategoryKeys.ts` and
|
||||
`1700000000400-RepairCategorySchemaAndSystemCategories.ts` must be kept
|
||||
in sync whenever a system category is added or renamed.
|
||||
|
||||
# What to look at next
|
||||
|
||||
- The `UPLOAD_DIR` value is read from `ConfigService` and defaults to
|
||||
`./data/uploads` (see
|
||||
[`env.schema.ts`](/architecture/key-files.md) for the canonical
|
||||
defaults).
|
||||
- The icons directory is served by `app.use('/uploads', express.static(uploadDir))`
|
||||
in `backend/src/main.ts:79`, so the on-disk files are the same
|
||||
bytes returned by `GET /uploads/icons/<KEY>.png` to the SPA.
|
||||
- Admin overrides go through
|
||||
[`POST /api/v1/uploads/category-icon`](/api/uploads.md), which
|
||||
normalizes uploaded images to the same 128×128 PNG size and writes
|
||||
to the same `UPLOAD_DIR/icons/<KEY>.png` path.
|
||||
- The challenges board and admin categories page consume
|
||||
`<img src="/uploads/icons/<KEY>.png">` from each `category.icon_path`
|
||||
column — see [Challenges Board](/guides/challenges-board.md) and
|
||||
[Admin — Categories](/guides/admin-categories.md).
|
||||
|
||||
# Tests
|
||||
|
||||
| File | What it asserts |
|
||||
|---|---|
|
||||
| `tests/backend/system-category-icons.spec.ts` | Real `sharp` + `os.tmpdir()`: writes six PNGs of `format === 'png'` and `width === height === 128` on a fresh dir; second invocation reports all six as `skipped` and does not change `mtimeMs` of an existing `CRY.png` (admin-upload contract); creates the `icons/` subdirectory when the parent is empty. No mocks. |
|
||||
|
||||
# See also
|
||||
|
||||
- [Category Repair Migration](/database/category-repair-migration.md) — canonical six system categories, DB-side.
|
||||
- [Uploads Endpoints](/api/uploads.md) — admin override path for the same on-disk files.
|
||||
- [Challenges Board](/guides/challenges-board.md) — tester-visible consumption of the icons.
|
||||
- [Admin — Categories](/guides/admin-categories.md) — admin override UI.
|
||||
- [Backend Module Map](/architecture/backend-modules.md) — startup chain.
|
||||
- [Key Files Index](/architecture/key-files.md) — file-by-file responsibilities.
|
||||
Reference in New Issue
Block a user