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.
95 lines
2.9 KiB
Markdown
95 lines
2.9 KiB
Markdown
---
|
|
# quotesdb-njv2
|
|
title: 'quotesdb/api: POST /api/admin/lock and /api/admin/unlock endpoints'
|
|
status: completed
|
|
type: feature
|
|
priority: normal
|
|
created_at: 2026-03-10T23:32:05Z
|
|
updated_at: 2026-03-10T23:32:12Z
|
|
blocked_by:
|
|
- quotesdb-04cw
|
|
---
|
|
|
|
## 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` (or `src/bin/api/handlers/admin.rs`) — add `lock_submissions` and `unlock_submissions` handlers
|
|
- `src/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
|
|
|
|
```rust
|
|
/// 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)
|
|
|
|
```rust
|
|
.route("/api/admin/lock", post(handlers::lock_submissions))
|
|
.route("/api/admin/unlock", post(handlers::unlock_submissions))
|
|
```
|
|
|
|
---
|
|
|
|
## Tests
|
|
|
|
- `POST /api/admin/lock` with correct `X-Admin-Code` → `200 { "submissions_locked": true }`
|
|
- `POST /api/admin/unlock` with correct `X-Admin-Code` → `200 { "submissions_locked": false }`
|
|
- `POST /api/admin/lock` with wrong code → `403`
|
|
- `POST /api/admin/unlock` with missing header → `403`
|
|
- Lock/unlock idempotent: locking when already locked still returns `200 { "submissions_locked": true }`
|
|
|
|
---
|
|
|
|
## Validation
|
|
|
|
```sh
|
|
cargo fmt && cargo check && cargo clippy && cargo test
|
|
```
|
|
|
|
---
|
|
|
|
## Commit
|
|
|
|
```
|
|
feat(quotesdb): POST /api/admin/lock and /api/admin/unlock endpoints
|
|
```
|