236 lines
9.1 KiB
Rust
236 lines
9.1 KiB
Rust
use std::{
|
|
collections::HashMap,
|
|
io::{self, Write},
|
|
path::PathBuf,
|
|
time::SystemTime,
|
|
};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{server_config::ServerConfig, static_http_server_route::StaticHttpServerRoute};
|
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
pub struct ServerRoutesScan {
|
|
pub routes_date: u128,
|
|
pub routes: Vec<StaticHttpServerRoute>,
|
|
pub graph: HashMap<String, Vec<StaticHttpServerRoute>>,
|
|
pub subdomains: HashMap<String, Vec<StaticHttpServerRoute>>,
|
|
}
|
|
|
|
impl ServerRoutesScan {
|
|
fn new(
|
|
routes_date: u128,
|
|
routes: Vec<StaticHttpServerRoute>,
|
|
graph: HashMap<String, Vec<StaticHttpServerRoute>>,
|
|
subdomains: HashMap<String, Vec<StaticHttpServerRoute>>,
|
|
) -> Self {
|
|
Self {
|
|
routes_date,
|
|
routes,
|
|
graph,
|
|
subdomains,
|
|
}
|
|
}
|
|
|
|
pub fn check_for_changes_mut(&mut self, config: &ServerConfig) {
|
|
match self.check_for_changes(config) {
|
|
Ok(new) => {
|
|
self.routes_date = new.routes_date;
|
|
self.routes = new.routes;
|
|
self.graph = new.graph;
|
|
self.subdomains = new.subdomains;
|
|
}
|
|
Err(err) if err.kind() == io::ErrorKind::AddrInUse => (),
|
|
Err(err) => {
|
|
self.routes_date = u128::default();
|
|
self.routes = Vec::default();
|
|
self.graph = HashMap::default();
|
|
self.subdomains = HashMap::default();
|
|
eprintln!("Warn: Error on adapt_from_disk_changes: {:?}", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn check_for_changes(&self, config: &ServerConfig) -> io::Result<Self> {
|
|
let routes_disk_date = std::fs::metadata(&config.routes)
|
|
.ok()
|
|
.or_else(|| {
|
|
std::fs::File::create(&config.routes)
|
|
.and_then(|mut file| file.write_all(b""))
|
|
.and_then(|_| std::fs::metadata(&config.routes))
|
|
.ok()
|
|
})
|
|
.and_then(|metadata| metadata.modified().ok())
|
|
.and_then(|systime| systime.duration_since(SystemTime::UNIX_EPOCH).ok())
|
|
.map(|duration| duration.as_micros())
|
|
.unwrap_or(0u128);
|
|
|
|
if routes_disk_date == 0 || self.routes_date == 0 || self.routes_date != routes_disk_date {
|
|
self.adapt_from_disk_changes(config)
|
|
} else {
|
|
Err(io::ErrorKind::AddrInUse.into())
|
|
}
|
|
}
|
|
fn adapt_from_disk_changes(&self, config: &ServerConfig) -> io::Result<Self> {
|
|
let mut routes: Vec<StaticHttpServerRoute> = Vec::new();
|
|
std::fs::create_dir_all(config.sites.clone())?;
|
|
for dir_entry_result in config.sites.read_dir()? {
|
|
let dir_entry: std::fs::DirEntry = dir_entry_result?;
|
|
if !dir_entry.metadata()?.is_dir() {
|
|
eprintln!(
|
|
"WARNING: {:?} is not a directory! Ignoring...",
|
|
dir_entry.path()
|
|
);
|
|
continue;
|
|
}
|
|
let site_definition = dir_entry.path().join("site.yaml");
|
|
if !(match site_definition.metadata() {
|
|
Ok(metadata) => metadata.is_file(),
|
|
Err(_) => false,
|
|
}) {
|
|
eprintln!("WARNING: {:?} is not a file! Ignoring...", site_definition);
|
|
continue;
|
|
}
|
|
let site_definition_string: String = match std::fs::read_to_string(&site_definition) {
|
|
Ok(site_definition_) => site_definition_,
|
|
Err(_) => {
|
|
eprintln!(
|
|
"WARNING: {:?} does not have valid text! Ignoring...",
|
|
site_definition,
|
|
);
|
|
continue;
|
|
}
|
|
};
|
|
let mut route_vec: Vec<StaticHttpServerRoute> = match serde_yaml::from_str(
|
|
site_definition_string.as_str(),
|
|
) {
|
|
Ok(routes_) => routes_,
|
|
Err(_) => {
|
|
eprintln!(
|
|
"WARNING: {:?} does not deserialize into a Vec<StaticHttpServerRoutes> struct! Ignoring...",
|
|
site_definition,
|
|
);
|
|
continue;
|
|
}
|
|
};
|
|
let site_files_cand = dir_entry.path().join("files");
|
|
let site_files_try = site_files_cand.canonicalize();
|
|
let site_files = site_files_try.unwrap_or(site_files_cand);
|
|
if !(site_files
|
|
.metadata()
|
|
.map(|metadata| metadata.is_dir())
|
|
.unwrap_or(false))
|
|
{
|
|
eprintln!(
|
|
"WARNING: {:?} is not a directory! Is likely to only return 404s...",
|
|
site_files
|
|
);
|
|
}
|
|
for route in route_vec.iter_mut() {
|
|
if config.is_on_dev || !route.only_on_dev {
|
|
if route.path == PathBuf::from("")
|
|
|| route.path == PathBuf::from("/")
|
|
|| route
|
|
.path
|
|
.canonicalize()
|
|
.and_then(|pb| PathBuf::from(".").canonicalize().map(|cd| cd == pb))
|
|
.unwrap_or(true)
|
|
|| route
|
|
.path
|
|
.canonicalize()
|
|
.map(|pb| pb == site_files)
|
|
.unwrap_or(true)
|
|
{
|
|
route.path.clone_from(&site_files);
|
|
} else {
|
|
route.path = route
|
|
.path
|
|
.canonicalize()
|
|
.unwrap_or_else(|_| site_files.clone());
|
|
}
|
|
//
|
|
if route.template_path == PathBuf::from("")
|
|
|| route.template_path == PathBuf::from("/")
|
|
|| route
|
|
.template_path
|
|
.canonicalize()
|
|
.and_then(|pb| PathBuf::from(".").canonicalize().map(|cd| cd == pb))
|
|
.unwrap_or(true)
|
|
|| route
|
|
.template_path
|
|
.canonicalize()
|
|
.map(|pb| pb == site_files)
|
|
.unwrap_or(true)
|
|
{
|
|
route.template_path.clone_from(&site_files);
|
|
} else {
|
|
route.template_path = route
|
|
.template_path
|
|
.canonicalize()
|
|
.unwrap_or_else(|_| site_files.clone());
|
|
}
|
|
//
|
|
let on_404 = route.path.join(route.on_404.clone());
|
|
if on_404.is_file() {
|
|
route.on_404 = on_404;
|
|
} else {
|
|
route.on_404 = PathBuf::default();
|
|
}
|
|
if !route.prefix.starts_with('/') {
|
|
route.prefix = format!("/{}", route.prefix);
|
|
}
|
|
if !route.prefix.ends_with('/') {
|
|
route.prefix = format!("{}/", route.prefix);
|
|
}
|
|
routes.append(&mut vec![route.clone()]);
|
|
}
|
|
}
|
|
}
|
|
let mut graph: HashMap<String, Vec<StaticHttpServerRoute>> = HashMap::new();
|
|
for route in routes.iter() {
|
|
if !graph.contains_key(&route.domain) {
|
|
graph.insert(route.domain.clone(), Vec::new());
|
|
}
|
|
graph
|
|
.get_mut(&route.domain)
|
|
.map(|graph_vec| {
|
|
graph_vec.push(route.clone());
|
|
})
|
|
.ok_or(std::io::ErrorKind::NotFound)?;
|
|
}
|
|
let mut graph2: HashMap<String, Vec<StaticHttpServerRoute>> = HashMap::new();
|
|
for ge in graph.iter() {
|
|
let mut vector = ge.1.clone();
|
|
vector.sort_by(|x, y| y.prefix.len().cmp(&x.prefix.len()));
|
|
graph2.insert(ge.0.clone(), vector);
|
|
}
|
|
let new_graph = graph2
|
|
.clone()
|
|
.into_iter()
|
|
.map(|(k, v)| (k.trim_start_matches('*').to_owned(), v))
|
|
.collect();
|
|
let new_subdomains = graph2
|
|
.clone()
|
|
.into_iter()
|
|
.filter(|(k, _)| k.starts_with('*'))
|
|
.map(|(k, v)| (k.trim_start_matches('*').to_owned(), v))
|
|
.collect();
|
|
std::fs::File::create(&config.routes)?.write_all(
|
|
serde_yaml::to_string(&graph)
|
|
.map_err(|_| std::io::ErrorKind::InvalidInput)?
|
|
.as_bytes(),
|
|
)?;
|
|
let new_routes_date = std::fs::metadata(&config.routes)?
|
|
.modified()?
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.map_err(|_| std::io::ErrorKind::InvalidData)?
|
|
.as_micros();
|
|
Ok(Self::new(
|
|
new_routes_date,
|
|
routes,
|
|
new_graph,
|
|
new_subdomains,
|
|
))
|
|
}
|
|
}
|