1mod routes;
7mod state;
8
9use std::net::SocketAddr;
10
11use anyhow::Result;
12use axum::routing::{get, post};
13use tower_http::cors::CorsLayer;
14
15pub use state::ServerState;
16
17pub async fn run(
23 config_toml: String,
24 secrets: std::collections::HashMap<String, String>,
25 build_info: trustee_core::types::BuildInfo,
26 addr: SocketAddr,
27) -> Result<()> {
28 let (mut session, workflow_rx) = trustee_core::session::Session::new();
30 session.config_toml = Some(config_toml);
31 session.secrets = Some(secrets);
32 session.build_info = Some(build_info);
33 session.parse_auto_handoff_config();
34
35 let (ws_tx, _ws_rx) = tokio::sync::broadcast::channel::<String>(256);
37
38 let state = ServerState::new(session, ws_tx);
40
41 state.clone().spawn_drain_task(workflow_rx);
43
44 let app = axum::Router::new()
46 .route("/api/v1/health", get(routes::health))
48 .route("/api/v1/session", get(routes::get_session))
49 .route("/api/v1/session/command", post(routes::post_command))
50 .route("/api/v1/session/cancel", post(routes::post_cancel))
51 .route("/api/v1/session/handoff", post(routes::post_handoff))
52 .route("/api/v1/session/stream", get(routes::ws_handler))
53 .route("/", get(routes::serve_index))
55 .route("/{file}", get(routes::serve_static))
56 .layer(CorsLayer::permissive())
57 .layer(axum::extract::DefaultBodyLimit::max(10 * 1024 * 1024))
58 .with_state(state);
59
60 let listener = tokio::net::TcpListener::bind(addr).await?;
62 tracing::info!("Trustee API listening on http://{}", addr);
63 axum::serve(listener, app).await?;
64
65 Ok(())
66}