use axum::{routing::get, Json, Router};
use std::net::SocketAddr;
use tokio::net::TcpListener;
const DASHBOARD_JSON: &str = include_str!("../assets/grafana_dashboard.json");
#[derive(Debug, Clone)]
pub struct DashboardServerConfig {
pub bind_address: SocketAddr,
}
impl Default for DashboardServerConfig {
fn default() -> Self {
Self {
bind_address: ([127, 0, 0, 1], 9898).into(),
}
}
}
impl DashboardServerConfig {
pub fn new(addr: impl Into<SocketAddr>) -> Self {
Self {
bind_address: addr.into(),
}
}
pub fn with_port(port: u16) -> Self {
Self {
bind_address: ([0, 0, 0, 0], port).into(),
}
}
}
pub async fn start_dashboard_server(config: DashboardServerConfig) -> std::io::Result<()> {
let app = Router::new()
.route("/dashboard", get(serve_dashboard))
.route("/health", get(health_check));
let listener = TcpListener::bind(config.bind_address).await?;
log::info!(
"FlyLLM dashboard server listening on http://{}",
config.bind_address
);
log::info!(
"Dashboard JSON available at http://{}/dashboard",
config.bind_address
);
axum::serve(listener, app).await?;
Ok(())
}
async fn serve_dashboard() -> Json<serde_json::Value> {
match serde_json::from_str(DASHBOARD_JSON) {
Ok(json) => Json(json),
Err(e) => {
log::error!("Failed to parse dashboard JSON: {}", e);
Json(serde_json::json!({
"error": "Failed to parse dashboard JSON",
"details": e.to_string()
}))
}
}
}
async fn health_check() -> &'static str {
"OK"
}