From 2de7f3e060c9e8fedd73b2b7d69fb2f14dc8e38f Mon Sep 17 00:00:00 2001 From: whit3rabbit Date: Sun, 5 Apr 2026 15:12:52 -0500 Subject: [PATCH] feat(proxy): register traffic/uptime routes, spawn health checker, switch to dist/index.html - Add traffic/uptime module declarations and route registrations - Update include_str! from admin-ui/index.html to admin-ui/dist/index.html - Spawn health checker after SharedState construction with backend URL snapshot - Fix started_at field in cost/mod.rs test SharedState construction - Unignore dist/ in .gitignore so compiled-in SPA works without a build step Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 3 +- crates/proxy/admin-ui/dist/index.html | 61 +++++++++++++++++++++++++++ crates/proxy/src/admin/routes/mod.rs | 6 ++- crates/proxy/src/cost/mod.rs | 1 + crates/proxy/src/main.rs | 11 +++++ 5 files changed, 80 insertions(+), 2 deletions(-) create mode 100644 crates/proxy/admin-ui/dist/index.html diff --git a/.gitignore b/.gitignore index 8086652..c42a4ff 100644 --- a/.gitignore +++ b/.gitignore @@ -43,4 +43,5 @@ scripts/.venv/ # Admin UI build artifacts crates/proxy/admin-ui/node_modules/ -crates/proxy/admin-ui/dist/ +# dist/ is committed so `include_str!` works at compile time without a build step +# crates/proxy/admin-ui/dist/ diff --git a/crates/proxy/admin-ui/dist/index.html b/crates/proxy/admin-ui/dist/index.html new file mode 100644 index 0000000..2f6681c --- /dev/null +++ b/crates/proxy/admin-ui/dist/index.html @@ -0,0 +1,61 @@ + + + + + + Proxy Admin + + + + +
+ + diff --git a/crates/proxy/src/admin/routes/mod.rs b/crates/proxy/src/admin/routes/mod.rs index fa4d699..4f62393 100644 --- a/crates/proxy/src/admin/routes/mod.rs +++ b/crates/proxy/src/admin/routes/mod.rs @@ -6,6 +6,8 @@ pub mod keys; pub mod logs; pub mod mcp; pub mod models; +pub mod traffic; +pub mod uptime; use crate::admin::auth::{ extract_csrf_cookie, generate_csrf_token, validate_admin_token, validate_csrf_tokens, @@ -401,6 +403,8 @@ pub fn admin_router(shared: SharedState, token: Arc>) "/admin/api/mcp-servers/{name}", delete(mcp::remove_mcp_server), ) + .route("/admin/api/traffic", get(traffic::get_traffic)) + .route("/admin/api/uptime", get(uptime::get_uptime)) .with_state(shared.clone()) // Innermost: CSRF check runs after auth succeeds. .layer(middleware::from_fn_with_state( @@ -440,7 +444,7 @@ async fn health() -> Json { } /// Serve the embedded SPA HTML with a per-request CSP nonce. -static SPA_HTML: &str = include_str!("../../../admin-ui/index.html"); +static SPA_HTML: &str = include_str!("../../../admin-ui/dist/index.html"); async fn serve_spa() -> axum::response::Response { // Generate a per-request nonce (128-bit, base64url-encoded). diff --git a/crates/proxy/src/cost/mod.rs b/crates/proxy/src/cost/mod.rs index 0014984..f25f97a 100644 --- a/crates/proxy/src/cost/mod.rs +++ b/crates/proxy/src/cost/mod.rs @@ -441,6 +441,7 @@ mod tests { .time_to_live(std::time::Duration::from_secs(86400)) .build(), ), + started_at: std::time::SystemTime::now(), }; let vk_ctx = VirtualKeyContext { diff --git a/crates/proxy/src/main.rs b/crates/proxy/src/main.rs index e7c3c31..b818631 100644 --- a/crates/proxy/src/main.rs +++ b/crates/proxy/src/main.rs @@ -497,6 +497,7 @@ async fn async_main(args: Vec) { .time_to_live(std::time::Duration::from_secs(86400)) .build(), ), + started_at: std::time::SystemTime::now(), }; // Admin token: use env var or generate 256-bit random hex written to a file. @@ -597,6 +598,16 @@ async fn async_main(args: Vec) { } }); + // Spawn background health checker with a snapshot of backend base URLs. + // base_url lives in BackendConfig (not RuntimeConfig), so we snapshot + // it here once at startup rather than storing it in SharedState. + let backend_urls: Vec<(String, String)> = multi_config + .backends + .iter() + .map(|(name, bc)| (name.clone(), bc.base_url.clone())) + .collect(); + admin::health_check::spawn(shared.clone(), backend_urls); + // Bind admin listener; spawned after the shutdown channel is created below. let admin_app = admin::routes::admin_router(shared.clone(), admin_token); let admin_bind = std::env::var("ADMIN_BIND").unwrap_or_else(|_| "127.0.0.1".into());