Docker / publish (push) Canceled after 0s
Release / crates-io (push) Canceled after 0s
Release / release (macos-14, aarch64-apple-darwin) (push) Canceled after 0s
Release / release (ubuntu-latest, x86_64-unknown-linux-gnu) (push) Canceled after 0s
Release / release (windows-latest, x86_64-pc-windows-msvc) (push) Canceled after 0s
The crates.io/release build shipped a STALE frontend (committed web/js was behind the working tree), so the admin console hung on load. Commit the current working web/js (app, api, dashboard, login, import view, i18n de/es/fr/tr/zh, css) and the backend WIP (import.rs, admin/assets.rs) so the released console matches the dev build. Verified: clean `cargo build --release`, empty-pool start, working dashboard served.
69 lines
2.4 KiB
Rust
69 lines
2.4 KiB
Rust
//! Embedded static assets for the admin panel (SPA). The `web/` directory is compiled
|
|
//! into the binary via `rust-embed`, so the gateway remains a single self-contained file.
|
|
//!
|
|
//! Served as the main router's fallback: everything under `/admin/*` not matched by
|
|
//! an explicit route is served as a file; an unknown path under `/admin` falls back to
|
|
//! `index.html` (the client-side hash router handles navigation).
|
|
|
|
use axum::{
|
|
http::{header, StatusCode, Uri},
|
|
response::{IntoResponse, Response},
|
|
};
|
|
use rust_embed::RustEmbed;
|
|
|
|
#[derive(RustEmbed)]
|
|
#[folder = "web/"]
|
|
struct WebAssets;
|
|
|
|
fn serve_file(path: &str) -> Response {
|
|
// The panel is rebuilt often (assets are embedded at compile time and served
|
|
// from memory), so tell the browser to always revalidate — otherwise a stale
|
|
// cached SPA hides freshly shipped fixes until a hard reload.
|
|
const NO_CACHE: &str = "no-store, must-revalidate";
|
|
match WebAssets::get(path) {
|
|
Some(content) => {
|
|
let mime = mime_guess::from_path(path).first_or_octet_stream();
|
|
(
|
|
[
|
|
(header::CONTENT_TYPE, mime.as_ref()),
|
|
(header::CACHE_CONTROL, NO_CACHE),
|
|
],
|
|
content.data.into_owned(),
|
|
)
|
|
.into_response()
|
|
}
|
|
None => {
|
|
// SPA: unknown path under /admin → serve index.html
|
|
match WebAssets::get("index.html") {
|
|
Some(content) => (
|
|
[
|
|
(header::CONTENT_TYPE, "text/html; charset=utf-8"),
|
|
(header::CACHE_CONTROL, NO_CACHE),
|
|
],
|
|
content.data.into_owned(),
|
|
)
|
|
.into_response(),
|
|
None => (StatusCode::NOT_FOUND, "not found").into_response(),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Application-wide fallback: serves `/admin/*`; everything else → 404 JSON.
|
|
pub async fn fallback(uri: Uri) -> Response {
|
|
let path = uri.path();
|
|
if let Some(rest) = path.strip_prefix("/admin") {
|
|
let rest = rest.trim_start_matches('/');
|
|
let file = if rest.is_empty() { "index.html" } else { rest };
|
|
serve_file(file)
|
|
} else {
|
|
(
|
|
StatusCode::NOT_FOUND,
|
|
axum::Json(serde_json::json!({
|
|
"error": { "message": "not found", "type": "not_found", "code": "not_found" }
|
|
})),
|
|
)
|
|
.into_response()
|
|
}
|
|
}
|