Skip to content

Commit 733a221

Browse files
committed
middleware: Serve svelte/build/ folder under /svelte path
1 parent 4df8014 commit 733a221

File tree

2 files changed

+40
-4
lines changed

2 files changed

+40
-4
lines changed

src/middleware.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,9 @@ pub fn apply_axum_middleware(state: AppState, router: Router<()>) -> Router {
7878
.layer(conditional_layer(config.serve_dist, || {
7979
from_fn(static_or_continue::serve_dist)
8080
}))
81+
.layer(conditional_layer(config.serve_dist, || {
82+
from_fn(static_or_continue::serve_svelte)
83+
}))
8184
.layer(conditional_layer(config.serve_html, || {
8285
from_fn_with_state(state.clone(), ember_html::serve_html)
8386
}))

src/middleware/static_or_continue.rs

Lines changed: 37 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,29 +4,47 @@
44
use axum::extract::Request;
55
use axum::middleware::Next;
66
use axum::response::Response;
7-
use http::{Method, StatusCode};
7+
use http::uri::PathAndQuery;
8+
use http::{Method, StatusCode, Uri};
89
use std::path::Path;
910
use tower::ServiceExt;
1011
use tower_http::services::ServeDir;
1112

1213
pub async fn serve_local_uploads(request: Request, next: Next) -> Response {
13-
serve("local_uploads", request, next).await
14+
serve("local_uploads", None, request, next).await
1415
}
1516

1617
pub async fn serve_dist(request: Request, next: Next) -> Response {
17-
serve("dist", request, next).await
18+
serve("dist", None, request, next).await
1819
}
1920

20-
async fn serve<P: AsRef<Path>>(path: P, request: Request, next: Next) -> Response {
21+
pub async fn serve_svelte(request: Request, next: Next) -> Response {
22+
serve("svelte/build", Some("/svelte"), request, next).await
23+
}
24+
25+
async fn serve<P: AsRef<Path>>(
26+
path: P,
27+
strip_prefix: Option<&str>,
28+
request: Request,
29+
next: Next,
30+
) -> Response {
2131
// index.html is a Jinja template, which is to be rendered by `ember_html::serve_html`.
2232
if matches!(*request.method(), Method::GET | Method::HEAD)
2333
&& !matches!(request.uri().path().as_bytes(), b"/" | b"/index.html")
34+
&& strip_prefix.is_none_or(|prefix| request.uri().path().starts_with(prefix))
2435
{
2536
let mut static_req = Request::new(());
2637
*static_req.method_mut() = request.method().clone();
2738
*static_req.uri_mut() = request.uri().clone();
2839
*static_req.headers_mut() = request.headers().clone();
2940

41+
if let Some(prefix) = strip_prefix
42+
&& let Some(new_path) = request.uri().path().strip_prefix(prefix)
43+
&& let Some(new_uri) = replace_uri_path(request.uri(), new_path)
44+
{
45+
*static_req.uri_mut() = new_uri;
46+
}
47+
3048
let serve_dir = ServeDir::new(path).precompressed_br().precompressed_gzip();
3149
let Ok(response) = serve_dir.oneshot(static_req).await;
3250
if response.status() != StatusCode::NOT_FOUND {
@@ -36,3 +54,18 @@ async fn serve<P: AsRef<Path>>(path: P, request: Request, next: Next) -> Respons
3654

3755
next.run(request).await
3856
}
57+
58+
/// Replaces the path component of a URI while preserving the query string.
59+
fn replace_uri_path(uri: &Uri, new_path: &str) -> Option<Uri> {
60+
let new_path = if new_path.is_empty() { "/" } else { new_path };
61+
62+
let new_path_and_query = match uri.query() {
63+
Some(query) => format!("{new_path}?{query}"),
64+
None => new_path.to_owned(),
65+
};
66+
67+
let path_and_query = PathAndQuery::try_from(new_path_and_query).ok()?;
68+
let mut parts = uri.clone().into_parts();
69+
parts.path_and_query = Some(path_and_query);
70+
Uri::from_parts(parts).ok()
71+
}

0 commit comments

Comments
 (0)