From b736068ee7b82e05c2ede8bc48ace7ffa4709e29 Mon Sep 17 00:00:00 2001 From: Chris Xiong Date: Wed, 24 Jul 2024 23:40:11 -0400 Subject: Initial commit. --- backend/src/main.rs | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 160 insertions(+) create mode 100644 backend/src/main.rs (limited to 'backend/src/main.rs') diff --git a/backend/src/main.rs b/backend/src/main.rs new file mode 100644 index 0000000..0474f89 --- /dev/null +++ b/backend/src/main.rs @@ -0,0 +1,160 @@ +mod config; +mod monolith; +mod render; + +use std::env; +use std::collections::hash_map::{HashMap, RandomState}; + +enum CGIStatusCode { + S200, + C400, + C404, + C405 +} + +type CGIHTTPHeaders = Vec<(String, String)>; + +struct CGIResponse { + status: CGIStatusCode, + headers: CGIHTTPHeaders, + body: String +} + +fn cgi_handle_request(conf: &config::Config) -> Result { + let headers = vec![ + (String::from("Allow"), String::from("GET")), + (String::from("Content-type"), String::from("text/html")) + ]; + let mkerr = |status| CGIResponse { + status, + headers: headers.clone(), + body: String::from("") + }; + let request_method = env::var("REQUEST_METHOD").map_err(|_| mkerr(CGIStatusCode::C400))?; + if request_method != "GET" { + return Err(mkerr(CGIStatusCode::C405)); + } + let query: HashMap<_, _, RandomState> = + if let Ok(query_string) = env::var("QUERY_STRING") { + if query_string.len() > 0 { + HashMap::from_iter(query_string.split('&').map(|qi| { + let (k, v) = config::split_at_first(qi, "="); + (String::from(k), String::from(v)) + })) + } else { HashMap::from([(String::from("page"), String::from("1"))]) } + } else { HashMap::from([(String::from("page"), String::from("1"))]) }; + let m = monolith::Monolith::new(String::from("posts.monolith")); + if let Some(ps) = query.get("page") { + let p = usize::from_str_radix(&ps, 10).map_err(|_| mkerr(CGIStatusCode::C400))?.checked_sub(1).ok_or(mkerr(CGIStatusCode::C404))?; + let ps = m.get_page_posts(p).ok_or(mkerr(CGIStatusCode::C404))?; + let r = render::Renderer::load("./template"); + return Ok(CGIResponse { + status: CGIStatusCode::S200, + headers, + body: r.render_page(ps, p, m.get_page_count(), conf) + }); + } else if let Some(ds) = query.get("post") { + let d = i64::from_str_radix(&ds, 10).map_err(|_| mkerr(CGIStatusCode::C400))?; + let p = m.get_post_2(d).ok_or(mkerr(CGIStatusCode::C404))?; + let r = render::Renderer::load("./template"); + return Ok(CGIResponse { + status: CGIStatusCode::S200, + headers, + body: r.render_single_post(p, conf) + }); + } + Err(mkerr(CGIStatusCode::C400)) +} + +fn cgimain(conf: config::Config) -> Result<(), &'static str> { + let r = match cgi_handle_request(&conf) { + Ok(r) => r, + Err(r) => r + }; + let (status, status_str) = match r.status { + CGIStatusCode::S200 => (200, "OK"), + CGIStatusCode::C400 => (400, "Bad Request"), + CGIStatusCode::C404 => (404, "Not Found"), + CGIStatusCode::C405 => (405, "Method Not Allowed") + }; + print!("Status: {} {}\r\n", status, status_str); + r.headers.iter().for_each(|(f, v)| print!("{}: {}\r\n", f, v)); + print!("\r\n"); + if status < 400 { + print!("{}", r.body); + } else { + let rdr = render::Renderer::load("./template"); + print!("{}", rdr.render_error(status, String::from(status_str), &conf)); + } + Ok(()) +} + +fn dbgmain(conf: config::Config) -> Result<(), &'static str> { + eprintln!("in debug mode"); + eprintln!("notekins version {}", conf.get("VERSION_STRING")); + let mut m = monolith::Monolith::new(String::from("posts.monolith")); + let mut args = env::args(); + args.next(); + if let Some(dbgop) = args.next() { + match dbgop.as_str() { + "get_post" => { + let tss = args.next().ok_or("missing timestamp")?; + let ts = i64::from_str_radix(&tss, 10).map_err(|_| "invalid timestamp")?; + m.load_index(); + let p = m.get_post(ts).ok_or("post not found")?; + monolith::test_print_post(&p); + Ok(()) + }, + "get_post2" => { + let tss = args.next().ok_or("missing timestamp")?; + let ts = i64::from_str_radix(&tss, 10).map_err(|_| "invalid timestamp")?; + let p = m.get_post_2(ts).ok_or("post not found")?; + monolith::test_print_post(&p); + Ok(()) + }, + "get_page" => { + let pgs = args.next().ok_or("missing page")?; + let pg = usize::from_str_radix(&pgs, 10).map_err(|_| "invalid page")?; + let ps = m.get_page_posts(pg).ok_or("page out of range")?; + for p in ps { + monolith::test_print_post(&p); + } + Ok(()) + }, + "render_page" => { + let pgs = args.next().ok_or("missing page")?; + let pg = usize::from_str_radix(&pgs, 10).map_err(|_| "invalid page")?; + let ps = m.get_page_posts(pg).ok_or("page out of range")?; + let r = render::Renderer::load("./template"); + println!("{}", r.render_page(ps, pg, m.get_page_count(), &conf)); + Ok(()) + }, + "render_post" => { + let tss = args.next().ok_or("missing timestamp")?; + let ts = i64::from_str_radix(&tss, 10).map_err(|_| "invalid timestamp")?; + let p = m.get_post_2(ts).ok_or("post not found")?; + let r = render::Renderer::load("./template"); + println!("{}", r.render_single_post(p, &conf)); + Ok(()) + }, + _ => Err("unsupported debug option") + } + } else { + m.load_index(); + let dates = m.get_all_dates(); + for d in dates { + let p = m.get_post(d); + println!("{:?}", p) + } + Ok(()) + } +} + +fn main() -> Result<(), &'static str> { + let conf = config::Config::parse_config("notekins.conf"); + if let Ok(_) = env::var("SERVER_SOFTWARE") { + cgimain(conf) + } else { + dbgmain(conf) + } +} -- cgit v1.2.3