use axum::{Json, Router, extract::State, http::StatusCode, response::IntoResponse, routing::get}; use serde::Serialize; use utoipa::ToSchema; use crate::AppState; /// Liveness payload: the process is running. #[derive(Serialize, ToSchema)] pub(crate) struct Live { /// Always `"ok"` when the process serves requests. pub status: &'static str, } /// Readiness payload: dependencies were checked. #[derive(Serialize, ToSchema)] pub(crate) struct Ready { /// `"ok"` when ready, `"degraded"` otherwise. pub status: &'static str, /// Whether the database responded to a ping. pub database: bool, } /// Liveness probe — no dependencies checked. #[utoipa::path(get, path = "/health/live", responses((status = 200, body = Live)))] pub(crate) async fn live() -> Json { Json(Live { status: "ok" }) } /// Readiness probe — confirms the database answers. #[utoipa::path( get, path = "/health/ready", responses( (status = 200, body = Ready, description = "Ready"), (status = 503, body = Ready, description = "A dependency is unavailable") ) )] pub(crate) async fn ready(State(state): State) -> impl IntoResponse { match state.db.ping().await { Ok(()) => ( StatusCode::OK, Json(Ready { status: "ok", database: true, }), ), Err(_) => ( StatusCode::SERVICE_UNAVAILABLE, Json(Ready { status: "degraded", database: false, }), ), } } /// Health routes, parameterized over [`AppState`]. pub(crate) fn routes() -> Router { Router::new() .route("/health/live", get(live)) .route("/health/ready", get(ready)) }