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 let rate_config = &ctx.config().rate_limits;
30
31 if rate_config.disabled
32 && let Some(tx) = events
33 {
34 tx.warning("Rate limiting disabled - development mode only");
35 }
36
37 let router = configure_routes(ctx, events)?;
38 apply_global_middleware(router, ctx)
39}
40
41fn apply_global_middleware(router: Router, ctx: &AppContext) -> Result<Router> {
42 let mut router = router;
43
44 router = router.layer(DefaultBodyLimit::max(2 * 1024 * 1024));
45
46 let analytics_middleware = AnalyticsMiddleware::new(ctx)?;
47 router = router.layer(axum::middleware::from_fn({
48 let middleware = analytics_middleware;
49 move |req, next| {
50 let middleware = middleware.clone();
51 async move { middleware.track_request(req, next).await }
52 }
53 }));
54
55 let global_context_middleware = PublicContextMiddleware::new();
56 router = router.layer(axum::middleware::from_fn({
57 let middleware = global_context_middleware;
58 move |req, next| async move { middleware.handle(req, next).await }
59 }));
60
61 let session_middleware = SessionMiddleware::new(ctx)?;
62 router = router.layer(axum::middleware::from_fn({
63 let middleware = session_middleware;
64 move |req, next| {
65 let middleware = middleware.clone();
66 async move { middleware.handle(req, next).await }
67 }
68 }));
69
70 let cors = CorsMiddleware::build_layer(ctx.config())?;
71 router = router.layer(cors);
72
73 router = router.layer(axum::middleware::from_fn(remove_trailing_slash));
74
75 router = router.layer(axum::middleware::from_fn(inject_trace_header));
76
77 router = router.layer(axum::middleware::from_fn(inject_served_by));
78
79 if ctx.config().content_negotiation.enabled {
80 router = router.layer(axum::middleware::from_fn(
81 crate::services::middleware::content_negotiation_middleware,
82 ));
83 }
84
85 if ctx.config().security_headers.enabled {
86 let security_config = ctx.config().security_headers.clone();
87 router = router.layer(axum::middleware::from_fn(move |req, next| {
88 let config = security_config.clone();
89 inject_security_headers(config, req, next)
90 }));
91 }
92
93 Ok(router)
94}