You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
136 lines
4.1 KiB
Markdown
136 lines
4.1 KiB
Markdown
# Admin Features — Design
|
|
|
|
**Date:** 2026-03-04
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
Adds a set of admin-only controls to the QuotesDB API and UI:
|
|
|
|
- **Auth code reset** — change the admin passphrase in-place.
|
|
- **Submission lock / unlock** — globally prevent new quotes from being created.
|
|
- **Public status endpoint** — lets the UI know whether submissions are open.
|
|
- **Admin UI page** — a single `/admin` page with an auth code field, a reset
|
|
form, and a lock/unlock toggle.
|
|
- **Submit page gate** — shows a "submissions are closed" banner instead of the
|
|
form when the lock is active.
|
|
|
|
---
|
|
|
|
## API
|
|
|
|
### Public endpoints
|
|
|
|
| Method | Path | Auth | Description |
|
|
|--------|------|------|-------------|
|
|
| `GET` | `/api/status` | None | Returns `{ "submissions_locked": bool }` |
|
|
|
|
### Admin endpoints
|
|
|
|
All admin endpoints require the `X-Admin-Code` header matching the value stored
|
|
in `admin_config`. A mismatch returns `403 Forbidden`.
|
|
|
|
| Method | Path | Body | Response |
|
|
|--------|------|------|----------|
|
|
| `POST` | `/api/admin/reset-auth-code` | `{ "new_code"?: "..." }` | `200 { "auth_code": "new-code" }` |
|
|
| `POST` | `/api/admin/lock` | — | `200 { "submissions_locked": true }` |
|
|
| `POST` | `/api/admin/unlock` | — | `200 { "submissions_locked": false }` |
|
|
|
|
`new_code` is optional. If omitted, the server generates a new 4-word
|
|
passphrase.
|
|
|
|
### Modified endpoints
|
|
|
|
`PUT /api/quotes` — gains a pre-flight lock check:
|
|
- If `submissions_locked = true` → `423 Locked` with
|
|
`{ "error": "submissions are closed" }`.
|
|
|
|
---
|
|
|
|
## Database
|
|
|
|
The existing `admin_config` key/value table gains a second row:
|
|
|
|
| key | value |
|
|
|-----|-------|
|
|
| `admin_auth_code` | the 4-word passphrase (already exists) |
|
|
| `submissions_locked` | `"0"` or `"1"` (new) |
|
|
|
|
`submissions_locked` is seeded to `"0"` alongside `admin_auth_code` on
|
|
startup.
|
|
|
|
### New `QuoteRepository` trait methods
|
|
|
|
```rust
|
|
/// Replace the admin auth code if `current` matches; generate or use `new_code`.
|
|
/// Returns the new auth code string.
|
|
async fn update_admin_auth_code(
|
|
&self,
|
|
current: &str,
|
|
new_code: Option<&str>,
|
|
) -> Result<String, DbError>;
|
|
|
|
/// Return whether submissions are currently locked.
|
|
async fn get_submissions_locked(&self) -> Result<bool, DbError>;
|
|
|
|
/// Set the submissions lock state.
|
|
async fn set_submissions_locked(&self, locked: bool) -> Result<(), DbError>;
|
|
```
|
|
|
|
---
|
|
|
|
## Frontend
|
|
|
|
### New route
|
|
|
|
| Route | Page |
|
|
|-------|------|
|
|
| `/admin` | Admin controls page |
|
|
|
|
### `/admin` page
|
|
|
|
- A single persistent `Admin auth code` text input at the top.
|
|
- **Reset auth code section:** optional `New passphrase` field + "Reset" button.
|
|
On success, shows the new code prominently.
|
|
- **Submissions section:** shows current lock state (fetched via `GET
|
|
/api/status` on mount) and a single "Lock submissions" / "Unlock submissions"
|
|
button.
|
|
- All actions send `X-Admin-Code: <value-from-field>`.
|
|
- `403` responses show an "Wrong auth code" inline error.
|
|
|
|
### `/submit` page
|
|
|
|
- On mount, calls `GET /api/status`.
|
|
- If `submissions_locked = true`, hides the form entirely and shows a banner:
|
|
> "Submissions are currently closed."
|
|
- The submit nav link in the top nav remains active (users still reach the page
|
|
and see the closed notice).
|
|
|
|
### New API client functions (`src/bin/ui/api.rs`)
|
|
|
|
```rust
|
|
pub async fn get_status() -> Result<StatusResponse, ApiError>
|
|
pub async fn admin_reset_auth_code(current: &str, new_code: Option<&str>, admin_code: &str) -> Result<String, ApiError>
|
|
pub async fn admin_lock(admin_code: &str) -> Result<bool, ApiError>
|
|
pub async fn admin_unlock(admin_code: &str) -> Result<bool, ApiError>
|
|
```
|
|
|
|
---
|
|
|
|
## Auth flow
|
|
|
|
The admin code is a 4-word passphrase printed to stderr on API startup. It is
|
|
never exposed over the API (no GET endpoint). The admin enters it manually into
|
|
the `/admin` page's persistent field each session.
|
|
|
|
---
|
|
|
|
## Error handling
|
|
|
|
| Scenario | HTTP status | UI message |
|
|
|----------|-------------|------------|
|
|
| Wrong admin code | `403 Forbidden` | "Wrong auth code." |
|
|
| Submissions locked on create | `423 Locked` | Banner: "Submissions are currently closed." |
|
|
| DB error | `500 Internal Server Error` | Generic error display |
|