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.
This commit is contained in:
parent
3b8360fa40
commit
85de86f835
3 changed files with 65 additions and 29 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1267,7 +1267,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "servant"
|
name = "servant"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"actix-files",
|
"actix-files",
|
||||||
"actix-web",
|
"actix-web",
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "servant"
|
name = "servant"
|
||||||
version = "0.2.0"
|
version = "0.2.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
authors = ["Andrew G. <me@nuark.xyz>"]
|
authors = ["Andrew G. <me@nuark.xyz>"]
|
||||||
|
|
||||||
|
|
|
||||||
64
src/main.rs
64
src/main.rs
|
|
@ -1,9 +1,8 @@
|
||||||
use std::path::Path;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use actix_files;
|
use actix_files;
|
||||||
use actix_web::{
|
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 clap::{arg, command, Parser};
|
||||||
use env_logger::Env;
|
use env_logger::Env;
|
||||||
|
|
@ -44,13 +43,18 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
let args = ServantArgs::parse();
|
let args = ServantArgs::parse();
|
||||||
|
|
||||||
let bind_addr = format!("{}:{}", args.host, args.port);
|
// Log configured rewrite rules
|
||||||
println!("Listening on http://{}", bind_addr);
|
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())
|
let bind_addr = format!("{}:{}", args.host, args.port);
|
||||||
.join(args.index_file.as_str())
|
log::info!("Listening on http://{}", bind_addr);
|
||||||
.display()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let mount_point = args.mount.clone();
|
let mount_point = args.mount.clone();
|
||||||
let serve_dir = args.serve_dir.clone();
|
let serve_dir = args.serve_dir.clone();
|
||||||
|
|
@ -68,29 +72,61 @@ async fn main() -> std::io::Result<()> {
|
||||||
|
|
||||||
App::new()
|
App::new()
|
||||||
.wrap(Logger::default())
|
.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(
|
.service(
|
||||||
actix_files::Files::new(&mount_point, &serve_dir)
|
actix_files::Files::new(&mount_point, &serve_dir)
|
||||||
.index_file(&default_index)
|
.use_last_modified(true),
|
||||||
.default_handler(move |req: ServiceRequest| {
|
)
|
||||||
|
.default_service(actix_web::dev::fn_service(move |req: ServiceRequest| {
|
||||||
let (http_req, _payload) = req.into_parts();
|
let (http_req, _payload) = req.into_parts();
|
||||||
let host = http_req
|
let host = http_req
|
||||||
.headers()
|
.headers()
|
||||||
.get("Host")
|
.get("Host")
|
||||||
.and_then(|h| h.to_str().ok())
|
.and_then(|h| h.to_str().ok())
|
||||||
.unwrap_or_default();
|
.unwrap_or_default()
|
||||||
|
.to_string();
|
||||||
|
|
||||||
|
log::info!("Request for domain: '{}'", host);
|
||||||
|
|
||||||
// Determine the index file based on the host
|
// Determine the index file based on the host
|
||||||
let index_file = index_map
|
let index_file = index_map
|
||||||
.get(host)
|
.get(&host)
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_else(|| format!("{}/{}", serve_dir, default_index));
|
.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 {
|
async move {
|
||||||
let response = actix_files::NamedFile::open(index_file)?.into_response(&http_req);
|
let response = actix_files::NamedFile::open(index_file)?.into_response(&http_req);
|
||||||
Ok(ServiceResponse::new(http_req, response))
|
Ok(ServiceResponse::new(http_req, response))
|
||||||
}
|
}
|
||||||
}),
|
}))
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.bind(bind_addr)?
|
.bind(bind_addr)?
|
||||||
.run()
|
.run()
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue