pub mod auth;
mod routes;
mod state;
use std::net::SocketAddr;
use std::sync::Arc;
use anyhow::Result;
use axum::routing::{get, post};
use tower_http::cors::CorsLayer;
pub use auth::{AuthConfig, AuthState};
pub use state::ServerState;
pub async fn run(
config_toml: String,
secrets: std::collections::HashMap<String, String>,
build_info: trustee_core::types::BuildInfo,
addr: SocketAddr,
) -> Result<()> {
let auth_state = AuthConfig::from_toml(&config_toml).map(|cfg| {
let is_dev = cfg.dev_config.local_dev_mode;
tracing::info!(
"Auth enabled: {} mode, issuer={}",
if is_dev { "development" } else { "production" },
cfg.issuer_url
);
Arc::new(AuthState::new(cfg))
});
let (mut session, workflow_rx) = trustee_core::session::Session::new();
session.config_toml = Some(config_toml);
session.secrets = Some(secrets);
session.build_info = Some(build_info);
session.parse_auto_handoff_config();
let (ws_tx, _ws_rx) = tokio::sync::broadcast::channel::<String>(256);
let state = ServerState::new(session, ws_tx, auth_state);
state.clone().spawn_drain_task(workflow_rx);
let app = axum::Router::new()
.route("/api/v1/health", get(routes::health))
.nest("/auth", auth::auth_routes())
.route("/api/v1/session", get(routes::get_session))
.route("/api/v1/session/command", post(routes::post_command))
.route("/api/v1/session/cancel", post(routes::post_cancel))
.route("/api/v1/session/handoff", post(routes::post_handoff))
.route("/api/v1/session/stream", get(routes::ws_handler))
.route("/", get(routes::serve_index))
.route("/{file}", get(routes::serve_static))
.layer(CorsLayer::permissive())
.layer(axum::extract::DefaultBodyLimit::max(10 * 1024 * 1024))
.with_state(state);
let listener = tokio::net::TcpListener::bind(addr).await?;
tracing::info!("Trustee API listening on http://{}", addr);
axum::serve(listener, app).await?;
Ok(())
}