use axum::Json;
use axum::extract::State;
use serde::Deserialize;
use serde_json::{Value, json};
use std::time::Duration;
use crate::admin;
use crate::sqlite::nonce::Nonce;
use crate::webadmin::AdminState;
use crate::webadmin::error::AdminError;
use crate::webadmin::session::{Authenticated, AuthenticatedWrite};
#[derive(Debug, Deserialize, Default)]
pub struct CleanupRequest {
#[serde(rename = "ttlSeconds")]
pub ttl_seconds: Option<u64>,
}
pub async fn get_nonces(
State(state): State<AdminState>,
_auth: Authenticated,
) -> Result<Json<Value>, AdminError> {
let count = Nonce::count(&state.database).await?;
Ok(Json(json!({
"count": count,
"ttlSeconds": state.config.nonce.ttl_seconds,
})))
}
pub async fn cleanup_nonces(
State(state): State<AdminState>,
AuthenticatedWrite(auth): AuthenticatedWrite,
body: Option<Json<CleanupRequest>>,
) -> Result<Json<Value>, AdminError> {
let seconds = body
.and_then(|Json(body)| body.ttl_seconds)
.unwrap_or(state.config.nonce.ttl_seconds);
let removed =
admin::cleanup_nonces(Duration::from_secs(seconds), state.database.clone()).await?;
tracing::info!(event = "admin_nonces_cleaned",
outcome = "success",
surface = "api",
rows_removed = removed,
ttl_seconds = seconds,
username = %auth.user.username);
Ok(Json(json!({ "removed": removed })))
}
pub async fn list_profiles(State(state): State<AdminState>, _auth: Authenticated) -> Json<Value> {
Json(Value::Array(profile_rows(&state)))
}
pub(crate) fn profile_rows(state: &AdminState) -> Vec<Value> {
let mut names: Vec<&String> = state.profiles.keys().collect();
names.sort();
names
.into_iter()
.filter_map(|name| state.profiles.get(name))
.map(|profile| {
json!({
"name": profile.name,
"baseUrl": profile.base_url,
"directory": profile.directory_url(),
"challengeBypass": profile.challenges.is_bypassed(),
"eabEnabled": profile.eab.enabled,
})
})
.collect()
}