use axum::{
extract::{Path, State},
http::StatusCode,
routing::{patch as patch_route, post},
Json, Router,
};
use gradatum_core::error::GradatumError;
use gradatum_core::identity::NoteId;
use gradatum_dto::{NoteStatusPatch, VaultDowngradeRequest, VaultDowngradeResponse};
use ulid::Ulid;
use crate::state::AppState;
pub fn router() -> Router<AppState> {
Router::new()
.route("/vault_downgrade", post(vault_downgrade))
.route("/notes/{id}", patch_route(patch_note))
}
fn parse_note_id(s: &str) -> Result<NoteId, StatusCode> {
Ulid::from_string(s)
.map(NoteId)
.map_err(|_| StatusCode::BAD_REQUEST)
}
async fn vault_downgrade(
State(state): State<AppState>,
Json(req): Json<VaultDowngradeRequest>,
) -> Result<Json<VaultDowngradeResponse>, StatusCode> {
let note_id = parse_note_id(&req.note_id)?;
let replaced_by = req.replaced_by.as_deref().map(parse_note_id).transpose()?;
state
.search
.downgrade_note(¬e_id, &req.reason, replaced_by.as_ref())
.await
.map_err(|e| match e {
GradatumError::NoteNotFound(_) => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
})?;
let now = chrono::Utc::now().timestamp_millis();
Ok(Json(VaultDowngradeResponse {
note_id: req.note_id,
status: "downgraded".to_string(),
status_changed: now,
reason: req.reason,
}))
}
async fn patch_note(
State(state): State<AppState>,
Path(id): Path<String>,
Json(body): Json<NoteStatusPatch>,
) -> Result<StatusCode, StatusCode> {
let note_id = parse_note_id(&id)?;
if body.status.is_none() && body.status_reason.is_none() && body.replaced_by.is_none() {
return Err(StatusCode::BAD_REQUEST);
}
if let Some(s) = &body.status {
if !matches!(s.as_str(), "live" | "staging" | "downgraded") {
return Err(StatusCode::BAD_REQUEST);
}
}
let replaced_by = body.replaced_by.as_deref().map(parse_note_id).transpose()?;
state
.search
.patch_note_status(
¬e_id,
body.status.as_deref(),
body.status_reason.as_deref(),
replaced_by.as_ref(),
)
.await
.map_err(|e| match e {
GradatumError::NoteNotFound(_) => StatusCode::NOT_FOUND,
_ => StatusCode::INTERNAL_SERVER_ERROR,
})?;
Ok(StatusCode::NO_CONTENT)
}