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.
2.9 KiB
2.9 KiB
| title | status | type | priority | created_at | updated_at | blocked_by | |
|---|---|---|---|---|---|---|---|
| quotesdb/api: POST /api/admin/lock and /api/admin/unlock endpoints | completed | feature | normal | 2026-03-10T23:32:05Z | 2026-03-10T23:32:12Z |
|
POST /api/admin/lock and /api/admin/unlock endpoints
Add the two admin-protected endpoints that toggle the global submissions lock. Both require X-Admin-Code and return the current lock state after the operation.
Note: This ticket depends on ticket 35685a (GET /api/status) because that ticket adds get_submissions_locked and set_submissions_locked to the QuoteRepository trait and seeds the submissions_locked row in the database. Complete 35685a first.
Files to modify
src/bin/api/handlers/mod.rs(orsrc/bin/api/handlers/admin.rs) — addlock_submissionsandunlock_submissionshandlerssrc/bin/api/main.rs— register the two new routes
No new DB trait methods are needed; both handlers reuse set_submissions_locked(bool) introduced in 35685a.
Handlers
/// POST /api/admin/lock
/// Requires X-Admin-Code header. Sets submissions_locked = true.
/// Response: 200 { "submissions_locked": true } or 403 on bad code.
pub async fn lock_submissions(
State(repo): State<Arc<dyn QuoteRepository>>,
headers: HeaderMap,
) -> impl IntoResponse {
let admin_code = extract_admin_code(&headers);
if !verify_admin_code(&repo, admin_code).await { ... }
match repo.set_submissions_locked(true).await {
Ok(()) => Json(json!({ "submissions_locked": true })).into_response(),
Err(_) => StatusCode::INTERNAL_SERVER_ERROR.into_response(),
}
}
/// POST /api/admin/unlock
/// Requires X-Admin-Code header. Sets submissions_locked = false.
/// Response: 200 { "submissions_locked": false } or 403 on bad code.
pub async fn unlock_submissions(
State(repo): State<Arc<dyn QuoteRepository>>,
headers: HeaderMap,
) -> impl IntoResponse {
// same pattern, locked = false
}
Implement a shared helper verify_admin_code(repo, code) -> bool (or extract inline) that fetches the stored admin code from admin_config and compares it. Use constant-time comparison if possible.
Route registration (src/bin/api/main.rs)
.route("/api/admin/lock", post(handlers::lock_submissions))
.route("/api/admin/unlock", post(handlers::unlock_submissions))
Tests
POST /api/admin/lockwith correctX-Admin-Code→200 { "submissions_locked": true }POST /api/admin/unlockwith correctX-Admin-Code→200 { "submissions_locked": false }POST /api/admin/lockwith wrong code →403POST /api/admin/unlockwith missing header →403- Lock/unlock idempotent: locking when already locked still returns
200 { "submissions_locked": true }
Validation
cargo fmt && cargo check && cargo clippy && cargo test
Commit
feat(quotesdb): POST /api/admin/lock and /api/admin/unlock endpoints