From b67edf5a3275d5a601d5cde3f014925872d653e6 Mon Sep 17 00:00:00 2001 From: NetworkCats Date: Wed, 4 Mar 2026 05:12:45 +0800 Subject: [PATCH] feat: generate OpenAPI server URL from SITE_DOMAIN at startup --- openapi.json | 11 +---------- src/handlers.rs | 16 ++++++++++++++-- src/main.rs | 6 +++++- tests/integration.rs | 7 +++++-- 4 files changed, 25 insertions(+), 15 deletions(-) diff --git a/openapi.json b/openapi.json index 0f010e8..e5c5b8a 100644 --- a/openapi.json +++ b/openapi.json @@ -8,16 +8,7 @@ "name": "MIT" } }, - "servers": [ - { - "url": "https://{domain}", - "variables": { - "domain": { - "default": "localhost:3000" - } - } - } - ], + "servers": [], "paths": { "/json": { "get": { diff --git a/src/handlers.rs b/src/handlers.rs index 054cf35..0ee50d6 100644 --- a/src/handlers.rs +++ b/src/handlers.rs @@ -24,6 +24,7 @@ pub struct AppState { pub db: SharedDb, pub site_domain: Arc, pub dev_mode: bool, + pub openapi_json: Arc, } #[derive(Template)] @@ -329,14 +330,25 @@ pub async fn tor_handler( Ok(format!("{}\n", proxy.is_tor)) } -pub async fn openapi_json() -> Response { +pub async fn openapi_json(State(state): State) -> Response { ( [(header::CONTENT_TYPE, "application/json; charset=utf-8")], - include_str!("../openapi.json"), + String::from(state.openapi_json.as_ref()), ) .into_response() } +pub fn build_openapi_json(site_domain: &str) -> Arc { + let mut spec: serde_json::Value = + serde_json::from_str(include_str!("../openapi.json")).expect("openapi.json must be valid"); + + let server_url = format!("https://{site_domain}"); + spec["servers"] = serde_json::json!([{ "url": server_url }]); + + let json = serde_json::to_string_pretty(&spec).expect("serialization cannot fail"); + Arc::from(json) +} + pub async fn robots_txt(State(state): State) -> Response { let body = format!( "User-agent: *\n\ diff --git a/src/main.rs b/src/main.rs index fc6388b..9b19aa2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -43,10 +43,14 @@ async fn main() { cancel.clone(), ); + let site_domain: std::sync::Arc = config.site_domain.into(); + let openapi_json = handlers::build_openapi_json(&site_domain); + let state = handlers::AppState { db: shared_db, - site_domain: config.site_domain.into(), + site_domain, dev_mode: config.dev_mode, + openapi_json, }; let app = routes::build_router(state); diff --git a/tests/integration.rs b/tests/integration.rs index 530688f..1e264cb 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -5,7 +5,7 @@ use http_body_util::BodyExt; use tower::ServiceExt; use rustyip::db; -use rustyip::handlers::AppState; +use rustyip::handlers::{AppState, build_openapi_json}; use rustyip::routes::build_router; fn test_db_path() -> String { @@ -24,10 +24,13 @@ fn build_test_app() -> axum::Router { fn build_test_app_with_dev_mode(dev_mode: bool) -> axum::Router { let reader = db::load_db(Path::new(&test_db_path())).expect("failed to load test DB"); let shared_db = db::new_shared(reader); + let site_domain: std::sync::Arc = "test.example.com".into(); + let openapi_json = build_openapi_json(&site_domain); let state = AppState { db: shared_db, - site_domain: "test.example.com".into(), + site_domain, dev_mode, + openapi_json, }; build_router(state) }