systemprompt_api/services/server/
builder.rs1use anyhow::Result;
11use axum::Router;
12use axum::extract::DefaultBodyLimit;
13use systemprompt_runtime::AppContext;
14use systemprompt_traits::{StartupEventExt, StartupEventSender};
15
16use super::routes::configure_routes;
17use crate::services::middleware::{
18 AnalyticsMiddleware, CorsMiddleware, PublicContextMiddleware, SessionMiddleware,
19 inject_security_headers, inject_served_by, inject_trace_header, remove_trailing_slash,
20};
21
22pub use super::discovery::*;
23pub use super::health::handle_health;
24
25pub fn setup_api_server(ctx: &AppContext, events: Option<&StartupEventSender>) -> Result<Router> {
26 let rate_config = &ctx.config().rate_limits;
27
28 if rate_config.disabled
29 && let Some(tx) = events
30 {
31 tx.warning("Rate limiting disabled - development mode only");
32 }
33
34 let router = configure_routes(ctx, events)?;
35 apply_global_middleware(router, ctx)
36}
37
38fn apply_global_middleware(router: Router, ctx: &AppContext) -> Result<Router> {
39 let mut router = router;
40
41 router = router.layer(DefaultBodyLimit::max(2 * 1024 * 1024));
42
43 let analytics_middleware = AnalyticsMiddleware::new(ctx)?;
44 router = router.layer(axum::middleware::from_fn({
45 let middleware = analytics_middleware;
46 move |req, next| {
47 let middleware = middleware.clone();
48 async move { middleware.track_request(req, next).await }
49 }
50 }));
51
52 let global_context_middleware = PublicContextMiddleware::new();
53 router = router.layer(axum::middleware::from_fn({
54 let middleware = global_context_middleware;
55 move |req, next| async move { middleware.handle(req, next).await }
56 }));
57
58 let session_middleware = SessionMiddleware::new(ctx)?;
59 router = router.layer(axum::middleware::from_fn({
60 let middleware = session_middleware;
61 move |req, next| {
62 let middleware = middleware.clone();
63 async move { middleware.handle(req, next).await }
64 }
65 }));
66
67 let cors = CorsMiddleware::build_layer(ctx.config())?;
68 router = router.layer(cors);
69
70 router = router.layer(axum::middleware::from_fn(remove_trailing_slash));
71
72 router = router.layer(axum::middleware::from_fn(inject_trace_header));
73
74 router = router.layer(axum::middleware::from_fn(inject_served_by));
75
76 if ctx.config().content_negotiation.enabled {
77 router = router.layer(axum::middleware::from_fn(
78 crate::services::middleware::content_negotiation_middleware,
79 ));
80 }
81
82 if ctx.config().security_headers.enabled {
83 let security_config = ctx.config().security_headers.clone();
84 router = router.layer(axum::middleware::from_fn(move |req, next| {
85 let config = security_config.clone();
86 inject_security_headers(config, req, next)
87 }));
88 }
89
90 Ok(router)
91}