AI Implementation feature(882): Admin Area General Settings and Categories 1.00 (#22)

This commit was merged in pull request #22.
This commit is contained in:
2026-07-22 12:05:16 +00:00
parent c9e8dfc611
commit fac3179427
9 changed files with 578 additions and 189 deletions
+115 -17
View File
@@ -1,19 +1,120 @@
---
type: api
title: Admin Endpoints
description: User management endpoints mounted under /api/v1/admin (admin role required).
tags: [api, admin, users]
timestamp: 2026-07-21T18:28:00Z
description: Admin-only endpoints for user management, general settings, categories, and supporting uploads.
tags: [api, admin, users, general, categories]
timestamp: 2026-07-22T12:00:00Z
---
# Endpoints
| Method | Path | Auth | Source |
|---------|-----------------------|---------|-----------------------------------------------------|
| `GET` | `/api/v1/admin/users` | Admin | `backend/src/modules/admin/admin.controller.ts` |
| `POST` | `/api/v1/admin/users` | Admin | Same. |
| `PATCH` | `/api/v1/admin/users/:id` | Admin | Same. |
| `DELETE`| `/api/v1/admin/users/:id` | Admin | Same. |
All handlers below are mounted behind `JwtAuthGuard` (global) +
`AdminGuard` + `@Roles('admin')`, so they require a valid admin JWT.
CSRF is enforced on every mutating call (standard cookie + header
pattern; nothing is added to the skip list).
## User management — `/api/v1/admin`
| Method | Path | Source |
|----------|-------------------------------|---------------------------------------------------|
| `GET` | `/api/v1/admin/users` | `backend/src/modules/admin/admin.controller.ts` |
| `POST` | `/api/v1/admin/users` | Same. |
| `PATCH` | `/api/v1/admin/users/:id` | Same. |
| `DELETE` | `/api/v1/admin/users/:id` | Same. |
`AdminService` enforces the **last-admin invariant** (`LAST_ADMIN` /
`409`) — the final admin row cannot be demoted or deleted. `POST`
creates a new user; `PATCH .../:id` changes role / status; `DELETE .../:id`
removes the row.
## General settings — `/api/v1/admin/general`
| Method | Path | Source |
|--------|---------------------------------------|-------------------------------------------------------------|
| `GET` | `/api/v1/admin/general/settings` | `backend/src/modules/admin/admin-general.controller.ts` |
| `PUT` | `/api/v1/admin/general/settings` | Same. |
| `GET` | `/api/v1/admin/general/themes` | Same. |
`GET /settings` returns the full `GeneralSettingsView`
(`pageTitle`, `logo`, `welcomeMarkdown`, `themeKey`, `eventStartUtc`,
`eventEndUtc`, `defaultChallengeIp`, `registrationsEnabled`).
`PUT /settings` is validated by `GeneralSettingsSchema` (zod):
* `pageTitle` 1120 chars
* `logo` up to 2048 chars (a public URL — uploaded separately)
* `welcomeMarkdown` up to 64 000 chars
* `themeKey` is one of `THEME_IDS`
* `eventStartUtc` / `eventEndUtc` parseable dates; `superRefine`
enforces `eventEndUtc > eventStartUtc` and reports the issue on
`eventEndUtc`
* `defaultChallengeIp` 1255 chars
* `registrationsEnabled` boolean
On success the handler persists every field via `SettingsService`,
emits an SSE `general` event (`{ topic: 'general', themeKey }`) via
`SseHubService`, and returns the updated view.
`GET /themes` returns the `ThemeView[]` for which a corresponding
JSON file exists under `THEMES_DIR`. Each item is `{ id, key, name }`.
See [Admin — General Settings](/guides/admin-general-settings.md).
## Categories — `/api/v1/admin/categories`
| Method | Path | Source |
|----------|-------------------------------------|-----------------------------------------------------------------|
| `GET` | `/api/v1/admin/categories` | `backend/src/modules/admin/admin-categories.controller.ts` |
| `POST` | `/api/v1/admin/categories` | Same. |
| `PUT` | `/api/v1/admin/categories/:id` | Same. |
| `DELETE` | `/api/v1/admin/categories/:id` | Same. |
Behavior:
* `GET` returns all categories sorted by `LOWER(abbreviation)` ascending.
* `POST` uppercases the abbreviation, rejects duplicates with `409 CONFLICT`,
and persists the row. Body validated by `CreateCategorySchema`
(`name` 1120 chars; `abbreviation` 26 chars; `description` up to
2000 chars; `iconPath` optional, up to 2048 chars).
* `PUT` validates the body with `UpdateCategorySchema` (every field
optional). System rows (`system_key IS NOT NULL`) cannot change their
abbreviation — server returns `409 SYSTEM_PROTECTED`. Duplicate
abbreviations on user rows return `409 CONFLICT`. Updates the
`updated_at` timestamp on success.
* `DELETE` is blocked for system rows (`403 SYSTEM_PROTECTED`) and for
rows with at least one attached challenge (`409 CATEGORY_HAS_CHALLENGES`
with `{ count }` in `details`). Returns `404 NOT_FOUND` for unknown ids.
Response shape (`CategoryView`):
```json
{
"id": "uuid",
"name": "Cryptography",
"abbreviation": "CRY",
"description": "Cryptographic challenges",
"iconPath": "/uploads/icons/CRY.png",
"isSystem": true,
"systemKey": "CRY",
"createdAt": "2026-07-21T18:00:00.000Z",
"updatedAt": "2026-07-21T18:00:00.000Z"
}
```
See [Admin — Categories](/guides/admin-categories.md).
## Supporting uploads
Logo and category-icon uploads live on the uploads module and are
documented in [Uploads Endpoints](/api/uploads.md):
| Method | Path | Purpose |
|--------|-----------------------------------|----------------------------------|
| `POST` | `/api/v1/uploads/logo` | Logo used by General settings. |
| `POST` | `/api/v1/uploads/category-icon` | Icon used by Categories. |
Both endpoints are admin-only and use `FileInterceptor('file')` for a
single multipart part named `file`.
# Guard chain
@@ -21,16 +122,13 @@ timestamp: 2026-07-21T18:28:00Z
is `@Public()`. Admin handlers are not.
2. `AdminGuard` (`backend/src/common/guards/admin.guard.ts`) requires
`req.user.role === 'admin'`.
# Behavior
* `AdminService` enforces the **last-admin invariant** (`LAST_ADMIN` /
`409`) — the final admin row cannot be demoted or deleted.
* `POST /api/v1/admin/users` creates a new user; `PATCH /api/v1/admin/users/:id`
changes role / status; `DELETE /api/v1/admin/users/:id` removes the row.
3. `RolesGuard` enforces `@Roles('admin')` metadata on the controller.
# See also
- [Backend Module Map](/architecture/backend-modules.md)
- [Admin Shell & User Management](/guides/admin-shell.md)
- [Admin Shell](/guides/admin-shell.md)
- [Admin — General Settings](/guides/admin-general-settings.md)
- [Admin — Categories](/guides/admin-categories.md)
- [Uploads Endpoints](/api/uploads.md)
- [REST API Overview](/api/rest-overview.md)