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 <noreply@anthropic.com>
This commit is contained in:
whit3rabbit
2026-04-05 15:12:52 -05:00
co-authored by Claude Sonnet 4.6
parent f5fa86d3d5
commit 2de7f3e060
5 changed files with 80 additions and 2 deletions
+2 -1
View File
@@ -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/
File diff suppressed because one or more lines are too long
+5 -1
View File
@@ -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<zeroize::Zeroizing<String>>)
"/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<serde_json::Value> {
}
/// 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).
+1
View File
@@ -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 {
+11
View File
@@ -497,6 +497,7 @@ async fn async_main(args: Vec<String>) {
.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<String>) {
}
});
// 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());