use std::sync::Arc;
use axum::extract::Path;
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::Extension;
use axum::Json;
use utoipa_swagger_ui::{serve, Config};
pub fn swagger_ui_router() -> axum::Router {
let config: Arc<Config<'static>> =
Arc::new(Config::new(["/api-docs/openapi.json".to_string()]));
axum::Router::new()
.route("/api-docs/openapi.json", get(serve_openapi_json))
.route("/swagger-ui/", get(serve_swagger_ui))
.route("/swagger-ui/{*rest}", get(serve_swagger_ui))
.layer(Extension(config))
}
async fn serve_openapi_json() -> impl IntoResponse {
Json(crate::openapi::generate_openapi_spec())
}
async fn serve_swagger_ui(
path: Option<Path<String>>,
Extension(state): Extension<Arc<Config<'static>>>,
) -> impl IntoResponse {
let tail = path.as_ref().map(|p| p.as_str()).unwrap_or("");
if tail.contains("..") {
return StatusCode::BAD_REQUEST.into_response();
}
match serve(tail, state) {
Ok(Some(file)) => (
StatusCode::OK,
[("Content-Type", file.content_type)],
file.bytes.into_owned(),
)
.into_response(),
Ok(None) => StatusCode::NOT_FOUND.into_response(),
Err(error) => {
log::error!("Swagger UI serve failed: {}", error);
(StatusCode::INTERNAL_SERVER_ERROR, "internal server error").into_response()
}
}
}