crabmail

Static HTML email archive viewer in Rust
git clone git://git.alexwennerberg.com/crabmail
Log | Files | Refs | README | LICENSE

commit cec4dd34981f00df46932062befe42116df4af9e
parent 5fb5195d090af5f1ede28db9e0332fb7b997bd61
Author: alex wennerberg <alex@alexwennerberg.com>
Date:   Sun, 20 Mar 2022 11:46:21 -0700

Create homepage

Diffstat:
MTODO | 23+++--------------------
Msrc/arg.rs | 6+++++-
Msrc/main.rs | 2+-
Msrc/templates/gmi.rs | 13+++++++++----
Msrc/templates/html.rs | 21++++++++++++++++++---
5 files changed, 36 insertions(+), 29 deletions(-)

diff --git a/TODO b/TODO @@ -1,30 +1,13 @@ TODO ==== - -allow mbox/single folder input atom feeds working -gemini pages finish up --> gemini body parser paginate list home create list home page -dkim? - +fix docs +check for html escape bugz +add date to generated string Duplicate ID verification: warn on duplicate ID, use first received-date. This is to prevent someone overwriting old emails secretly -Add replies as well as parents - - Note that unfortunately a lot of clients don't parse the in-reply-to - header. Maybe there is a non-sucky workaround - Color highlight on anchor select - -Refactor so there is a better interface with the filesystem, which then -will let me do some caching stuff - -Maybe -===== -Allow Mbox - - why not? -> "Do one thing and do it well". Use mblaze for mbox conversion. -Take a list of folders rather than a single top-level folder - - maybe cleaner API diff --git a/src/arg.rs b/src/arg.rs @@ -21,7 +21,10 @@ use std::str::FromStr; fn usage() -> ! { let name = env::args().next().unwrap(); eprintln!( - "usage: {} [-rR] [-c CONFIG] [-d OUT_DIR] maildir + "usage: {} [-rR] [-c CONFIG] [-d OUT_DIR] MAIDLIR + +MAILDIR A directory containing the maildirs of lists you want to parse + FLAGS: -g include gemini output @@ -40,6 +43,7 @@ pub struct Args { pub positional: Vec<OsString>, pub a: i32, // placeholder pub include_gemini: bool, + pub no_index: bool, } impl Args { diff --git a/src/main.rs b/src/main.rs @@ -48,7 +48,6 @@ impl Lists { for list in &mut self.lists { list.persist(); // todo somewhat awkward - write_if_unchanged(&list.out_dir.join("style.css"), css); } } } @@ -149,6 +148,7 @@ fn main() -> Result<()> { config.out_dir = args.out_dir; INSTANCE.set(config).unwrap(); + // TODO allow one level lower -- one list etc let mut lists = Lists { lists: vec![], out_dir: Config::global().out_dir.clone(), diff --git a/src/templates/gmi.rs b/src/templates/gmi.rs @@ -7,11 +7,16 @@ use nanotemplate::template; impl Lists { pub fn to_gmi(&self) -> String { + let mut lists = String::new(); + for list in &self.lists { + lists.push_str(&format!("=> ./{0} {0}\n", &h(&list.config.name))); + } + // this looks stupid ok I know template( - r#" - # Mail Archive + r#"# Mail Archives +{lists} "#, - &[("title", "tbd")], + &[("lists", &lists)], ) .unwrap() } @@ -20,7 +25,7 @@ impl Lists { impl List { pub fn to_gmi(&self) -> Vec<String> { // TODO paginate - let mut threads = "# list name".to_string(); + let mut threads = format!("## {}\n", self.config.name); for thread in &self.thread_topics { threads.push_str( // TODO reuse with html templates? diff --git a/src/templates/html.rs b/src/templates/html.rs @@ -12,7 +12,7 @@ const header: &str = r#"<!DOCTYPE html> <head> <title>{title}</title> <meta http-equiv='Permissions-Policy' content='interest-cohort=()'/> -<link rel='stylesheet' type='text/css' href='../style.css' /> +<link rel='stylesheet' type='text/css' href='{css_path}' /> <meta name='viewport' content='width=device-width, initial-scale=1.0, maximum-scale=1.0,user-scalable=0' /> <link rel='icon' href='data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>📧</text></svg>'></head> <meta name="description" content="{title}"/> @@ -28,11 +28,24 @@ Archive generated with <a href='https://crabmail.flounder.online/'>crabmail</a> impl Lists { pub fn to_html(&self) -> String { + let mut lists = String::new(); + for list in &self.lists { + lists.push_str(&format!( + "<a href='./{0}'><h2>{0}</h2></a>\n", + &x(&list.config.name) + )); + } let body = r#"<h1 class="page-title">{title}</h1> - <a href="atom.xml"><img alt="Atom Feed" src='{rss_svg}' /></a>"#; + <hr> + {lists} + <hr>"#; template( &format!("{}{}{}", header, body, footer), - &[("rss_svg", RSS_SVG), ("title", "tbd")], + &[ + ("title", "Mail Archives"), + ("css_path", "style.css"), + ("lists", &lists), + ], ) .unwrap() } @@ -95,6 +108,7 @@ impl List { ("header", header), ("description", &self.config.description), ("title", self.config.title.as_str()), + ("css_path", "../style.css"), ("threads", &threads), ("list_email", &self.config.email), ("rss_svg", RSS_SVG), @@ -138,6 +152,7 @@ impl Thread { // TODO html escape &[ ("title", x(&root.subject).as_ref()), + ("css_path", "../../style.css"), ("rss_svg", RSS_SVG), ("path_id", &x(root.pathescape_msg_id().to_str().unwrap())), ],