mocra 0.3.0

A distributed, event-driven crawling and data collection framework
use crate::engine::api::state::ApiState;
use axum::{Json, extract::State};
use serde::Serialize;
use std::time::Instant;
// use sea_orm::ConnectionTrait; // for ping() - DatabaseConnection implements it inherently or via Deref?

#[derive(Serialize)]
pub struct Components {
    cache: ComponentStatus,
    db: ComponentStatus,
}

#[derive(Serialize)]
pub struct ComponentStatus {
    status: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    error: Option<String>,
}

#[derive(Serialize)]
pub struct HealthResponse {
    status: String,
    components: Components,
}

impl ComponentStatus {
    fn up() -> Self {
        Self {
            status: "up".to_string(),
            error: None,
        }
    }
    fn down(e: impl ToString) -> Self {
        Self {
            status: "down".to_string(),
            error: Some(e.to_string()),
        }
    }
}

pub async fn health_check(State(state): State<ApiState>) -> Json<HealthResponse> {
    let started = Instant::now();
    let cache_status = match state.state.cache_service.ping().await {
        Ok(_) => ComponentStatus::up(),
        Err(e) => ComponentStatus::down(e),
    };

    // Check DB
    let db_status = match state.state.db.ping().await {
        Ok(_) => ComponentStatus::up(),
        Err(e) => ComponentStatus::down(e),
    };

    let global_status = if cache_status.status == "up" && db_status.status == "up" {
        "up"
    } else {
        "degraded"
    };

    crate::common::metrics::set_component_health("cache", cache_status.status == "up");
    crate::common::metrics::set_component_health("db", db_status.status == "up");
    crate::common::metrics::inc_throughput(
        "control_plane",
        "health",
        "health_check",
        global_status,
        1,
    );
    crate::common::metrics::observe_latency(
        "control_plane",
        "health",
        "health_check",
        global_status,
        started.elapsed().as_secs_f64(),
    );

    Json(HealthResponse {
        status: global_status.to_string(),
        components: Components {
            cache: cache_status,
            db: db_status,
        },
    })
}