fix: use relative Vite base; serve SPA at both /v2/ and /clawstor/

Vite base: '/clawstor/' caused 404s when the SPA was served at /v2/
because asset paths like /clawstor/assets/... didn't match the actual
serve path /v2/assets/...

- vite.config.ts: switch to base: './' (relative paths work from any
  mount point — /v2/, /clawstor/, or bare /)
- serve.rs: mount the SPA at both /v2/ and /clawstor/; add 308 redirect
  from /v2 and /clawstor (no trailing slash) to their canonical forms
  so browsers don't misinterpret the last segment as a filename and
  break relative ./assets/ resolution

Co-Authored-By: Claude Sonnet 4.6 <[email protected]>
This commit is contained in:
Omar Sobh
2026-07-23 19:45:55 +00:00
co-authored by Claude Sonnet 4.6
parent a8f0aa911c
commit 5001ac233c
3 changed files with 24 additions and 14 deletions
+17 -6
View File
@@ -719,12 +719,23 @@ pub fn build_app_with_v2(
v2_router = v2_router.merge(crate::serve_v2::build(v2s));
}
if let Some(dir) = v2_static_dir {
v2_router = v2_router.nest_service(
"/v2",
tower_http::services::ServeDir::new(&dir).fallback(
tower_http::services::ServeFile::new(dir.join("index.html")),
),
);
// Redirect bare paths (no trailing slash) to their canonical
// slash-terminated form. Without this, browsers treat the path
// segment as a file and resolve relative asset paths one level
// too high, producing 404s for `./assets/…`.
let redirect_v2 = get(|| async {
axum::response::Redirect::permanent("/v2/")
});
let redirect_clawstor = get(|| async {
axum::response::Redirect::permanent("/clawstor/")
});
let spa = tower_http::services::ServeDir::new(&dir)
.fallback(tower_http::services::ServeFile::new(dir.join("index.html")));
v2_router = v2_router
.route("/v2", redirect_v2)
.route("/clawstor", redirect_clawstor)
.nest_service("/v2/", spa.clone())
.nest_service("/clawstor/", spa);
}
Router::new()