use axum::{extract::State, http::StatusCode, Json};
use serde_json::{json, Value};
use std::sync::atomic::Ordering;
use crate::api::AppState;
pub async fn health() -> (StatusCode, Json<Value>) {
(StatusCode::OK, Json(json!({ "status": "ok" })))
}
pub async fn ready(State(state): State<AppState>) -> (StatusCode, Json<Value>) {
if sqlx::query_scalar::<_, i32>("SELECT 1")
.fetch_one(&state.pool)
.await
.is_err()
{
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({ "status": "not_ready", "failing": "database" })),
);
}
let expected = crate::db::expected_migration_count() as i64;
let applied: Result<i64, _> =
sqlx::query_scalar("SELECT COUNT(*) FROM _sqlx_migrations WHERE success = TRUE")
.fetch_one(&state.pool)
.await;
match applied {
Ok(n) if n >= expected => {}
Ok(n) => {
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({
"status": "not_ready",
"failing": "migrations",
"expected": expected,
"applied": n
})),
);
}
Err(_) => {
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({ "status": "not_ready", "failing": "migrations" })),
);
}
}
let listener = state.listener_alive.load(Ordering::Relaxed);
let stale = state.stale_alive.load(Ordering::Relaxed);
if !(listener && stale) {
let failing = if !listener && !stale {
"background_tasks"
} else if !listener {
"background_task:listener"
} else {
"background_task:stale_recovery"
};
return (
StatusCode::SERVICE_UNAVAILABLE,
Json(json!({ "status": "not_ready", "failing": failing })),
);
}
(StatusCode::OK, Json(json!({ "status": "ready" })))
}