66 lines
1.9 KiB
Rust
66 lines
1.9 KiB
Rust
use anyhow::{Context, Result};
|
|
use axum::body::Body;
|
|
use axum::extract::Path;
|
|
use axum::http::{header, HeaderValue, StatusCode};
|
|
use axum::response::{IntoResponse, Response};
|
|
use axum::routing::get;
|
|
use axum::Router;
|
|
use mime_guess::from_path;
|
|
use rust_embed::RustEmbed;
|
|
|
|
#[derive(RustEmbed)]
|
|
#[folder = "../backchannel-web/dist"]
|
|
struct WebAssets;
|
|
|
|
pub async fn serve(bind_addr: &str) -> Result<()> {
|
|
let app = Router::new()
|
|
.route("/", get(index))
|
|
.route("/*path", get(asset_or_index));
|
|
|
|
let listener = tokio::net::TcpListener::bind(bind_addr)
|
|
.await
|
|
.with_context(|| format!("Failed to bind HTTP server to {bind_addr}"))?;
|
|
|
|
tracing::info!("BackChannel web UI listening on http://{bind_addr}");
|
|
axum::serve(listener, app).await.context("HTTP server failed")
|
|
}
|
|
|
|
async fn index() -> Response {
|
|
render_asset("index.html")
|
|
}
|
|
|
|
async fn asset_or_index(Path(path): Path<String>) -> Response {
|
|
if path.is_empty() {
|
|
return render_asset("index.html");
|
|
}
|
|
|
|
render_asset(&path)
|
|
}
|
|
|
|
fn render_asset(path: &str) -> Response {
|
|
let canonical = path.trim_start_matches('/');
|
|
|
|
if let Some(content) = WebAssets::get(canonical) {
|
|
return build_response(canonical, content.data.as_ref(), StatusCode::OK);
|
|
}
|
|
|
|
if let Some(index) = WebAssets::get("index.html") {
|
|
return build_response("index.html", index.data.as_ref(), StatusCode::OK);
|
|
}
|
|
|
|
(StatusCode::NOT_FOUND, "Web UI asset not found").into_response()
|
|
}
|
|
|
|
fn build_response(path: &str, body: &[u8], status: StatusCode) -> Response {
|
|
let mime = from_path(path).first_or_octet_stream();
|
|
|
|
let mut response = Response::new(Body::from(body.to_vec()));
|
|
*response.status_mut() = status;
|
|
response.headers_mut().insert(
|
|
header::CONTENT_TYPE,
|
|
HeaderValue::from_str(mime.as_ref()).unwrap_or(HeaderValue::from_static("application/octet-stream")),
|
|
);
|
|
|
|
response
|
|
}
|