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.
vibed/quotesdb/docs/plans/2026-03-04-admin-features-d...

4.1 KiB

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 = true423 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

/// 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)

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