feat: Allow to host under subpath #145

This commit is contained in:
rustmailer
2026-03-12 01:55:14 +08:00
parent f3c46f97b9
commit 40eca89a75
13 changed files with 121 additions and 67 deletions
+30 -7
View File
@@ -37,8 +37,8 @@ use http::{HeaderValue, Method};
use poem::endpoint::EmbeddedFilesEndpoint;
use poem::listener::{Listener, TcpListener};
use poem::middleware::{CatchPanic, Compression, SetHeader};
use poem::{endpoint::EmbeddedFileEndpoint, middleware::Cors, EndpointExt, Route, Server};
use poem::{get, post};
use poem::{get, handler, post, IntoResponse};
use poem::{middleware::Cors, EndpointExt, Route, Server};
use public::oauth2::oauth2_callback;
use std::collections::HashSet;
use std::time::Duration;
@@ -115,7 +115,7 @@ pub async fn start_http_server() -> BichonResult<()> {
)
};
let route = Route::new()
let app_logic = Route::new()
.nest("/api-docs/swagger", swagger)
.nest("/api-docs/redoc", redoc)
.nest("/api-docs/explorer", openapi_explorer)
@@ -130,10 +130,10 @@ pub async fn start_http_server() -> BichonResult<()> {
"/assets",
EmbeddedFilesEndpoint::<FrontEndAssets>::new().with(cache_static()),
)
.at(
"/*",
EmbeddedFileEndpoint::<FrontEndAssets>::new("index.html"),
)
.at("/*", serve_index_with_base);
let route = Route::new()
.nest(&SETTINGS.bichon_base_url, app_logic)
.with(cors)
.with_if(SETTINGS.bichon_http_compression_enabled, Compression::new())
.with(CatchPanic::new());
@@ -158,3 +158,26 @@ pub async fn start_http_server() -> BichonResult<()> {
.await
.map_err(|e| raise_error!(format!("{:#?}", e), ErrorCode::InternalError))
}
#[handler]
async fn serve_index_with_base() -> impl IntoResponse {
let mut html =
String::from_utf8_lossy(&FrontEndAssets::get("index.html").unwrap().data).to_string();
let raw_base = &SETTINGS.bichon_base_url;
let base_href = if raw_base.ends_with('/') {
raw_base.clone()
} else {
format!("{}/", raw_base)
};
let inject_content = format!(
r#"<base href="{}"><script>window.__BICHON_BASE__ = '{}';</script>"#,
base_href, raw_base
);
html = html.replace("<head>", &format!("<head>{}", inject_content));
poem::Response::builder()
.content_type("text/html; charset=utf-8")
.body(html)
}
+22
View File
@@ -74,6 +74,16 @@ pub struct Settings {
)]
pub bichon_public_url: String,
/// bichon base URL path (default: "/")
#[clap(
long,
default_value = "/",
env,
help = "Set the base UI path for bichon (e.g., '/bichon' or '/bichon/'). Must start with /",
value_parser = validate_base_url
)]
pub bichon_base_url: String,
/// CORS allowed origins (default: "*")
#[clap(
long,
@@ -381,6 +391,18 @@ impl Settings {
}
}
fn validate_base_url(s: &str) -> Result<String, String> {
if s == "/" {
return Ok(s.to_string());
}
if !s.starts_with('/') {
return Err(String::from(
"Base URL must start with '/' (e.g., '/bichon')",
));
}
Ok(s.to_string())
}
#[derive(Clone, Copy, Debug, PartialEq, ValueEnum)]
pub enum CompressionAlgorithm {
#[clap(name = "none")]