From 85de86f835dd79055bde33ff3104e3e48f7ad9bd Mon Sep 17 00:00:00 2001 From: Andrew nuark G Date: Mon, 5 May 2025 23:58:18 +0700 Subject: [PATCH] fix: ensure domain-based index rewriting works for SPAs Previously, SPA routes (e.g. `/dashboard`) bypassed custom index rewriting logic due to `actix_files` intercepting requests before the fallback handler. This hotfix reorders route registration and introduces explicit handling for unmatched paths and root (/) to support React-style routing with domain-specific index.html rewrites. --- Cargo.lock | 2 +- Cargo.toml | 2 +- src/main.rs | 90 +++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 65 insertions(+), 29 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 20d199d..4ca35e2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1267,7 +1267,7 @@ dependencies = [ [[package]] name = "servant" -version = "0.2.0" +version = "0.2.1" dependencies = [ "actix-files", "actix-web", diff --git a/Cargo.toml b/Cargo.toml index fb657f0..49e9e48 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "servant" -version = "0.2.0" +version = "0.2.1" edition = "2021" authors = ["Andrew G. "] diff --git a/src/main.rs b/src/main.rs index d68a5a0..9481b68 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,9 +1,8 @@ -use std::path::Path; use std::collections::HashMap; use actix_files; use actix_web::{ - dev::{ServiceRequest, ServiceResponse}, middleware::Logger, App, HttpServer + dev::{ServiceRequest, ServiceResponse}, middleware::Logger, web, App, HttpRequest, HttpServer }; use clap::{arg, command, Parser}; use env_logger::Env; @@ -44,13 +43,18 @@ async fn main() -> std::io::Result<()> { let args = ServantArgs::parse(); - let bind_addr = format!("{}:{}", args.host, args.port); - println!("Listening on http://{}", bind_addr); + // Log configured rewrite rules + if !args.rewrite_index.is_empty() { + log::info!("Configured domain-to-index mappings:"); + for (domain, path) in &args.rewrite_index { + log::info!(" {} => {}", domain, path); + } + } else { + log::info!("No domain-to-index rewrite rules configured."); + } - let index_path = Path::new(args.serve_dir.as_str()) - .join(args.index_file.as_str()) - .display() - .to_string(); + let bind_addr = format!("{}:{}", args.host, args.port); + log::info!("Listening on http://{}", bind_addr); let mount_point = args.mount.clone(); let serve_dir = args.serve_dir.clone(); @@ -68,29 +72,61 @@ async fn main() -> std::io::Result<()> { App::new() .wrap(Logger::default()) + .route("/", web::get().to({ + let index_map = index_map.clone(); + let default_index = default_index.clone(); + let serve_dir = serve_dir.clone(); + move |req: HttpRequest| { + let host = req + .headers() + .get("Host") + .and_then(|h| h.to_str().ok()) + .unwrap_or_default() + .to_string(); + + let index_file = index_map + .get(&host) + .cloned() + .unwrap_or_else(|| format!("{}/{}", serve_dir, default_index)); + + log::info!("Custom '/' route: serving '{}' for domain '{}'", index_file, host); + + async move { + Ok::<_, actix_web::Error>( + actix_files::NamedFile::open(index_file)?.into_response(&req), + ) + } + } + })) .service( actix_files::Files::new(&mount_point, &serve_dir) - .index_file(&default_index) - .default_handler(move |req: ServiceRequest| { - let (http_req, _payload) = req.into_parts(); - let host = http_req - .headers() - .get("Host") - .and_then(|h| h.to_str().ok()) - .unwrap_or_default(); + .use_last_modified(true), + ) + .default_service(actix_web::dev::fn_service(move |req: ServiceRequest| { + let (http_req, _payload) = req.into_parts(); + let host = http_req + .headers() + .get("Host") + .and_then(|h| h.to_str().ok()) + .unwrap_or_default() + .to_string(); - // Determine the index file based on the host - let index_file = index_map - .get(host) - .cloned() - .unwrap_or_else(|| format!("{}/{}", serve_dir, default_index)); + log::info!("Request for domain: '{}'", host); - async move { - let response = actix_files::NamedFile::open(index_file)?.into_response(&http_req); - Ok(ServiceResponse::new(http_req, response)) - } - }), - ) + // Determine the index file based on the host + let index_file = index_map + .get(&host) + .cloned() + .unwrap_or_else(|| format!("{}/{}", serve_dir, default_index)); + + // Log which index is being used for which domain + log::info!("Serving file '{}' for domain '{}'", index_file, host); + + async move { + let response = actix_files::NamedFile::open(index_file)?.into_response(&http_req); + Ok(ServiceResponse::new(http_req, response)) + } + })) }) .bind(bind_addr)? .run()