feat: Add frontend routing and asset handling with Axum
This commit is contained in:
@@ -14,6 +14,7 @@ mod cli;
|
||||
mod config;
|
||||
mod connector;
|
||||
mod db;
|
||||
mod routes;
|
||||
mod service;
|
||||
|
||||
#[tokio::main]
|
||||
|
||||
71
apps/nxmesh-master/src/routes/frontend/mod.rs
Normal file
71
apps/nxmesh-master/src/routes/frontend/mod.rs
Normal file
@@ -0,0 +1,71 @@
|
||||
use axum::{Router, response::IntoResponse};
|
||||
use tracing::error;
|
||||
|
||||
// In development, build the frontend from the source directory, the soft link will handle the path resolution
|
||||
// In deployment, pre-build the frontend and replace the frontend-dist folder with the built assets, the rust-embed will handle the embedding and path resolution
|
||||
#[derive(rust_embed::Embed)]
|
||||
#[folder = "./frontend-dist/"]
|
||||
struct FrontendAssets;
|
||||
|
||||
const INDEX_HTML: &str = "index.html";
|
||||
|
||||
pub async fn get_router() -> Router {
|
||||
Router::new()
|
||||
.route(
|
||||
"/",
|
||||
axum::routing::get(get_fallback_handler)
|
||||
.head(get_fallback_handler)
|
||||
.options(get_fallback_handler),
|
||||
)
|
||||
.route(
|
||||
"/{*path}",
|
||||
axum::routing::get(get_file_handler)
|
||||
.head(get_file_handler)
|
||||
.options(get_file_handler),
|
||||
)
|
||||
//
|
||||
.fallback(get_fallback_handler().await)
|
||||
}
|
||||
|
||||
#[cfg_attr(debug_assertions, axum::debug_handler)]
|
||||
pub async fn get_fallback_handler() -> Result<axum::response::Html<Vec<u8>>, axum::http::StatusCode>
|
||||
{
|
||||
let index_html = get_index_html();
|
||||
match index_html {
|
||||
Some(html) => Ok(axum::response::Html(html)),
|
||||
None => Err(axum::http::StatusCode::NOT_FOUND),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_index_html() -> Option<Vec<u8>> {
|
||||
FrontendAssets::get(INDEX_HTML).map(|asset| asset.data.as_ref().to_owned())
|
||||
}
|
||||
|
||||
#[cfg_attr(debug_assertions, axum::debug_handler)]
|
||||
async fn get_file_handler(
|
||||
axum::extract::Path(path): axum::extract::Path<String>,
|
||||
) -> Result<axum::response::Response, axum::http::StatusCode> {
|
||||
let file_path = if path.is_empty() {
|
||||
INDEX_HTML.to_string()
|
||||
} else {
|
||||
path
|
||||
};
|
||||
|
||||
match FrontendAssets::get(&file_path) {
|
||||
Some(asset) => {
|
||||
let content_type = mime_guess::from_path(&file_path).first_or_octet_stream();
|
||||
let response = axum::response::Response::builder()
|
||||
.header(axum::http::header::CONTENT_TYPE, content_type.as_ref())
|
||||
.body(asset.data.into_owned().into())
|
||||
.map_err(|e| {
|
||||
error!("Failed to build response for {}: {}", file_path, e);
|
||||
axum::http::StatusCode::INTERNAL_SERVER_ERROR
|
||||
})?;
|
||||
Ok(response)
|
||||
}
|
||||
// return index.html for any file not found to support client-side routing in the frontend
|
||||
None => get_fallback_handler()
|
||||
.await
|
||||
.map(|html| html.into_response()),
|
||||
}
|
||||
}
|
||||
9
apps/nxmesh-master/src/routes/mod.rs
Normal file
9
apps/nxmesh-master/src/routes/mod.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
use axum::Router;
|
||||
|
||||
mod frontend;
|
||||
|
||||
pub async fn get_root_router() -> Router {
|
||||
Router::new()
|
||||
.merge(frontend::get_router().await)
|
||||
.fallback(frontend::get_fallback_handler().await)
|
||||
}
|
||||
Reference in New Issue
Block a user