use axum::extract::State;
use axum::Json;
use time::OffsetDateTime;
use crate::lfd::http::dto::{HealthResponse, MetricsResponse, StatusResponse};
use crate::lfd::http::state::HttpState;
use crate::lfd::types::RunStatus;
pub async fn health_handler(State(state): State<HttpState>) -> Json<HealthResponse> {
let counts = counts(&state).await;
Json(HealthResponse {
status: if counts.database_ok { "ok" } else { "degraded" }.to_string(),
uptime_seconds: (OffsetDateTime::now_utc() - state.started_at).whole_seconds(),
database: counts.database_ok,
waves_running: counts.waves_running,
})
}
pub async fn status_handler(State(state): State<HttpState>) -> Json<StatusResponse> {
let counts = counts(&state).await;
Json(StatusResponse {
pid: std::process::id(),
role: "gatekeeper: reads, push, webhook ingress; mutations exec lf".to_string(),
waves_defined: counts.waves_defined,
waves_running: counts.waves_running,
})
}
pub async fn metrics_handler(State(state): State<HttpState>) -> Json<MetricsResponse> {
let counts = counts(&state).await;
Json(MetricsResponse {
waves_total: counts.waves_defined,
waves_running: counts.waves_running,
})
}
struct Counts {
waves_defined: u32,
waves_running: u32,
database_ok: bool,
}
async fn counts(state: &HttpState) -> Counts {
let waves_fut = state.store.list_waves(None);
let runs_fut = state.store.list_runs(None, None);
let health_fut = state.store.health_check();
let (waves, runs, health) = tokio::join!(waves_fut, runs_fut, health_fut);
let waves = waves.unwrap_or_default();
let runs = runs.unwrap_or_default();
let database_ok = health.is_ok();
let waves_defined = waves.len() as u32;
let waves_running = runs
.iter()
.filter(|run| {
matches!(
run.status,
RunStatus::Running | RunStatus::Waiting | RunStatus::Pending
)
})
.count() as u32;
Counts {
waves_defined,
waves_running,
database_ok,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::lfd::http::routes::test_helpers::test_http_state;
#[tokio::test]
async fn health_handler_reports_core_status() {
let state = test_http_state().await;
let Json(payload) = health_handler(State(state)).await;
assert_eq!(payload.waves_running, 0);
assert!(payload.database);
}
#[tokio::test]
async fn status_handler_names_the_gatekeeper_role() {
let state = test_http_state().await;
let Json(payload) = status_handler(State(state)).await;
assert_eq!(payload.waves_defined, 0);
assert!(payload.role.contains("gatekeeper"));
}
}