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.

89 lines
2.9 KiB
Markdown

+++
title = "quotesdb/api: POST /api/admin/lock and /api/admin/unlock endpoints"
priority = 6
status = "done"
ticket_type = "feature"
dependencies = ["69a2c5"]
+++
## 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
```