docs: update documentation to OKF v0.1 format
This commit is contained in:
@@ -85,6 +85,11 @@ column are sorted by `difficulty` (`LOW < MEDIUM < HIGH`) then by name.
|
|||||||
Only `enabled = 1` rows are returned. The query joins
|
Only `enabled = 1` rows are returned. The query joins
|
||||||
`challenge` and `category` once and groups in memory.
|
`challenge` and `category` once and groups in memory.
|
||||||
|
|
||||||
|
On a fresh database the `SeedSampleChallenges1700000000600`
|
||||||
|
migration populates the board with a representative sample set
|
||||||
|
spanning every canonical system category so the page renders cards
|
||||||
|
without any manual admin action.
|
||||||
|
|
||||||
# `GET /api/v1/challenges/status`
|
# `GET /api/v1/challenges/status`
|
||||||
|
|
||||||
Authenticated event-window snapshot (the same `EventStatePayload`
|
Authenticated event-window snapshot (the same `EventStatePayload`
|
||||||
|
|||||||
@@ -56,6 +56,14 @@ canonical enums, the `enabled` flag, `updated_at`, the
|
|||||||
category FK stays at `ON DELETE RESTRICT` so the
|
category FK stays at `ON DELETE RESTRICT` so the
|
||||||
`CATEGORY_HAS_CHALLENGES` business rule is preserved.
|
`CATEGORY_HAS_CHALLENGES` business rule is preserved.
|
||||||
|
|
||||||
|
The `SeedSampleChallenges1700000000600` migration inserts a small
|
||||||
|
representative set of sample challenges (one or more per canonical
|
||||||
|
system category) on a fresh database so the `/challenges` board has
|
||||||
|
cards immediately after bootstrap. It is idempotent: if the
|
||||||
|
`challenge` table already contains rows (e.g. an admin imported a
|
||||||
|
custom challenge set), it does nothing. Sample flags follow the
|
||||||
|
`flag{<token>}` convention and are exposed only inside admin views.
|
||||||
|
|
||||||
## `challenge_file`
|
## `challenge_file`
|
||||||
|
|
||||||
| Column | Type | Description |
|
| Column | Type | Description |
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
---
|
||||||
|
type: database
|
||||||
|
title: Seed Sample Challenges Migration (1700000000600)
|
||||||
|
description: Forward-only migration that inserts a representative sample set of challenges on a fresh database so the /challenges board renders cards without manual admin action.
|
||||||
|
tags: [database, migration, challenge, seed, sample]
|
||||||
|
timestamp: 2026-07-23T02:23:34Z
|
||||||
|
---
|
||||||
|
|
||||||
|
# Purpose
|
||||||
|
|
||||||
|
A brand-new HIPCTF database (default path
|
||||||
|
[`./data/db.sqlite`](/database/schema.md)) has no `challenge` rows until
|
||||||
|
an admin imports or creates them. The
|
||||||
|
`SeedSampleChallenges1700000000600` migration bridges that gap by
|
||||||
|
inserting a small, representative sample set so the authenticated
|
||||||
|
`/challenges` board (see [Challenges Board](/guides/challenges-board.md))
|
||||||
|
already has cards on the very first visit and the public landing can
|
||||||
|
demonstrate the platform with real content.
|
||||||
|
|
||||||
|
The migration runs at the end of the `MIGRATIONS` array in
|
||||||
|
[`DatabaseModule`](/architecture/backend-modules.md) so every other
|
||||||
|
category / challenge schema migration has already landed and the
|
||||||
|
`category` rows it joins against are present.
|
||||||
|
|
||||||
|
# File
|
||||||
|
|
||||||
|
`backend/src/database/migrations/1700000000600-SeedSampleChallenges.ts`
|
||||||
|
|
||||||
|
Registered last in `backend/src/database/database.module.ts` so it
|
||||||
|
runs after `UpgradeChallengeAdminSchema1700000000500`.
|
||||||
|
|
||||||
|
# Sample set
|
||||||
|
|
||||||
|
Eight challenges, one or more per canonical system category. Source
|
||||||
|
of truth is the typed `SAMPLE_CHALLENGES` array exported by the
|
||||||
|
migration:
|
||||||
|
|
||||||
|
| Name | `system_key` | Difficulty | `initial_points` | `minimum_points` | `decay_solves` | Flag |
|
||||||
|
|-------------------|--------------|------------|------------------|------------------|----------------|--------------|
|
||||||
|
| Alpha Cipher | `CRY` | `LOW` | 200 | 100 | 5 | `flag{alpha}` |
|
||||||
|
| Beta Cipher | `CRY` | `MEDIUM` | 300 | 100 | 10 | `flag{beta}` |
|
||||||
|
| Zeta Key | `CRY` | `HIGH` | 500 | 100 | 10 | `flag{zeta}` |
|
||||||
|
| Web Welcome | `WEB` | `LOW` | 100 | 50 | 10 | `flag{web1}` |
|
||||||
|
| Mobile Mayhem | `PWN` | `MEDIUM` | 300 | 100 | 10 | `flag{mob1}` |
|
||||||
|
| Hardware Hello | `HW` | `LOW` | 100 | 50 | 10 | `flag{hw1}` |
|
||||||
|
| Markdown Mystery | `MSC` | `LOW` | 100 | 50 | 10 | `flag{md}` |
|
||||||
|
| Reverse Ranger | `REV` | `MEDIUM` | 300 | 100 | 10 | `flag{rev1}` |
|
||||||
|
|
||||||
|
All rows are inserted with `protocol='WEB'`, `port=NULL`,
|
||||||
|
`ip_address=''`, and `enabled=1` so they appear on the board the
|
||||||
|
moment the event window opens (see [Event Window](/guides/event-window.md)).
|
||||||
|
Categories are resolved by `system_key` rather than UUID so the seed
|
||||||
|
works regardless of the UUIDs the category migration assigned at
|
||||||
|
install time. Sample flags follow the `flag{<token>}` convention and
|
||||||
|
are exposed only inside admin views — the public challenge DTO
|
||||||
|
continues to strip them (see [Challenges Endpoints](/api/challenges.md)).
|
||||||
|
|
||||||
|
# What the `up()` does
|
||||||
|
|
||||||
|
1. **Idempotency guard** — `SELECT COUNT(*) FROM "challenge"`. If the
|
||||||
|
table already contains rows the migration returns immediately; an
|
||||||
|
admin that imported a custom challenge set first is never
|
||||||
|
overwritten.
|
||||||
|
2. **System-category lookup** —
|
||||||
|
`SELECT id, system_key FROM "category" WHERE system_key IS NOT NULL`
|
||||||
|
and builds an in-memory `Map<system_key, id>`. If fewer than six
|
||||||
|
canonical keys are present (the seed cannot bind) the migration
|
||||||
|
aborts as a no-op rather than inserting challenges into the wrong
|
||||||
|
category.
|
||||||
|
3. **Insert sample rows** — for each entry in `SAMPLE_CHALLENGES`, an
|
||||||
|
`INSERT INTO "challenge"` with a fresh UUID v4, ISO 8601
|
||||||
|
`created_at` / `updated_at` via `strftime('%Y-%m-%dT%H:%M:%fZ','now')`,
|
||||||
|
and `enabled=1`. Categories without a matching `system_key` are
|
||||||
|
silently skipped (defensive — only happens if the canonical six
|
||||||
|
are missing).
|
||||||
|
|
||||||
|
# Idempotency
|
||||||
|
|
||||||
|
* Running the migration twice (or restarting after it has been
|
||||||
|
recorded) leaves the same eight rows. The early-return count
|
||||||
|
guard skips the second run.
|
||||||
|
* Re-running the migration after a custom `challenge` row exists also
|
||||||
|
no-ops, so manually-imported content from the
|
||||||
|
[Admin — Challenges Full-Replace Import](/guides/admin-challenges-import.md)
|
||||||
|
flow is preserved.
|
||||||
|
|
||||||
|
# `down()`
|
||||||
|
|
||||||
|
Scoped to the seeded set: `DELETE FROM "challenge" WHERE "name" IN (...)`
|
||||||
|
using the eight fixed names from `SAMPLE_CHALLENGES`. It deliberately
|
||||||
|
matches **only** the sample names so unrelated admin rows are not
|
||||||
|
removed.
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
|
||||||
|
| File | What it asserts |
|
||||||
|
|---|---|
|
||||||
|
| `tests/backend/migrations.spec.ts` — *"seeds sample challenges on a fresh database (Job 906)"* | After all migrations run on an in-memory SQLite DB, the eight expected sample names are present. |
|
||||||
|
| `tests/backend/migrations.spec.ts` — *"seeded sample challenges are enabled, schema-compatible, and point at the right categories (Job 906)"* | Every seeded row has `enabled=true`, a valid `difficulty`, `initial_points >= minimum_points`, a non-empty `flag`, and a `category_id` that resolves to one of the six canonical system categories. |
|
||||||
|
| `tests/backend/migrations.spec.ts` — *"SeedSampleChallenges down() scope (Job 906)"* | `down()` removes only rows whose name is in the seed list and leaves manually-inserted rows of similar names (e.g. `Other`) intact. |
|
||||||
|
| `tests/backend/challenges-board.spec.ts` | Board-listing test now allows extra seeded cards inside the CRY column (`Alpha Cipher`, `Beta Cipher`, `Zeta Key`) while still asserting that the hand-inserted `alphacipher` LOW and `Zeta-key` HIGH rows occupy the bookended slots and that the disabled `hidden` row is excluded. |
|
||||||
|
|
||||||
|
# Operational notes
|
||||||
|
|
||||||
|
* On a fresh database the seed fires before the first app boot, so
|
||||||
|
[`GET /api/v1/challenges/board`](/api/challenges.md) returns the
|
||||||
|
sample columns immediately and the player-facing [Challenges Board](/guides/challenges-board.md)
|
||||||
|
shows eight cards. An admin deleting every challenge is the only
|
||||||
|
way to reach an empty board.
|
||||||
|
* To confirm on the command line after `npm run setup`:
|
||||||
|
```
|
||||||
|
sqlite3 ./data/db.sqlite 'SELECT COUNT(*) FROM challenge;'
|
||||||
|
```
|
||||||
|
Expected count: `8`. Re-running setup on a populated DB leaves the
|
||||||
|
count unchanged (idempotent guard).
|
||||||
|
* Played flags are usable for end-to-end demos: e.g. submitting
|
||||||
|
`flag{alpha}` against the Alpha Cipher card immediately awards
|
||||||
|
points and broadcasts the solve on the
|
||||||
|
[Scoreboard Stream](/guides/scoreboard-stream.md).
|
||||||
|
|
||||||
|
# See also
|
||||||
|
|
||||||
|
* [Database Schema Overview](/database/schema.md) — full migration list
|
||||||
|
and bootstrap behavior.
|
||||||
|
* [Challenge Tables](/database/challenges.md) — `challenge` schema,
|
||||||
|
indexes, and provenance.
|
||||||
|
* [Challenges Endpoints](/api/challenges.md) — board query and flag
|
||||||
|
submission.
|
||||||
|
* [Challenges Board](/guides/challenges-board.md) — user-facing guide.
|
||||||
|
* [Admin — Challenges](/guides/admin-challenges.md) — how an admin
|
||||||
|
replaces or extends the sample set.
|
||||||
|
* [Category Repair Migration](/database/category-repair-migration.md)
|
||||||
|
— earlier canonical-six migration this seed depends on.
|
||||||
@@ -10,7 +10,14 @@ timestamp: 2026-07-23T00:10:00Z
|
|||||||
|
|
||||||
`/challenges` is rendered by `ChallengesPage`
|
`/challenges` is rendered by `ChallengesPage`
|
||||||
(`frontend/src/app/features/challenges/challenges.page.ts`) inside the
|
(`frontend/src/app/features/challenges/challenges.page.ts`) inside the
|
||||||
authenticated shell. The page is gated by:
|
authenticated shell.
|
||||||
|
|
||||||
|
On a fresh database the `SeedSampleChallenges1700000000600`
|
||||||
|
migration loads a representative sample set of challenges so the
|
||||||
|
board has cards the moment an admin configures the event window; an
|
||||||
|
empty board is only possible when the admin deleted every challenge.
|
||||||
|
|
||||||
|
The page is gated by:
|
||||||
|
|
||||||
| Gate | Where |
|
| Gate | Where |
|
||||||
|---------------------------------------|--------------------------------------------------|
|
|---------------------------------------|--------------------------------------------------|
|
||||||
|
|||||||
+5
-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-23T00:10:00Z.
|
they need. Last regenerated 2026-07-23T02:23:34Z.
|
||||||
|
|
||||||
# Architecture
|
# Architecture
|
||||||
|
|
||||||
@@ -35,6 +35,10 @@ they need. Last regenerated 2026-07-23T00:10:00Z.
|
|||||||
Forward-only migration that adds `created_at`/`updated_at` to legacy
|
Forward-only migration that adds `created_at`/`updated_at` to legacy
|
||||||
`category` tables and reconciles system rows to the canonical
|
`category` tables and reconciles system rows to the canonical
|
||||||
CRY/HW/MSC/PWN/REV/WEB set.
|
CRY/HW/MSC/PWN/REV/WEB set.
|
||||||
|
* [Seed Sample Challenges Migration](/database/seed-sample-challenges-migration.md) -
|
||||||
|
Forward-only migration that inserts a representative sample set of
|
||||||
|
challenges on a fresh database so the `/challenges` board has cards
|
||||||
|
immediately, with idempotency guards and scoped `down()`.
|
||||||
* [Auth and Settings Tables](/database/auth-settings.md) - `refresh_token`
|
* [Auth and Settings Tables](/database/auth-settings.md) - `refresh_token`
|
||||||
and `setting` tables.
|
and `setting` tables.
|
||||||
* [Blog Post Table](/database/blog-posts.md) - `blog_post` table.
|
* [Blog Post Table](/database/blog-posts.md) - `blog_post` table.
|
||||||
|
|||||||
Reference in New Issue
Block a user