use axum::{
body::Body,
extract::Path,
http::{StatusCode, header},
response::Response,
};
use rust_embed::RustEmbed;
use tracing::{info, warn};
#[derive(RustEmbed)]
#[folder = "assets/"]
#[include = "*.css"]
#[include = "*.svg"]
#[include = "*.ico"]
pub struct StaticAssets;
pub async fn serve_static_asset(Path(asset_path): Path<String>) -> Result<Response, StatusCode> {
info!("serving static asset: {}", asset_path);
let asset = StaticAssets::get(&asset_path).ok_or_else(|| {
warn!("static asset not found: {}", asset_path);
StatusCode::NOT_FOUND
})?;
let mime_type = get_asset_mime_type(&asset_path);
Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, mime_type)
.header(header::CACHE_CONTROL, "public, max-age=31536000") .header(header::CONTENT_LENGTH, asset.data.len())
.body(Body::from(asset.data.to_vec()))
.map_err(|e| {
warn!("failed to build asset response: {}", e);
StatusCode::INTERNAL_SERVER_ERROR
})
}
pub async fn serve_embedded_favicon() -> Result<Response, StatusCode> {
info!("serving embedded favicon.ico");
let asset = StaticAssets::get("favicon.ico").ok_or_else(|| {
warn!("embedded favicon.ico not found");
StatusCode::NOT_FOUND
})?;
Response::builder()
.status(StatusCode::OK)
.header(header::CONTENT_TYPE, "image/x-icon")
.header(header::CACHE_CONTROL, "public, max-age=31536000")
.header(header::CONTENT_LENGTH, asset.data.len())
.body(Body::from(asset.data.to_vec()))
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)
}
fn get_asset_mime_type(file_path: &str) -> &'static str {
if file_path.ends_with(".css") {
"text/css"
} else if file_path.ends_with(".svg") {
"image/svg+xml"
} else if file_path.ends_with(".ico") {
"image/x-icon"
} else if file_path.ends_with(".png") {
"image/png"
} else if file_path.ends_with(".jpg") || file_path.ends_with(".jpeg") {
"image/jpeg"
} else {
"application/octet-stream"
}
}