Skip to main content

systemprompt_api/services/static_content/
fallback.rs

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