ifc_lite_server/routes/
health.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Health check endpoint.
6
7use axum::Json;
8use serde::Serialize;
9
10/// Health check response.
11#[derive(Debug, Serialize)]
12pub struct HealthResponse {
13    pub status: &'static str,
14    pub version: &'static str,
15    pub service: &'static str,
16}
17
18/// API information response.
19#[derive(Debug, Serialize)]
20pub struct ApiInfoResponse {
21    pub service: &'static str,
22    pub version: &'static str,
23    pub description: &'static str,
24    pub endpoints: Vec<EndpointInfo>,
25}
26
27/// Endpoint information.
28#[derive(Debug, Serialize)]
29pub struct EndpointInfo {
30    pub method: &'static str,
31    pub path: &'static str,
32    pub description: &'static str,
33}
34
35/// GET /api/v1/health - Health check endpoint.
36pub async fn check() -> Json<HealthResponse> {
37    Json(HealthResponse {
38        status: "healthy",
39        version: env!("CARGO_PKG_VERSION"),
40        service: "ifc-lite-server",
41    })
42}
43
44/// GET /api/v1/ready - readiness probe: 503 while the admission RSS
45/// breaker is shedding new work, so an external load balancer can drain the
46/// instance. Deliberately separate from `/api/v1/health` (liveness), which
47/// Railway's healthcheck consumes - gating THAT on load would restart-loop
48/// the box exactly when it is busiest.
49pub async fn ready(
50    axum::extract::State(state): axum::extract::State<crate::AppState>,
51) -> axum::response::Response {
52    use axum::response::IntoResponse;
53    if state.admission.is_shedding() {
54        (
55            axum::http::StatusCode::SERVICE_UNAVAILABLE,
56            axum::Json(HealthResponse {
57                status: "shedding",
58                version: env!("CARGO_PKG_VERSION"),
59                service: "ifc-lite-server",
60            }),
61        )
62            .into_response()
63    } else {
64        axum::Json(HealthResponse {
65            status: "ready",
66            version: env!("CARGO_PKG_VERSION"),
67            service: "ifc-lite-server",
68        })
69        .into_response()
70    }
71}
72
73/// GET / - API information endpoint.
74pub async fn info() -> Json<ApiInfoResponse> {
75    Json(ApiInfoResponse {
76        service: "ifc-lite-server",
77        version: env!("CARGO_PKG_VERSION"),
78        description: "High-performance IFC processing server",
79        endpoints: vec![
80            EndpointInfo {
81                method: "GET",
82                path: "/api/v1/health",
83                description: "Health check endpoint",
84            },
85            EndpointInfo {
86                method: "POST",
87                path: "/api/v1/parse",
88                description: "Full parse with all geometry",
89            },
90            EndpointInfo {
91                method: "POST",
92                path: "/api/v1/parse/stream",
93                description: "Streaming parse (Server-Sent Events)",
94            },
95            EndpointInfo {
96                method: "POST",
97                path: "/api/v1/parse/metadata",
98                description: "Quick metadata extraction only",
99            },
100            EndpointInfo {
101                method: "GET",
102                path: "/api/v1/cache/:key",
103                description: "Retrieve cached result",
104            },
105        ],
106    })
107}
108
109#[cfg(test)]
110#[path = "health_tests.rs"]
111mod health_tests;