44use axum:: extract:: Request ;
55use axum:: middleware:: Next ;
66use axum:: response:: Response ;
7- use http:: { Method , StatusCode } ;
7+ use http:: uri:: PathAndQuery ;
8+ use http:: { Method , StatusCode , Uri } ;
89use std:: path:: Path ;
910use tower:: ServiceExt ;
1011use tower_http:: services:: ServeDir ;
1112
1213pub 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
1617pub 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