diff --git a/src/lib/web/mod.rs b/src/lib/web/mod.rs index 65a6640..cf07531 100644 --- a/src/lib/web/mod.rs +++ b/src/lib/web/mod.rs @@ -1,24 +1,14 @@ -mod endpoints; +pub mod routes; use std::net::SocketAddr; -use axum::{extract::ws::Message, http::StatusCode, routing::get, Router}; -use tokio::{ - signal, - sync::{broadcast, mpsc, RwLock}, -}; +use axum::{extract::Request, ServiceExt}; +use tokio::signal; +use tower::Layer; +use tower_http::normalize_path::NormalizePathLayer; use tracing::*; -fn default_router() -> Router { - Router::new() - .route("/", get(endpoints::root)) - .route("/:path", get(endpoints::root)) - .fallback(get(|| async { (StatusCode::NOT_FOUND, "Not found :(") })) -} - pub async fn run(address: std::net::SocketAddrV4) { - let router = default_router(); - let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(1)); let mut first = true; loop { @@ -36,16 +26,14 @@ pub async fn run(address: std::net::SocketAddrV4) { } }; + let app = NormalizePathLayer::trim_trailing_slash().layer(routes::router()); + let service = ServiceExt::::into_make_service_with_connect_info::(app); + info!("Running web server on address {address:?}"); - if let Err(error) = axum::serve( - listener, - router - .clone() - .into_make_service_with_connect_info::(), - ) - .with_graceful_shutdown(shutdown_signal()) - .await + if let Err(error) = axum::serve(listener, service) + .with_graceful_shutdown(shutdown_signal()) + .await { error!("WebServer error: {error}"); continue; diff --git a/src/lib/web/endpoints.rs b/src/lib/web/routes/mod.rs similarity index 50% rename from src/lib/web/endpoints.rs rename to src/lib/web/routes/mod.rs index d17a73b..4850fea 100644 --- a/src/lib/web/endpoints.rs +++ b/src/lib/web/routes/mod.rs @@ -1,14 +1,39 @@ +pub mod v1; + use axum::{ extract::Path, http::{header, StatusCode}, response::IntoResponse, + routing::get, + Router, }; use include_dir::{include_dir, Dir}; use mime_guess::from_path; +use tower_http::{cors::CorsLayer, trace::TraceLayer}; +use tracing::*; + +use crate::cli; static HTML_DIST: Dir = include_dir!("src/webpage/dist"); -pub async fn root(filename: Option>) -> impl IntoResponse { +#[instrument(level = "trace")] +pub fn router() -> Router { + let app = Router::new() + .route_service("/", get(root)) + .route("/:path", get(root)) + .nest("/v1", v1::router()) + .fallback(handle_404()) + .layer(CorsLayer::permissive()) + .layer(TraceLayer::new_for_http()); + + match cli::default_api_version() { + 1 => app.merge(v1::router()), + _ => unimplemented!(), + } +} + +#[instrument(level = "trace")] +async fn root(filename: Option>) -> impl IntoResponse { let filename = filename .map(|Path(name)| { if name.is_empty() { @@ -20,10 +45,7 @@ pub async fn root(filename: Option>) -> impl IntoResponse { .unwrap_or_else(|| "index.html".into()); HTML_DIST.get_file(&filename).map_or_else( - || { - // Return 404 Not Found if the file doesn't exist - (StatusCode::NOT_FOUND, "404 Not Found").into_response() - }, + || handle_404().into_response(), |file| { // Determine the MIME type based on the file extension let mime_type = from_path(&filename).first_or_octet_stream(); @@ -32,3 +54,7 @@ pub async fn root(filename: Option>) -> impl IntoResponse { }, ) } + +fn handle_404() -> (StatusCode, &'static str) { + (StatusCode::NOT_FOUND, "404 Not Found") +}