diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..28e5594 --- /dev/null +++ b/.env.example @@ -0,0 +1,13 @@ +BOOK_PORT=8123 +BOOK_DATA_DIR=./data +BOOK_SITE_TITLE=guestbook +BOOK_SITE_URL=https://example.com +BOOK_TELEGRAM_BOT_TOKEN=your-bot-token-here +BOOK_TELEGRAM_CHAT_ID=0 +BOOK_HONEYPOT=true +BOOK_MAX_NAME_LENGTH=50 +BOOK_MAX_MESSAGE_LENGTH=1000 +BOOK_MAX_WEBSITE_LENGTH=100 +BOOK_OPEN_REGISTRATION=true +BOOK_SEPARATOR=------------------------------------------------------------ +# BOOK_TEMPLATE=./templates/default.html diff --git a/module.nix b/module.nix index f6409fd..577bbf6 100644 --- a/module.nix +++ b/module.nix @@ -75,6 +75,12 @@ in description = "Allow new guestbook submissions. When false, the form is hidden and submissions are rejected."; }; + separator = mkOption { + type = types.str; + default = "------------------------------------------------------------"; + description = "Separator between guestbook entries."; + }; + templateFile = mkOption { type = types.nullOr types.path; default = null; @@ -121,6 +127,7 @@ in BOOK_MAX_MESSAGE_LENGTH = toString cfg.maxMessageLength; BOOK_MAX_WEBSITE_LENGTH = toString cfg.maxWebsiteLength; BOOK_OPEN_REGISTRATION = if cfg.openRegistration then "true" else "false"; + BOOK_SEPARATOR = cfg.separator; } // lib.optionalAttrs (cfg.templateFile != null) { BOOK_TEMPLATE = cfg.templateFile; }; diff --git a/src/config.rs b/src/config.rs index 769d263..af4798f 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,6 +15,7 @@ pub struct Config { pub max_website_length: usize, pub open_registration: bool, pub template: Option, + pub separator: String, } impl Config { @@ -57,6 +58,8 @@ impl Config { open_registration: env::var("BOOK_OPEN_REGISTRATION") .map(|v| v != "false") .unwrap_or(true), + separator: env::var("BOOK_SEPARATOR") + .unwrap_or_else(|_| "------------------------------------------------------------".into()), template: env::var("BOOK_TEMPLATE").ok().map(|path| { std::fs::read_to_string(&path) .unwrap_or_else(|e| panic!("failed to read template {path}: {e}")) diff --git a/src/render.rs b/src/render.rs index a029320..a55bd10 100644 --- a/src/render.rs +++ b/src/render.rs @@ -35,23 +35,23 @@ entries "#; -pub fn render_page(template: &str, title: &str, entries: &[Entry], form_html: &str) -> String { - let entries_html = render_entries(entries); +pub fn render_page(template: &str, title: &str, entries: &[Entry], form_html: &str, separator: &str) -> String { + let entries_html = render_entries(entries, separator); template .replace("{{title}}", title) .replace("{{form}}", form_html) .replace("{{entries}}", &entries_html) } -fn render_entries(entries: &[Entry]) -> String { +fn render_entries(entries: &[Entry], separator: &str) -> String { let mut html = String::new(); for entry in entries { - html.push_str(&render_entry(entry)); + html.push_str(&render_entry(entry, separator)); } html } -fn render_entry(entry: &Entry) -> String { +fn render_entry(entry: &Entry, separator: &str) -> String { let mut header = format!("{} - {}", entry.meta.date, entry.meta.name); if !entry.meta.website.is_empty() { header.push_str(&format!( @@ -60,7 +60,7 @@ fn render_entry(entry: &Entry) -> String { )); } format!( - "\n{header}\n\n{}\n\n------------------------------------------------------------\n", + "\n{header}\n\n{}\n\n{separator}\n", entry.body ) } @@ -99,16 +99,16 @@ mod tests { #[test] fn test_render_default_template() { - let html = render_page(DEFAULT_TEMPLATE, "ily.rs", &[], FORM_HTML); + let html = render_page(DEFAULT_TEMPLATE, "ily.rs", &[], FORM_HTML, "---"); assert!(html.contains("ily.rs")); assert!(html.contains("action=\"/submit\"")); - assert!(html.contains("
"));
+        assert!(html.contains("
"));
     }
 
     #[test]
     fn test_render_custom_template() {
         let custom = "{{title}} {{form}} {{entries}}";
-        let html = render_page(custom, "my site", &[], FORM_HTML);
+        let html = render_page(custom, "my site", &[], FORM_HTML, "---");
         assert!(html.contains("my site"));
         assert!(html.contains("action=\"/submit\""));
     }
@@ -116,31 +116,31 @@ mod tests {
     #[test]
     fn test_render_entry_no_website() {
         let entry = make_entry("alice", "2026-04-09", "Hello!");
-        let html = render_page(DEFAULT_TEMPLATE, "test", &[entry], FORM_HTML);
+        let html = render_page(DEFAULT_TEMPLATE, "test", &[entry], FORM_HTML, "---");
         assert!(html.contains("2026-04-09 - alice"));
         assert!(html.contains("Hello!"));
-        assert!(html.contains("----"));
+        assert!(html.contains("---"));
     }
 
     #[test]
     fn test_render_entry_with_website() {
         let mut entry = make_entry("bob", "2026-04-09", "Hi!");
         entry.meta.website = "https://bob.com".into();
-        let html = render_page(DEFAULT_TEMPLATE, "test", &[entry], FORM_HTML);
+        let html = render_page(DEFAULT_TEMPLATE, "test", &[entry], FORM_HTML, "---");
         assert!(html.contains(r#""#));
     }
 
     #[test]
     fn test_render_preserves_html_in_body() {
         let entry = make_entry("carol", "2026-04-09", "Bold ");
-        let html = render_page(DEFAULT_TEMPLATE, "test", &[entry], FORM_HTML);
+        let html = render_page(DEFAULT_TEMPLATE, "test", &[entry], FORM_HTML, "---");
         assert!(html.contains("Bold"));
         assert!(html.contains(""));
     }
 
     #[test]
     fn test_render_empty_form_when_closed() {
-        let html = render_page(DEFAULT_TEMPLATE, "test", &[], "");
+        let html = render_page(DEFAULT_TEMPLATE, "test", &[], "", "---");
         assert!(!html.contains("action=\"/submit\""));
     }
 }
diff --git a/src/web.rs b/src/web.rs
index 5d2e92b..75a1049 100644
--- a/src/web.rs
+++ b/src/web.rs
@@ -44,6 +44,7 @@ async fn index(State(state): State>) -> Html {
         &state.config.site_title,
         &entries,
         form,
+        &state.config.separator,
     );
     Html(html)
 }
@@ -134,6 +135,7 @@ mod tests {
             max_website_length: 100,
             open_registration: true,
             template: None,
+            separator: "---".into(),
         }
     }