feat: Add frontend routing and asset handling with Axum
This commit is contained in:
@@ -14,6 +14,7 @@ mod cli;
|
|||||||
mod config;
|
mod config;
|
||||||
mod connector;
|
mod connector;
|
||||||
mod db;
|
mod db;
|
||||||
|
mod routes;
|
||||||
mod service;
|
mod service;
|
||||||
|
|
||||||
#[tokio::main]
|
#[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