systemprompt_api/services/static_content/
fallback.rs1use axum::extract::State;
8use axum::http::{HeaderMap, Method, Uri};
9use axum::response::IntoResponse;
10use systemprompt_models::api::ApiError;
11use systemprompt_models::modules::ApiPaths;
12
13use super::static_files::StaticContentState;
14use crate::error::ApiHttpError;
15
16pub async fn smart_fallback_handler(
17 State(state): State<StaticContentState>,
18 uri: Uri,
19 method: Method,
20 headers: HeaderMap,
21 req_ctx: Option<axum::Extension<systemprompt_models::RequestContext>>,
22) -> impl IntoResponse {
23 let path = uri.path();
24
25 if is_api_path(path) {
26 return ApiHttpError::from(
27 ApiError::not_found(format!("No route matches {method} {path}"))
28 .with_path(path)
29 .with_details(get_api_suggestions(path).join("; ")),
30 )
31 .into_response();
32 }
33
34 super::serve_static_content(State(state), uri, headers, req_ctx)
35 .await
36 .into_response()
37}
38
39pub fn is_api_path(path: &str) -> bool {
40 path.starts_with(ApiPaths::API_BASE)
41 || path.starts_with(ApiPaths::WELLKNOWN_BASE)
42 || path.starts_with("/server/")
43 || path.starts_with("/mcp/")
44 || path.starts_with("/health")
45 || path.starts_with(ApiPaths::OPENAPI_BASE)
46 || path.starts_with(ApiPaths::DOCS_BASE)
47 || path.starts_with(ApiPaths::SWAGGER_BASE)
48 || path.starts_with("/v1/")
49 || path.starts_with("/auth/")
50 || path.starts_with("/oauth/")
51}
52
53pub fn get_api_suggestions(path: &str) -> Vec<String> {
54 if path.starts_with(ApiPaths::API_BASE) {
55 vec![
56 format!("{} - API discovery endpoint", ApiPaths::DISCOVERY),
57 format!("{} - Health check", ApiPaths::HEALTH),
58 format!(
59 "{} - Core services (contexts, tasks, artifacts)",
60 ApiPaths::CORE_BASE
61 ),
62 format!("{} - Agent registry", ApiPaths::AGENTS_REGISTRY),
63 format!("{} - MCP server registry", ApiPaths::MCP_REGISTRY),
64 ]
65 } else if path.starts_with(ApiPaths::WELLKNOWN_BASE) {
66 vec![
67 format!("{} - OAuth metadata", ApiPaths::WELLKNOWN_OAUTH_SERVER),
68 format!("{} - Agent card", ApiPaths::WELLKNOWN_AGENT_CARD),
69 ]
70 } else if path.contains("health") {
71 vec![format!("{} - Health check endpoint", ApiPaths::HEALTH)]
72 } else if path.contains("openapi") || path.contains("swagger") {
73 vec![format!(
74 "{} - API discovery (OpenAPI not yet available)",
75 ApiPaths::DISCOVERY
76 )]
77 } else {
78 vec![
79 format!("{} - Start here for API discovery", ApiPaths::DISCOVERY),
80 "/ - Frontend application".to_owned(),
81 ]
82 }
83}