Files
HIPCTF2/docs/guides/admin-system-page.md
T

187 lines
9.3 KiB
Markdown

---
type: guide
title: Admin — System (Backup, Restore, Danger Zone)
description: How an admin navigates to /admin/system, creates a backup, stages and commits a restore, resets all scores, and wipes challenges — including the re-authentication confirmation, progress, and side effects.
tags: [guide, admin, system, backup, restore, danger-zone, confirmation]
timestamp: 2026-07-23T15:46:55Z
---
# Navigation
1. Sign in as an admin and open the admin side-nav (see the
[Admin Shell guide](/guides/admin-shell.md)).
2. Click **System** in the side-nav. The route resolves to
`/admin/system` and lazily loads the
`AdminSystemComponent` standalone component.
3. The page renders two panels side by side: **Database** (left) and
**Danger Zone** (right). On narrow screens they stack vertically.
# Database panel
## Create a backup
1. Click **Create backup** (data-testid `admin-system-create-backup`).
A spinner appears inside the button while the request is in flight.
2. The browser receives a JSON file via `Content-Disposition:
attachment; filename="hipctf-backup-YYYY-MM-DD.json"` and the
browser download UI saves it to disk.
3. On success the panel shows a green **Backup ready.** status line
(data-testid `admin-system-backup-success`).
4. On failure the panel shows a red error message under the buttons
(data-testid `admin-system-backup-error`). The friendly message
comes from `system.pure.ts` and resolves `SYSTEM_BACKUP_FAILED`,
`SYSTEM_FILE_READ_FAILED`, `UNAUTHORIZED`, `FORBIDDEN`, etc.
## Restore a backup
1. Click **Restore backup** (data-testid `admin-system-restore-backup`).
A hidden file picker opens restricted to `application/json`/`.json`.
2. Pick a backup JSON file. The progress line shows
**Reading selected backup…**, then **Validating backup
server-side…** (data-testid `admin-system-restore-progress`).
3. The server validates the JSON, decodes uploads, and verifies
sizes/checksums. On success a summary card appears
(data-testid `admin-system-restore-summary`) showing:
* Tables detected
* Uploaded files
* Total bytes
* Stage expiry (ISO timestamp)
4. From the summary card, the admin has two options:
* **Discard staged backup** (data-testid
`admin-system-discard-restore`) — drops the staged state and
resumes from a clean panel.
* **Continue to destructive restore…** (data-testid
`admin-system-confirm-restore`) — opens the confirmation modal.
### Confirmation modal (restore-backup)
* The modal title is **Restore database?**.
* The detail paragraph explains that the entire database and uploads
will be replaced (the modal's `preserved` line says **Nothing is
preserved**).
* The stage banner explicitly warns: *You will be logged out after
restore completes.*
* The admin must enter their current password and type
`RESTORE BACKUP` exactly into the confirmation phrase field.
* Click **Confirm**. The modal swaps the input fields for a
*Working…* spinner.
* The frontend calls `POST /confirmations` with `{ operation:
'restore-backup', password, stagingId }` to receive a single-use
token, then immediately calls `POST /restore/commit` with that
token and the staging id.
* On success the SPA calls `AuthService.forceServerInvalidation()`,
resets the `UserStore`, and navigates to `/login`. The
`ChallengesStore` and `ScoreboardStore` invalidate themselves via
the `SystemDataChangeService` `'restore-completed'` event.
* On failure the modal stays open and shows the resolved error
(e.g. `SYSTEM_TOKEN_MISMATCH`, `SYSTEM_RESTORE_ROLLED_BACK`). The
database panel also reflects the error inline.
# Danger Zone panel
The danger zone panel is outlined by a dashed red border and contains
two red buttons:
| Button | Operation |
|--------------------------------------------|---------------------|
| `admin-system-reset-scores` | `reset-scores` |
| `admin-system-wipe-challenges` | `wipe-challenges` |
Each button opens the same confirmation modal with operation-specific
copy:
* **Reset all scores?** — detail lists what is removed
(every solve and award), preserved (users, sessions, roles,
challenges, categories, etc.). Confirmation phrase: `RESET SCORES`.
* **Wipe all challenges?** — detail explains that every challenge, its
attached files, every solve, and every award are removed, and that
every uploaded challenge file on disk is deleted. Preserved:
users, sessions, roles, categories, settings, blog posts, and
root-level uploads (e.g. the site logo). Confirmation phrase:
`WIPE CHALLENGES`.
After the admin enters their password and types the confirmation
phrase, the SPA re-authenticates to issue a token and then commits the
operation. The panel shows a green summary
(`N solve record(s) removed.` or `N challenge(s), M file(s) wiped.`)
and a toast notification. The corresponding stores
(`ChallengesStore`, `ScoreboardStore`) automatically reset themselves
via the `SystemDataChangeService` `scores-reset` and `challenges-wiped`
events so every open tab sees fresh data.
# Cross-tab invalidation
After any of the destructive operations, the
`SystemDataChangeService` notifies all subscribers in the same tab via
an Angular signal. The injected effects in `ChallengesStore` and
`ScoreboardStore` reset their cached board/matrix/event-log/graph
data. For a restore, the SPA additionally forces a cross-tab
invalidation by calling `AuthService.forceServerInvalidation()`,
which clears the access token and notifies peer tabs via
`BroadcastChannel` + the `storage`-event fallback so they redirect to
`/login` as well.
# Error mapping
The pure helper `friendlySystemErrorMessage(op, err)` in
`system.pure.ts` converts the backend error codes into user-facing
messages. Notable mappings:
| Backend code | UI message |
|---------------------------------------|----------------------------------------------------------------------------|
| `SYSTEM_REAUTH_REQUIRED` | "Your session is no longer valid. Please log in again." |
| `SYSTEM_INVALID_CREDENTIALS` | "The current password is incorrect." |
| `SYSTEM_TOKEN_INVALID` / `_EXPIRED` | "Confirmation token is invalid / expired. Re-authenticate and try again." |
| `SYSTEM_TOKEN_REUSED` | "Confirmation token was already used. Re-authenticate to get a fresh token." |
| `SYSTEM_TOKEN_MISMATCH` | "Confirmation token does not match this operation." |
| `SYSTEM_RESTORE_VALIDATION_FAILED` | "Backup validation failed. No data was changed." |
| `SYSTEM_RESTORE_PAYLOAD_TOO_LARGE` | "The backup is larger than the configured upload limit." |
| `SYSTEM_RESTORE_STAGE_EXPIRED` | "The backup staging window expired. Re-upload and validate again." |
| `SYSTEM_RESTORE_ROLLED_BACK` | "Restore failed and was rolled back. Your data is unchanged." |
| `SYSTEM_DANGER_ROLLED_BACK` | "Operation failed and was rolled back. Nothing was changed." |
| `SYSTEM_BACKUP_FAILED` | "Backup could not be generated. No data was changed." |
| `SYSTEM_DANGER_FAILED` | "Operation could not complete. No data was changed." |
| `SYSTEM_OPERATION_IN_PROGRESS` | "Another destructive operation is currently running." |
# Examples
## Manual restore happy path
1. Download the backup from a known-good environment.
2. Sign in as the admin on the target environment.
3. Navigate to `/admin/system`, click **Restore backup**, select the
downloaded file.
4. Verify the summary matches the expected table counts and file
counts.
5. Click **Continue to destructive restore…**, enter the admin
password, type `RESTORE BACKUP`, click **Confirm**.
6. The modal closes, the page shows a brief *Restore complete.
Logging out…* toast, and the SPA navigates to `/login`.
7. If the server process is terminated during the database/uploads swap,
restart the server. Before accepting requests, startup recovery uses the
durable restore manifest to restore the complete old generation or finalize
the complete verified new generation; it never intentionally boots with a
mixed database/uploads generation. An ambiguous incomplete generation fails
startup closed and preserves the manifest and artifacts for diagnosis.
8. Sign in again with the same admin credentials. The data on the
target environment now matches the source backup.
## Manual reset happy path
1. Navigate to `/admin/system`.
2. Click **Reset all scores**, enter the password, type
`RESET SCORES`, click **Confirm**.
3. The danger zone panel shows the number of solve records removed
and a toast notifies the operation completed. Refresh the
Scoreboard page — every player is back at zero.
## Manual wipe happy path
1. Navigate to `/admin/system`.
2. Click **Wipe challenges**, enter the password, type
`WIPE CHALLENGES`, click **Confirm**.
3. The danger zone panel shows counts of challenges, files, and
solves removed. The Challenges page becomes empty and the
`/admin/challenges` list is empty. The `challenges/` folder on
disk is also gone.