diff --git a/Cargo.lock b/Cargo.lock index f69647ca22e8f21a6b95769245e79d3f1eca14c1..00086a3c324c20e8570a08df98b2c9ea51ae131e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -375,6 +375,8 @@ name = "cms_rust" version = "0.1.0" dependencies = [ "actix-web", + "dirs", + "fs_extra", "regex", "rustls", "rustls-pemfile", @@ -467,6 +469,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "dirs" +version = "4.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "encoding_rs" version = "0.8.31" @@ -508,6 +530,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2022715d62ab30faffd124d40b76f4134a550a87792276512b18d63272333394" + [[package]] name = "futures-core" version = "0.3.21" @@ -930,6 +958,17 @@ dependencies = [ "bitflags", ] +[[package]] +name = "redox_users" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +dependencies = [ + "getrandom", + "redox_syscall", + "thiserror", +] + [[package]] name = "regex" version = "1.6.0" @@ -1142,6 +1181,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "thiserror" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5f6586b7f764adc0231f4c79be7b920e766bb2f3e51b3661cdb263828f19994" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12bafc5b54507e0149cdf1b145a5d80ab80a90bcd9275df43d4fff68460f6c21" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.12" diff --git a/Cargo.toml b/Cargo.toml index 63e3fed244ace86841d47984de91c48aecbca599..f90e2a7f49458d3cf3b993f29f9f8b730db53d1e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,3 +12,5 @@ rustls-pemfile = "1.0.1" serde = { version = "1.0.143", features = ["derive"] } serde_json = "1.0.83" regex = "1.6" +fs_extra = "1.2" +dirs = "4.0" diff --git a/default_static/script.js b/default_static/script.js new file mode 100644 index 0000000000000000000000000000000000000000..2aff4460919b0fa7ed8f898d7f32ab00a2f4df9d --- /dev/null +++ b/default_static/script.js @@ -0,0 +1 @@ +console.log("Hello from default js") \ No newline at end of file diff --git a/default_static/style.css b/default_static/style.css new file mode 100644 index 0000000000000000000000000000000000000000..45ce6b3097f43459509cca20ee8595306be8f6eb --- /dev/null +++ b/default_static/style.css @@ -0,0 +1,3 @@ +body { + font-family: monospace; +} \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e785b97a480e3c175ddd2b99a020a3d8a1e07ded..72b48265f21464028248f1b64492f581b351ed5a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,5 +1,7 @@ +mod static_files; mod website; use actix_web::{get, App, HttpResponse, HttpServer, Responder}; +use static_files::StaticFilesManager; use std::path::PathBuf; use website::WebSite; @@ -37,6 +39,9 @@ async fn admin_login() -> impl Responder { #[actix_web::main] async fn main() -> std::io::Result<()> { let website = load_website_template(); + let site_name = "rust_cms".to_string(); // Get from arg + let mut static_files_manager = StaticFilesManager::new(&site_name).unwrap(); + static_files_manager.build_index().unwrap(); // GET HOST AND CERTS DIR FROM CLI ARGUMENT // Get port from arg, or get context from arg and define default port, or set default port to standard @@ -47,17 +52,15 @@ async fn main() -> std::io::Result<()> { // create the static dir in standard location if doesn't exist (like /var/{sitename}/static) // create the static files index (like Arc<Mutex<StaticFilesIndex>>) - // create a Rest service at root with extensive path argument: like #[get(/{pth:.*})] - // Then parse the website document and return the corresponding template, or 404 template - let host = "localhost"; - let certs_dir = std::path::PathBuf::from("/etc/letsencrypt/live").join(host); + let certs_dir = PathBuf::from("/etc/letsencrypt/live").join(host); let cert_file = &mut std::io::BufReader::new(std::fs::File::open(certs_dir.join("fullchain.pem")).unwrap()); let key_file = &mut std::io::BufReader::new(std::fs::File::open(certs_dir.join("privkey.pem")).unwrap()); let cert = rustls::Certificate(rustls_pemfile::certs(cert_file).unwrap().remove(0).to_vec()); + let key = rustls::PrivateKey( rustls_pemfile::pkcs8_private_keys(key_file) .unwrap() @@ -71,11 +74,15 @@ async fn main() -> std::io::Result<()> { .with_single_cert(vec![cert], key) .expect("bad certificate/key"); + let static_files_manager = + actix_web::web::Data::new(std::sync::Mutex::new(static_files_manager)); + HttpServer::new(move || { App::new() .wrap(actix_web::middleware::Logger::default()) .wrap(actix_web::middleware::Compress::default()) .app_data(actix_web::web::Data::new(website.clone())) + .app_data(actix_web::web::Data::clone(&static_files_manager)) .service(admin_dashboard) .service(admin_login) .service(page) diff --git a/src/static_files.rs b/src/static_files.rs new file mode 100644 index 0000000000000000000000000000000000000000..ced5de48b3f437d9966febc66afe053ddc80da34 --- /dev/null +++ b/src/static_files.rs @@ -0,0 +1,52 @@ +use std::path::PathBuf; + +#[derive(Clone)] +pub struct StaticFilesManager { + index: Vec<String>, +} + +impl StaticFilesManager { + fn create_dir_if_missing(app_dir: &PathBuf) -> Result<(), String> { + let static_path = app_dir.join("static"); + + if !static_path.exists() { + match std::fs::create_dir_all(static_path) { + Ok(_) => return Self::copy_default(&app_dir), + Err(err) => return Err(format!("{}", err)), + } + } + + Ok(()) + } + + fn copy_default(app_dir: &PathBuf) -> Result<(), String> { + let local_default = std::env::current_dir().unwrap().join("default_static"); + let standard_default = app_dir.join("static").join("default"); + if let Err(err) = std::fs::create_dir_all(&standard_default) { + return Err(format!("{}", err)); + } + + let mut copy_default_options = fs_extra::dir::CopyOptions::new(); + copy_default_options.content_only = true; + if let Err(err) = + fs_extra::dir::copy(local_default, standard_default, ©_default_options) + { + return Err(format!("{}", err)); + }; + Ok(()) + } + + pub fn new(website_name: &String) -> Result<Self, String> { + if let Err(err) = Self::create_dir_if_missing(&dirs::home_dir().unwrap().join(website_name)) + { + return Err(err); + } + + Ok(StaticFilesManager { index: Vec::new() }) + } + + pub fn build_index(&mut self) -> Result<(), String> { + self.index.push("TODO".to_string()); + Ok(()) + } +} diff --git a/src/website.rs b/src/website.rs index bfb564ce3fbc9a50d0945346fc609222f1a5ad90..9e2f6c7f2b9bd51e1e6a43a96531d33e54c242a7 100644 --- a/src/website.rs +++ b/src/website.rs @@ -18,26 +18,28 @@ pub struct PageData { pub struct HtmlDoc(String); impl HtmlDoc { - pub fn from_page_data(page_data: &PageData) -> Self { - let html_doc = std::fs::read_to_string( + fn load_template() -> String { + std::fs::read_to_string( std::env::current_dir() .unwrap() .join("templates") .join("html_doc.html"), ) - .expect("Missing html_doc template"); + .expect("Missing html_doc template") + } + pub fn from_page_data(page_data: &PageData) -> Self { let re = Regex::new(r#"\{[a-z]+\}"#).unwrap(); - let html_doc = re - .replace_all(&html_doc, |captures: &Captures| { + let html = re + .replace_all(&HtmlDoc::load_template(), |captures: &Captures| { let placeholder = captures.iter().next().unwrap().unwrap().as_str(); let placeholder = placeholder[1..placeholder.len() - 1].to_owned(); page_data.field_from_str_key(placeholder) }) .to_string(); - HtmlDoc(html_doc) + HtmlDoc(html) } pub fn to_string(&self) -> String { @@ -146,11 +148,11 @@ mod test_website { \"sub_pages\": [ { \"page_data\": { - \"title\": \"Another page\", + \"title\": \"Nested page\", \"lang\": \"en\", - \"slug\": \"otherpage\", - \"description\": \"Another testing page\", - \"html_body\": \"<h1>Another page</h1>\" + \"slug\": \"nested\", + \"description\": \"Nested testing page\", + \"html_body\": \"<h1>Nested page</h1>\" } } ] @@ -167,9 +169,14 @@ mod test_website { let root_page = root_page.unwrap(); assert_eq!(root_page.page_data.html_body, "<h1>Test Website</h1>"); - let sub_page = website.get_page_by_url(&PathBuf::from("/subpage")); + let sub_page = website.get_page_by_url(&PathBuf::from("subpage")); assert!(sub_page.is_some()); let sub_page = sub_page.unwrap(); assert_eq!(sub_page.page_data.html_body, "<h1>A sub page</h1>"); + + let nested_page = website.get_page_by_url(&PathBuf::from("subpage/nested")); + assert!(nested_page.is_some()); + let nested_page = nested_page.unwrap(); + assert_eq!(nested_page.page_data.html_body, "<h1>Nested page</h1>"); } }