use axum::http::{Response, StatusCode}; use utoipa::{Modify, OpenApi}; use utoipa_scalar::{Scalar, Servable}; use once_cell::sync::Lazy; use crate::{health, router::API_PREFIX}; /// Root OpenAPI document that only carries the health endpoints. /// /// The per-domain documents are merged at runtime by [`openapi`] so that every /// path keeps its absolute form (e.g. `/issues/{channel_id}`) before the /// unified [`API_PREFIX`] is applied. #[derive(OpenApi)] #[openapi( info( title = "Untitled API", description = "Unified HTTP API for the untitled platform", version = "1.94.0" ), paths(health::liveness, health::readiness) )] struct WebapiApiDoc; /// Build the merged OpenAPI document with every path prefixed by [`API_PREFIX`]. pub fn openapi() -> utoipa::openapi::OpenApi { let mut doc = export::openapi_concat!( WebapiApiDoc, auth::handler::AuthApiDoc, issues::IssuesApiDoc, project::ProjectApiDoc, repo::handler::openapi::ApiDoc, space::SpaceApiDoc, user::UserApiDoc ); export::openapi::PrefixModifier { prefix: API_PREFIX.to_owned(), } .modify(&mut doc); doc } static DOCS_HTML: Lazy = Lazy::new(|| { Scalar::with_url("/api-docs", openapi()).to_html() }); pub async fn docs() -> Response { Response::builder() .status(StatusCode::OK) .header("Content-Type", "text/html") .body(DOCS_HTML.clone()) .expect("Failed to build response") }