use crate::web::state::WebState;
use axum::{Json, extract::State};
use serde::Serialize;
use std::sync::Arc;
#[derive(Debug, Serialize)]
pub struct DashboardOverview {
pub status: String,
pub uptime_secs: u64,
pub metrics: crate::web::metrics::collector::MetricsOverview,
pub recent_alerts: usize,
pub active_sse_connections: usize,
pub active_ws_connections: usize,
}
pub async fn overview(State(state): State<Arc<WebState>>) -> Json<DashboardOverview> {
let mut metrics = state.metrics_collector().get_overview();
let alert_count = state.alert_engine().recent_alert_count();
if let Some(registry) = state.registry()
&& let Some(cache) = registry.get("cache")
&& let Some(cache_plugin) = cache
.as_ref()
.as_any()
.downcast_ref::<crate::plugins::CachePlugin>()
{
let stats = cache_plugin.stats();
let cache_hits = stats.hits();
let cache_misses = stats.misses();
let total = cache_hits + cache_misses;
metrics.cache_hits = cache_hits;
metrics.cache_misses = cache_misses;
metrics.cache_hit_rate = if total > 0 {
(cache_hits as f64 / total as f64) * 100.0
} else {
0.0
};
}
let response = DashboardOverview {
status: "running".to_string(),
uptime_secs: state.uptime_secs(),
metrics,
recent_alerts: alert_count,
active_sse_connections: state.active_sse_connections() as usize,
active_ws_connections: state.active_ws_connections() as usize,
};
Json(response)
}
#[derive(Debug, Serialize)]
pub struct CacheStatsResponse {
pub size: usize,
pub hits: u64,
pub misses: u64,
pub evictions: u64,
pub expirations: u64,
pub hit_rate: f64,
}
pub async fn cache_stats(State(state): State<Arc<WebState>>) -> Json<CacheStatsResponse> {
let Some(registry) = state.registry() else {
return Json(CacheStatsResponse {
size: 0,
hits: 0,
misses: 0,
evictions: 0,
expirations: 0,
hit_rate: 0.0,
});
};
let Some(cache) = registry.get("cache") else {
return Json(CacheStatsResponse {
size: 0,
hits: 0,
misses: 0,
evictions: 0,
expirations: 0,
hit_rate: 0.0,
});
};
if let Some(cache_plugin) = cache
.as_ref()
.as_any()
.downcast_ref::<crate::plugins::CachePlugin>()
{
let stats = cache_plugin.stats();
let hits = stats.hits();
let misses = stats.misses();
let total = hits + misses;
let hit_rate = if total > 0 {
(hits as f64 / total as f64) * 100.0
} else {
0.0
};
Json(CacheStatsResponse {
size: cache_plugin.size(),
hits,
misses,
evictions: stats.evictions(),
expirations: stats.expirations(),
hit_rate,
})
} else {
Json(CacheStatsResponse {
size: 0,
hits: 0,
misses: 0,
evictions: 0,
expirations: 0,
hit_rate: 0.0,
})
}
}
#[derive(Debug, Serialize)]
pub struct ServerInfoResponse {
pub version: String,
pub uptime_secs: u64,
pub rss_bytes: u64,
}
pub async fn server_info(State(state): State<Arc<WebState>>) -> Json<ServerInfoResponse> {
#[cfg(feature = "metrics")]
let rss_bytes = crate::metrics::memory::PROCESS_RESIDENT_MEMORY_BYTES.get() as u64;
#[cfg(not(feature = "metrics"))]
let rss_bytes = 0u64;
Json(ServerInfoResponse {
version: env!("CARGO_PKG_VERSION").to_string(),
uptime_secs: state.uptime_secs(),
rss_bytes,
})
}
#[derive(Debug, Serialize)]
pub struct RecentAlertsResponse {
pub alerts: Vec<crate::web::alerts::Alert>,
pub total: usize,
}
pub async fn recent_alerts(State(state): State<Arc<WebState>>) -> Json<RecentAlertsResponse> {
let alerts = state.alert_engine().recent_alerts(50);
let total = alerts.len();
Json(RecentAlertsResponse { alerts, total })
}