use axum::{
Json, Router,
extract::State,
http::StatusCode,
response::{IntoResponse, Response},
routing::get,
};
use core_config::AppInfo;
use futures::future::join_all;
use serde::Serialize;
use serde_json::{Value, json};
use std::collections::HashMap;
use std::future::Future;
use std::pin::Pin;
#[derive(Serialize)]
pub struct HealthResponse {
pub status: &'static str,
pub name: &'static str,
pub version: &'static str,
}
#[derive(Serialize)]
pub struct ReadyResponse {
pub ready: bool,
pub services: ServiceStatus,
}
#[derive(Serialize)]
pub struct ServiceStatus {
pub database: bool,
pub cache: bool,
}
pub type HealthCheckFuture<'a> = Pin<Box<dyn Future<Output = Result<(), String>> + Send + 'a>>;
pub async fn run_health_checks(
checks: Vec<(&str, HealthCheckFuture<'_>)>,
) -> Result<(StatusCode, Json<Value>), (StatusCode, Json<Value>)> {
let names: Vec<_> = checks.iter().map(|(name, _)| *name).collect();
let futures: Vec<_> = checks.into_iter().map(|(_, check)| check).collect();
let results = join_all(futures).await;
let mut status_map = HashMap::new();
let mut all_healthy = true;
for (name, result) in names.into_iter().zip(results) {
match result {
Ok(_) => {
status_map.insert(name, "connected");
}
Err(e) => {
tracing::error!("Readiness check failed: {} error: {:?}", name, e);
status_map.insert(name, "disconnected");
all_healthy = false;
}
}
}
let mut response = json!({
"status": if all_healthy { "ready" } else { "not ready" }
});
if let Value::Object(ref mut map) = response {
for (name, status) in status_map {
map.insert(name.to_string(), json!(status));
}
}
if all_healthy {
Ok((StatusCode::OK, Json(response)))
} else {
Err((StatusCode::SERVICE_UNAVAILABLE, Json(response)))
}
}
pub async fn health_handler(State(app): State<AppInfo>) -> Response {
let response = HealthResponse {
status: "healthy",
name: app.name,
version: app.version,
};
(StatusCode::OK, Json(response)).into_response()
}
pub fn health_router(app_info: AppInfo) -> Router {
Router::new()
.route("/health", get(health_handler))
.with_state(app_info)
}