From 754da83634898a409808632ca4c2cdea31d156b1 Mon Sep 17 00:00:00 2001 From: n-dusan Date: Thu, 21 Dec 2023 20:54:06 +0100 Subject: [PATCH] feat: work on multihost routing --- src/server/publish.rs | 50 ++++++++++++++++++++++++++++++++--------- src/stelae/archive.rs | 35 +++++++++++++++++++---------- src/stelae/stele.rs | 17 +++++++------- src/stelae/types/mod.rs | 2 ++ 4 files changed, 72 insertions(+), 32 deletions(-) diff --git a/src/server/publish.rs b/src/server/publish.rs index 3826ce7..6817a76 100644 --- a/src/server/publish.rs +++ b/src/server/publish.rs @@ -34,9 +34,9 @@ fn clean_path(path: &str) -> String { /// Global, read-only state #[derive(Debug, Clone)] -pub struct AppState { +pub struct AppState<'app> { /// Fully initialized Stelae archive - pub archive: Archive, + pub archive: Archive<'app>, } /// Git repository to serve @@ -185,7 +185,7 @@ pub async fn serve_archive( let root = state.archive.get_root().unwrap(); let shared_state = init_shared_app_state(root); // let shared_state = SharedState { fallback: None }; - dbg!(&shared_state); + dbg!(&state); HttpServer::new(move || init_app(shared_state.clone(), state.clone())) .bind((bind, port))? @@ -207,12 +207,40 @@ pub fn init_app( Error = Error, >, > { - App::new().service( - web::scope("") - .app_data(web::Data::new(shared_state)) - .wrap(TracingLogger::::new()) - .configure(|cfg| init_routes(cfg, state)), - ) + let config = state.archive.get_config().unwrap_or_else(|_| { + tracing::error!("Unable to parse config file."); + std::process::exit(1); + }); + let stelae_custom_header = config + .headers + .and_then(|headers| headers.stelae_use_custom_header); + + match stelae_custom_header { + Some(header) => { + lazy_static! { + static ref HEADER: &'static str = || { header }; + } + let mut app = App::new(); + for name in state.archive.stelae.keys() { + let mut stelae_scope = web::scope(""); + + stelae_scope = stelae_scope.guard(guard::Header(header, &name)); + app = app.service(stelae_scope); + } + App::new().service( + web::scope("") + .app_data(web::Data::new(shared_state)) + .wrap(TracingLogger::::new()) + .configure(|cfg| init_routes(cfg, state)), + ) + } + None => App::new().service( + web::scope("") + .app_data(web::Data::new(shared_state)) + .wrap(TracingLogger::::new()) + .configure(|cfg| init_routes(cfg, state)), + ), + } } /// Routes @@ -238,7 +266,7 @@ fn init_routes(cfg: &mut web::ServiceConfig, mut state: AppState) { repo: Repo { archive_path: state.archive.path.to_string_lossy().to_string(), path: PathBuf::from(repo_path.clone()), - org: stele.org.clone(), + org: stele.org.to_string(), name: name.to_string(), repo: Repository::open(repo_path) .expect("Unable to open Git repository"), @@ -292,7 +320,7 @@ fn init_routes(cfg: &mut web::ServiceConfig, mut state: AppState) { repo: Repo { archive_path: state.archive.path.to_string_lossy().to_string(), path: PathBuf::from(repo_path.clone()), - org: stele.org.clone(), + org: stele.org.to_string(), name: name.to_string(), repo: Repository::open(repo_path) .expect("Unable to open Git repository"), diff --git a/src/stelae/archive.rs b/src/stelae/archive.rs index 0faa113..d6ffc24 100644 --- a/src/stelae/archive.rs +++ b/src/stelae/archive.rs @@ -5,20 +5,21 @@ use crate::stelae::stele; use crate::stelae::stele::Stele; use crate::utils::archive::{find_archive_path, get_name_parts}; use serde_derive::{Deserialize, Serialize}; +use std::borrow::Cow; use std::collections::HashMap; use std::fs::{create_dir_all, read_to_string, write}; use std::path::{Path, PathBuf}; /// The Archive struct is used for interacting with a Stelae Archive. #[derive(Debug, Clone)] -pub struct Archive { +pub struct Archive<'archive> { /// Path to the Archive pub path: PathBuf, /// map of auth repo name to Stele object - pub stelae: HashMap, + pub stelae: HashMap<&'archive str, Stele<'archive>>, } -impl Archive { +impl<'archive> Archive<'archive> { /// Get an archive's config object. /// # Errors /// Will error if unable to find or parse config file at `.stelae/config.toml` @@ -60,13 +61,13 @@ impl Archive { root = Stele::new( self.path.clone(), - Some(name), - Some(org.clone()), - Some(self.path.clone().join(org)), + Some(&name), + Some(&org), + Some(self.path.clone().join(&org)), true, )?; } - self.stelae.insert(root.get_qualified_name(), root); + self.stelae.insert(&root.get_qualified_name(), root); Ok(()) } @@ -106,13 +107,13 @@ impl Archive { let (org, name) = get_name_parts(&qualified_name)?; let child = Stele::new( self.path.clone(), - Some(name), - Some(org.clone()), - Some(parent_dir.join(org)), + Some(&name), + Some(&org), + Some(parent_dir.join(&org)), false, )?; self.stelae - .entry(format!("{}/{}", child.org, child.name)) + .entry(&format!("{}/{}", child.org, child.name)) .or_insert_with(|| child.clone()); self.traverse_children(&child)?; } @@ -139,6 +140,15 @@ pub struct Config { root: stele::Config, /// Whether this is a shallow archive (all repos depth=1) shallow: bool, + /// Custom headers used to interact with the Stele + pub headers: Option, +} + +/// Headers object for an Archive +#[derive(Default, Deserialize, Serialize)] +pub struct Headers { + /// Specify a custom header to use when requesting a Stele's current documents. + pub stelae_use_custom_header: Option<&'static str>, } /// Create a new Stelae Archive at path, and return the new archive. @@ -150,7 +160,7 @@ pub fn init( root_org: String, root_hash: Option, shallow: bool, -) -> anyhow::Result> { +) -> anyhow::Result>> { raise_error_if_in_existing_archive(&path)?; let stelae_dir = path.join(PathBuf::from("./.stelae")); create_dir_all(&stelae_dir)?; @@ -162,6 +172,7 @@ pub fn init( hash: root_hash, }, shallow, + headers: None, }; let conf_str = toml_edit::ser::to_string_pretty(&conf)?; write(config_path, conf_str)?; diff --git a/src/stelae/stele.rs b/src/stelae/stele.rs index 98c613c..e411175 100644 --- a/src/stelae/stele.rs +++ b/src/stelae/stele.rs @@ -12,13 +12,13 @@ use super::types::repositories::Repository; /// Stele #[derive(Debug, Clone)] -pub struct Stele { +pub struct Stele<'stele> { /// Path to the containing Stelae archive. pub archive_path: PathBuf, /// Name of the authentication repo (e.g. law). - pub name: String, + pub name: &'stele str, /// Name of the Stele's directory, also known as Stele's organization (e.g. openlawlibrary). - pub org: String, + pub org: &'stele str, /// Full path to the Stele's directory. pub path: PathBuf, /// Stele's repositories (as specified in repositories.json). @@ -28,7 +28,7 @@ pub struct Stele { pub root: bool, } -impl Stele { +impl<'stele> Stele<'stele> { /// Create a new Stele object /// # Errors /// Will error if unable to find or parse repositories file at `targets/repositories.json` @@ -37,12 +37,12 @@ impl Stele { #[allow(clippy::unwrap_used, clippy::shadow_reuse)] pub fn new( archive_path: PathBuf, - name: Option, - org: Option, + name: Option<&str>, + org: Option<&str>, path: Option, root: bool, ) -> anyhow::Result { - let name = name.unwrap_or_else(|| "law".to_owned()); + let name = name.unwrap_or_else(|| "law"); let org = org.unwrap_or_else(|| { path.as_ref() .unwrap() @@ -50,7 +50,6 @@ impl Stele { .unwrap() .to_str() .unwrap() - .to_owned() }); let path = path.unwrap_or_else(|| archive_path.join(&org)); let mut stele = Self { @@ -98,7 +97,7 @@ impl Stele { /// Get Stele's qualified name. #[must_use] pub fn get_qualified_name(&self) -> String { - format!("{}/{}", self.org, self.name) + format!("{}/{}", self.org.to_string(), self.name.to_string()) } /// Get Stele's fallback repo. diff --git a/src/stelae/types/mod.rs b/src/stelae/types/mod.rs index 0542b2c..c52d4d8 100644 --- a/src/stelae/types/mod.rs +++ b/src/stelae/types/mod.rs @@ -1,2 +1,4 @@ +//! The Types module contains data models for Stelae. + pub mod dependencies; pub mod repositories;