Skip to main content

mini_apm_admin/api/
health.rs

1use axum::{Json, extract::State, http::StatusCode};
2use serde::Serialize;
3use std::time::Instant;
4
5use mini_apm::{DbPool, db};
6
7static START_TIME: std::sync::OnceLock<Instant> = std::sync::OnceLock::new();
8
9pub fn init_start_time() {
10    START_TIME.get_or_init(Instant::now);
11}
12
13#[derive(Serialize)]
14pub struct HealthResponse {
15    pub status: String,
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub error: Option<String>,
18    pub db_size_mb: f64,
19    pub uptime_seconds: u64,
20    pub db_ok: bool,
21}
22
23pub async fn health_handler(State(pool): State<DbPool>) -> (StatusCode, Json<HealthResponse>) {
24    let uptime_seconds = START_TIME.get().map(|t| t.elapsed().as_secs()).unwrap_or(0);
25
26    // Actually verify database connectivity
27    let db_ok = match pool.get() {
28        Ok(conn) => conn.query_row("SELECT 1", [], |_| Ok(())).is_ok(),
29        Err(_) => false,
30    };
31
32    if db_ok {
33        let db_size_mb = db::get_db_size(&pool).unwrap_or(0.0);
34        (
35            StatusCode::OK,
36            Json(HealthResponse {
37                status: "ok".to_string(),
38                error: None,
39                db_size_mb,
40                uptime_seconds,
41                db_ok: true,
42            }),
43        )
44    } else {
45        tracing::error!("Health check failed: database unreachable");
46        (
47            StatusCode::SERVICE_UNAVAILABLE,
48            Json(HealthResponse {
49                status: "unhealthy".to_string(),
50                error: Some("Database unreachable".to_string()),
51                db_size_mb: 0.0,
52                uptime_seconds,
53                db_ok: false,
54            }),
55        )
56    }
57}