From 5f35f1a2055c9d89ffa315f033b3b61db390681f Mon Sep 17 00:00:00 2001 From: Sebas Date: Tue, 23 Jun 2026 21:01:26 +0000 Subject: [PATCH] Stage 3: Admin-Router einbinden (AppState, /admin) --- crates/dtrack-http/src/main.rs | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/crates/dtrack-http/src/main.rs b/crates/dtrack-http/src/main.rs index 2fc6db1..fb9ff85 100644 --- a/crates/dtrack-http/src/main.rs +++ b/crates/dtrack-http/src/main.rs @@ -1,3 +1,5 @@ +mod admin; + use std::collections::HashSet; use std::sync::Arc; @@ -15,11 +17,12 @@ use dtrack_tools::{shared_client, DtrackServer}; use rmcp::transport::streamable_http_server::{ session::local::LocalSessionManager, StreamableHttpService, }; +use tokio::sync::RwLock; /// Bearer-Token-Gate fuer den /mcp-Endpoint. /// /// Stage 2: eine flache Token-Liste aus `DTRACK_HTTP_TOKENS` (kommagetrennt). -/// Multi-Client mit Rechten pro Token folgt in Stage 3 (Permission-Engine). +/// Rechte pro Token (Permission-Engine) folgen spaeter. #[derive(Clone)] struct AuthState { tokens: Arc>, @@ -48,11 +51,7 @@ impl AuthState { } } -async fn auth_mw( - State(auth): State, - req: Request, - next: Next, -) -> Result { +async fn auth_mw(State(auth): State, req: Request, next: Next) -> Result { if auth.allow_all { return Ok(next.run(req).await); } @@ -79,9 +78,17 @@ async fn main() -> Result<()> { let cfg = DtrackConfig::resolve()?; let client = DtrackClient::from_config(&cfg)?; - // Laufzeit-austauschbarer Client-Handle (Admin-UI kann ihn spaeter neu bauen). + let config_path = std::env::var("DTRACK_CONFIG").ok().map(std::path::PathBuf::from); + + // Laufzeit-austauschbarer Client-Handle (Admin-UI baut ihn live neu). let shared = shared_client(client); + let app_state = admin::AppState { + config: Arc::new(RwLock::new(cfg)), + client: shared.clone(), + config_path, + }; + // Pro Session ein frischer DtrackServer; der Handle wird nur geklont (Arc). let mcp_service = StreamableHttpService::new( move || Ok(DtrackServer::new(shared.clone())), @@ -94,15 +101,18 @@ async fn main() -> Result<()> { .nest_service("/mcp", mcp_service) .layer(middleware::from_fn_with_state(auth, auth_mw)); + let admin_router = admin::router(app_state, admin::AdminAuth::from_env()); + let app = Router::new() .route("/health", get(|| async { "OK" })) - .merge(mcp); + .merge(mcp) + .merge(admin_router); let addr = std::env::var("DTRACK_HTTP_ADDR").unwrap_or_else(|_| "0.0.0.0:8080".to_string()); let listener = tokio::net::TcpListener::bind(&addr) .await .with_context(|| format!("Bind auf {addr} fehlgeschlagen"))?; - tracing::info!("dtrack-http laeuft auf http://{addr}/mcp"); + tracing::info!("dtrack-http laeuft auf http://{addr}/mcp (Admin: /admin)"); axum::serve(listener, app) .with_graceful_shutdown(async {