mirror of
https://github.com/Xevion/banner.git
synced 2025-12-06 15:14:25 -06:00
fix: simplify asset serving, use fallback primarily
This commit is contained in:
@@ -121,7 +121,9 @@ fn determine_enabled_services(args: &Args) -> Result<Vec<ServiceName>, anyhow::E
|
|||||||
}
|
}
|
||||||
(Some(_), Some(_)) => {
|
(Some(_), Some(_)) => {
|
||||||
// This should be prevented by clap's conflicts_with, but just in case
|
// This should be prevented by clap's conflicts_with, but just in case
|
||||||
Err(anyhow::anyhow!("Cannot specify both --services and --disable-services"))
|
Err(anyhow::anyhow!(
|
||||||
|
"Cannot specify both --services and --disable-services"
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use axum::{
|
|||||||
response::{Html, IntoResponse, Response},
|
response::{Html, IntoResponse, Response},
|
||||||
};
|
};
|
||||||
use rust_embed::RustEmbed;
|
use rust_embed::RustEmbed;
|
||||||
|
use tracing::debug;
|
||||||
|
|
||||||
/// Embedded web assets from the dist directory
|
/// Embedded web assets from the dist directory
|
||||||
#[derive(RustEmbed)]
|
#[derive(RustEmbed)]
|
||||||
@@ -17,36 +18,6 @@ use rust_embed::RustEmbed;
|
|||||||
#[exclude = "*.map"]
|
#[exclude = "*.map"]
|
||||||
pub struct WebAssets;
|
pub struct WebAssets;
|
||||||
|
|
||||||
/// Serve embedded static assets
|
|
||||||
pub async fn serve_asset(Path(path): Path<String>) -> Response {
|
|
||||||
let path = path.trim_start_matches('/');
|
|
||||||
|
|
||||||
match WebAssets::get(path) {
|
|
||||||
Some(content) => {
|
|
||||||
let mime_type = mime_guess::from_path(path).first_or_text_plain();
|
|
||||||
let data = content.data.to_vec();
|
|
||||||
([(header::CONTENT_TYPE, mime_type.as_ref())], data).into_response()
|
|
||||||
}
|
|
||||||
None => (StatusCode::NOT_FOUND, "Asset not found").into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Serve the main SPA index.html for client-side routing
|
|
||||||
pub async fn serve_spa_index() -> Response {
|
|
||||||
match WebAssets::get("index.html") {
|
|
||||||
Some(content) => {
|
|
||||||
let data = content.data.to_vec();
|
|
||||||
let html_content = String::from_utf8_lossy(&data).to_string();
|
|
||||||
Html(html_content).into_response()
|
|
||||||
}
|
|
||||||
None => (
|
|
||||||
StatusCode::INTERNAL_SERVER_ERROR,
|
|
||||||
"Failed to load index.html",
|
|
||||||
)
|
|
||||||
.into_response(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ASSET_EXTENSIONS: &[&str] = &[
|
const ASSET_EXTENSIONS: &[&str] = &[
|
||||||
"js", "css", "png", "jpg", "jpeg", "gif", "svg", "ico", "woff", "woff2", "ttf", "eot",
|
"js", "css", "png", "jpg", "jpeg", "gif", "svg", "ico", "woff", "woff2", "ttf", "eot",
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -4,18 +4,19 @@ use axum::{
|
|||||||
Router,
|
Router,
|
||||||
extract::State,
|
extract::State,
|
||||||
http::{StatusCode, Uri},
|
http::{StatusCode, Uri},
|
||||||
response::{IntoResponse, Json, Response},
|
response::{Html, IntoResponse, Json, Response},
|
||||||
routing::{any, get},
|
routing::get,
|
||||||
};
|
};
|
||||||
|
use http::header;
|
||||||
use serde_json::{Value, json};
|
use serde_json::{Value, json};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tower_http::{
|
use tower_http::{
|
||||||
cors::{Any, CorsLayer},
|
cors::{Any, CorsLayer},
|
||||||
trace::TraceLayer,
|
trace::TraceLayer,
|
||||||
};
|
};
|
||||||
use tracing::info;
|
use tracing::{debug, info};
|
||||||
|
|
||||||
use crate::web::assets::{is_asset_path, serve_asset, serve_spa_index};
|
use crate::web::assets::WebAssets;
|
||||||
|
|
||||||
use crate::banner::BannerApi;
|
use crate::banner::BannerApi;
|
||||||
|
|
||||||
@@ -50,7 +51,6 @@ pub fn create_router(state: BannerState) -> Router {
|
|||||||
Router::new()
|
Router::new()
|
||||||
.route("/", get(root))
|
.route("/", get(root))
|
||||||
.nest("/api", api_router)
|
.nest("/api", api_router)
|
||||||
.route("/assets/{*path}", any(serve_asset))
|
|
||||||
.fallback(handle_spa_fallback)
|
.fallback(handle_spa_fallback)
|
||||||
.layer(TraceLayer::new_for_http())
|
.layer(TraceLayer::new_for_http())
|
||||||
}
|
}
|
||||||
@@ -79,20 +79,32 @@ async fn root() -> Response {
|
|||||||
|
|
||||||
/// Handles SPA routing by serving index.html for non-API, non-asset requests
|
/// Handles SPA routing by serving index.html for non-API, non-asset requests
|
||||||
async fn handle_spa_fallback(uri: Uri) -> Response {
|
async fn handle_spa_fallback(uri: Uri) -> Response {
|
||||||
let path = uri.path();
|
let path = uri.path().trim_start_matches('/');
|
||||||
|
|
||||||
// Don't serve index.html for API routes or asset requests
|
if let Some(content) = WebAssets::get(path) {
|
||||||
if path.starts_with("/api/") || is_asset_path(path) {
|
let mime_type = mime_guess::from_path(path).first_or_text_plain();
|
||||||
return (StatusCode::NOT_FOUND, "Not Found").into_response();
|
let data = content.data.to_vec();
|
||||||
|
return ([(header::CONTENT_TYPE, mime_type.as_ref())], data).into_response();
|
||||||
|
} else {
|
||||||
|
// Any assets that are not found should be treated as a 404, not falling back to the SPA index.html
|
||||||
|
if path.starts_with("assets/") {
|
||||||
|
return (StatusCode::NOT_FOUND, "Asset not found").into_response();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In production, serve embedded index.html for SPA routing
|
// Fall back to the SPA index.html
|
||||||
if cfg!(not(debug_assertions)) {
|
match WebAssets::get("index.html") {
|
||||||
return serve_spa_index().await;
|
Some(content) => {
|
||||||
|
let data = content.data.to_vec();
|
||||||
|
let html_content = String::from_utf8_lossy(&data).to_string();
|
||||||
|
Html(html_content).into_response()
|
||||||
|
}
|
||||||
|
None => (
|
||||||
|
StatusCode::INTERNAL_SERVER_ERROR,
|
||||||
|
"Failed to load index.html",
|
||||||
|
)
|
||||||
|
.into_response(),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Development fallback (shouldn't reach here in production)
|
|
||||||
(StatusCode::NOT_FOUND, "Not Found").into_response()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Health check endpoint
|
/// Health check endpoint
|
||||||
|
|||||||
Reference in New Issue
Block a user