From 52dee3eee487fc8ee57bb73319934d705e48579a Mon Sep 17 00:00:00 2001 From: Ryan Walters Date: Sun, 2 Nov 2025 22:51:17 -0600 Subject: [PATCH] feat(server): add trailing slash normalization and API root endpoint - Add tower-http normalize-path feature to handle trailing slashes - Implement NormalizePathLayer to trim trailing slashes from URLs - Add GET /api/ endpoint with API description message - Fix OAuth callback redirect to use /api/profile path --- pacman-server/Cargo.toml | 2 +- pacman-server/src/app.rs | 10 ++++++++-- pacman-server/src/routes.rs | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/pacman-server/Cargo.toml b/pacman-server/Cargo.toml index d34bcab..3857e66 100644 --- a/pacman-server/Cargo.toml +++ b/pacman-server/Cargo.toml @@ -44,7 +44,7 @@ jsonwebtoken = { version = "9.3", default-features = false } tracing = "0.1.41" tracing-subscriber = { version = "0.3.20", features = ["env-filter", "json"] } tracing-futures = { version = "0.2.5", features = ["tokio"] } -tower-http = { version = "0.6", features = ["trace", "fs"] } +tower-http = { version = "0.6", features = ["trace", "fs", "normalize-path"] } time = { version = "0.3", features = ["macros", "formatting"] } yansi = "1" s3-tokio = { version = "0.39.6", default-features = false } diff --git a/pacman-server/src/app.rs b/pacman-server/src/app.rs index 139d8bb..e0422b5 100644 --- a/pacman-server/src/app.rs +++ b/pacman-server/src/app.rs @@ -7,6 +7,7 @@ use std::sync::Arc; use std::time::Duration; use tokio::sync::{Notify, RwLock}; use tokio::task::JoinHandle; +use tower_http::normalize_path::NormalizePathLayer; use tower_http::services::{ServeDir, ServeFile}; use tracing::info_span; @@ -191,7 +192,12 @@ pub fn create_router(app_state: AppState) -> Router { .layer(axum::middleware::from_fn(inject_server_header)); // Create main router with API routes nested under /api - let router = Router::new().nest("/api", api_router); + let router = Router::new() + .route( + "/api/", + get(|| async { "Pac-Man API Server. Visit /api/auth/github to start OAuth flow." }), + ) + .nest("/api", api_router); // Add static file serving if the directory exists let router = if static_path.exists() { @@ -203,7 +209,7 @@ pub fn create_router(app_state: AppState) -> Router { }; // Add tracing layer to the entire router - router.layer( + router.layer(NormalizePathLayer::trim_trailing_slash()).layer( tower_http::trace::TraceLayer::new_for_http() .make_span_with(make_span) .on_request(|_request: &axum::http::Request, _span: &tracing::Span| { diff --git a/pacman-server/src/routes.rs b/pacman-server/src/routes.rs index a836802..4c4df66 100644 --- a/pacman-server/src/routes.rs +++ b/pacman-server/src/routes.rs @@ -175,7 +175,7 @@ pub async fn oauth_callback_handler( }); } - (StatusCode::FOUND, Redirect::to("/profile")).into_response() + (StatusCode::FOUND, Redirect::to("/api/profile")).into_response() } /// Handles the request to the profile endpoint.