diff --git a/src/dashboard/clients.rs b/src/dashboard/clients.rs index 7cd08d18..ca5c1709 100644 --- a/src/dashboard/clients.rs +++ b/src/dashboard/clients.rs @@ -33,7 +33,15 @@ pub(super) struct UpdateClientPayload { pub(super) rate_limit_per_minute: Option, } -pub(super) async fn handle_get_clients(State(state): State) -> Json> { +pub(super) async fn handle_get_clients( + State(state): State, + headers: axum::http::HeaderMap, +) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let result = sqlx::query( @@ -321,8 +329,14 @@ pub(super) async fn handle_delete_client( pub(super) async fn handle_client_usage( State(state): State, + headers: axum::http::HeaderMap, Path(id): Path, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; // Get per-model breakdown for this client @@ -381,8 +395,14 @@ pub(super) async fn handle_client_usage( pub(super) async fn handle_get_client_tokens( State(state): State, + headers: axum::http::HeaderMap, Path(id): Path, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let result = sqlx::query( diff --git a/src/dashboard/models.rs b/src/dashboard/models.rs index ed321cb9..f06cf46f 100644 --- a/src/dashboard/models.rs +++ b/src/dashboard/models.rs @@ -43,8 +43,14 @@ pub(super) struct ModelListParams { pub(super) async fn handle_get_models( State(state): State, + headers: axum::http::HeaderMap, Query(params): Query, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let registry = &state.app_state.model_registry; let pool = &state.app_state.db_pool; diff --git a/src/dashboard/providers.rs b/src/dashboard/providers.rs index 86d9db7b..7f9f204a 100644 --- a/src/dashboard/providers.rs +++ b/src/dashboard/providers.rs @@ -21,7 +21,15 @@ pub(super) struct UpdateProviderRequest { pub(super) billing_mode: Option, } -pub(super) async fn handle_get_providers(State(state): State) -> Json> { +pub(super) async fn handle_get_providers( + State(state): State, + headers: axum::http::HeaderMap, +) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let registry = &state.app_state.model_registry; let config = &state.app_state.config; let pool = &state.app_state.db_pool; @@ -154,8 +162,14 @@ pub(super) async fn handle_get_providers(State(state): State) -> pub(super) async fn handle_get_provider( State(state): State, + headers: axum::http::HeaderMap, Path(name): Path, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let registry = &state.app_state.model_registry; let config = &state.app_state.config; let pool = &state.app_state.db_pool; @@ -351,8 +365,14 @@ pub(super) async fn handle_update_provider( pub(super) async fn handle_test_provider( State(state): State, + headers: axum::http::HeaderMap, Path(name): Path, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let start = std::time::Instant::now(); let provider = match state.app_state.provider_manager.get_provider(&name).await { diff --git a/src/dashboard/system.rs b/src/dashboard/system.rs index 540aface..7a1be5bf 100644 --- a/src/dashboard/system.rs +++ b/src/dashboard/system.rs @@ -12,7 +12,15 @@ fn read_proc_file(path: &str) -> Option { std::fs::read_to_string(path).ok() } -pub(super) async fn handle_system_health(State(state): State) -> Json> { +pub(super) async fn handle_system_health( + State(state): State, + headers: axum::http::HeaderMap, +) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let mut components = HashMap::new(); components.insert("api_server".to_string(), "online".to_string()); components.insert("database".to_string(), "online".to_string()); @@ -67,7 +75,13 @@ pub(super) async fn handle_system_health(State(state): State) -> /// Real system metrics from /proc (Linux only). pub(super) async fn handle_system_metrics( State(state): State, + headers: axum::http::HeaderMap, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + // --- CPU usage (aggregate across all cores) --- // /proc/stat first line: cpu user nice system idle iowait irq softirq steal guest guest_nice let cpu_percent = read_proc_file("/proc/stat") @@ -220,7 +234,15 @@ pub(super) async fn handle_system_metrics( }))) } -pub(super) async fn handle_system_logs(State(state): State) -> Json> { +pub(super) async fn handle_system_logs( + State(state): State, + headers: axum::http::HeaderMap, +) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let result = sqlx::query( @@ -318,7 +340,15 @@ pub(super) async fn handle_system_backup( } } -pub(super) async fn handle_get_settings(State(state): State) -> Json> { +pub(super) async fn handle_get_settings( + State(state): State, + headers: axum::http::HeaderMap, +) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let registry = &state.app_state.model_registry; let provider_count = registry.providers.len(); let model_count: usize = registry.providers.values().map(|p| p.models.len()).sum(); diff --git a/src/dashboard/usage.rs b/src/dashboard/usage.rs index 19465be4..de7a532c 100644 --- a/src/dashboard/usage.rs +++ b/src/dashboard/usage.rs @@ -71,8 +71,14 @@ impl UsagePeriodFilter { pub(super) async fn handle_usage_summary( State(state): State, + headers: axum::http::HeaderMap, Query(filter): Query, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let (period_clause, period_binds) = filter.to_sql(); @@ -133,7 +139,9 @@ pub(super) async fn handle_usage_summary( ) .fetch_one(pool); - match tokio::join!(total_stats, today_stats, error_stats, avg_response) { + let results = tokio::join!(total_stats, today_stats, error_stats, avg_response); + + match results { (Ok(t), Ok(d), Ok(e), Ok(a)) => { let total_requests: i64 = t.get("total_requests"); let total_tokens: i64 = t.get("total_tokens"); @@ -168,14 +176,26 @@ pub(super) async fn handle_usage_summary( "total_cache_write_tokens": total_cache_write, }))) } - _ => Json(ApiResponse::error("Failed to fetch usage statistics".to_string())), + (t_res, d_res, e_res, a_res) => { + if let Err(e) = t_res { warn!("Total stats query failed: {}", e); } + if let Err(e) = d_res { warn!("Today stats query failed: {}", e); } + if let Err(e) = e_res { warn!("Error stats query failed: {}", e); } + if let Err(e) = a_res { warn!("Avg response query failed: {}", e); } + Json(ApiResponse::error("Failed to fetch usage statistics".to_string())) + } } } pub(super) async fn handle_time_series( State(state): State, + headers: axum::http::HeaderMap, Query(filter): Query, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let (period_clause, period_binds) = filter.to_sql(); let granularity = filter.granularity(); @@ -248,8 +268,14 @@ pub(super) async fn handle_time_series( pub(super) async fn handle_clients_usage( State(state): State, + headers: axum::http::HeaderMap, Query(filter): Query, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let (period_clause, period_binds) = filter.to_sql(); @@ -308,8 +334,14 @@ pub(super) async fn handle_clients_usage( pub(super) async fn handle_providers_usage( State(state): State, + headers: axum::http::HeaderMap, Query(filter): Query, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let (period_clause, period_binds) = filter.to_sql(); @@ -370,8 +402,14 @@ pub(super) async fn handle_providers_usage( pub(super) async fn handle_detailed_usage( State(state): State, + headers: axum::http::HeaderMap, Query(filter): Query, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let (period_clause, period_binds) = filter.to_sql(); @@ -433,8 +471,14 @@ pub(super) async fn handle_detailed_usage( pub(super) async fn handle_analytics_breakdown( State(state): State, + headers: axum::http::HeaderMap, Query(filter): Query, ) -> Json> { + let (_session, _) = match super::auth::require_admin(&state, &headers).await { + Ok((session, new_token)) => (session, new_token), + Err(e) => return e, + }; + let pool = &state.app_state.db_pool; let (period_clause, period_binds) = filter.to_sql();