systemprompt_api/services/server/
builder.rs1use anyhow::Result;
14use axum::Router;
15use axum::extract::DefaultBodyLimit;
16use systemprompt_runtime::AppContext;
17use systemprompt_traits::{StartupEventExt, StartupEventSender};
18
19use super::routes::configure_routes;
20use crate::services::middleware::{
21 AnalyticsMiddleware, CorsMiddleware, PublicContextMiddleware, SessionMiddleware,
22 inject_security_headers, inject_served_by, inject_trace_header, remove_trailing_slash,
23};
24
25pub use super::discovery::*;
26pub use super::health::handle_health;
27
28pub fn setup_api_server(ctx: &AppContext, events: Option<&StartupEventSender>) -> Result<Router> {
29 super::state_dirs::ensure_writable(&[ctx.app_paths().storage().data()])?;
30
31 let rate_config = &ctx.config().rate_limits;
32
33 if rate_config.disabled
34 && let Some(tx) = events
35 {
36 tx.warning("Rate limiting disabled - development mode only");
37 }
38
39 register_artifact_scanner(ctx);
40 let router = configure_routes(ctx, events)?;
41 apply_global_middleware(router, ctx)
42}
43
44fn register_artifact_scanner(ctx: &AppContext) {
48 let resolver = crate::services::gateway::policy::PolicyResolver::from_repository(
49 ctx.ai_repositories().gateway_policies.clone(),
50 );
51 ctx.artifact_ingest().register_scanner(std::sync::Arc::new(
52 crate::services::gateway::GatewayArtifactScanner::new(resolver),
53 ));
54}
55
56fn apply_global_middleware(router: Router, ctx: &AppContext) -> Result<Router> {
57 let mut router = router;
58
59 router = router.layer(DefaultBodyLimit::max(2 * 1024 * 1024));
60
61 let analytics_middleware = AnalyticsMiddleware::new(ctx)?;
62 router = router.layer(axum::middleware::from_fn({
63 let middleware = analytics_middleware;
64 move |req, next| {
65 let middleware = middleware.clone();
66 async move { middleware.track_request(req, next).await }
67 }
68 }));
69
70 let global_context_middleware = PublicContextMiddleware::new();
71 router = router.layer(axum::middleware::from_fn({
72 let middleware = global_context_middleware;
73 move |req, next| async move { middleware.seed(req, next).await }
74 }));
75
76 let session_middleware = SessionMiddleware::new(ctx);
77 router = router.layer(axum::middleware::from_fn({
78 let middleware = session_middleware;
79 move |req, next| {
80 let middleware = middleware.clone();
81 async move { middleware.handle(req, next).await }
82 }
83 }));
84
85 let cors = CorsMiddleware::build_layer(ctx.config())?;
86 router = router.layer(cors);
87
88 router = router.layer(axum::middleware::from_fn(remove_trailing_slash));
89
90 router = router.layer(axum::middleware::from_fn(inject_trace_header));
91
92 router = router.layer(axum::middleware::from_fn(inject_served_by));
93
94 if ctx.config().content_negotiation.enabled {
95 router = router.layer(axum::middleware::from_fn(
96 crate::services::middleware::content_negotiation_middleware,
97 ));
98 }
99
100 if ctx.config().security_headers.enabled {
101 let security_config = ctx.config().security_headers.clone();
102 router = router.layer(axum::middleware::from_fn(move |req, next| {
103 let config = security_config.clone();
104 inject_security_headers(config, req, next)
105 }));
106 }
107
108 Ok(router)
109}