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