283 lines
11 KiB
Rust
283 lines
11 KiB
Rust
use std::env;
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Debug)]
|
|
pub struct Config {
|
|
pub port: u16,
|
|
pub data_dir: PathBuf,
|
|
pub site_title: String,
|
|
|
|
pub telegram_bot_token: Option<String>,
|
|
pub telegram_chat_id: Option<i64>,
|
|
pub enable_honeypot: bool,
|
|
pub max_name_length: usize,
|
|
pub max_message_length: usize,
|
|
pub max_website_length: usize,
|
|
pub enable_submissions: bool,
|
|
pub enable_website_links: bool,
|
|
pub enable_html_injection: bool,
|
|
pub enable_captcha: bool,
|
|
pub captcha_question: String,
|
|
pub captcha_answer: String,
|
|
pub captcha_exact: bool,
|
|
pub captcha_casesensitive: bool,
|
|
pub enable_drawings: bool,
|
|
pub label_drawing: String,
|
|
pub canvas_width: u32,
|
|
pub canvas_height: u32,
|
|
pub template: Option<String>,
|
|
pub separator: String,
|
|
pub style: String,
|
|
pub form_prompt: String,
|
|
pub button_text: String,
|
|
pub label_name: String,
|
|
pub label_website: String,
|
|
pub label_message: String,
|
|
pub textarea_rows: u32,
|
|
pub textarea_cols: u32,
|
|
}
|
|
|
|
impl Config {
|
|
pub fn listen_addr(&self) -> String {
|
|
format!("127.0.0.1:{}", self.port)
|
|
}
|
|
|
|
/// Maximum drawing file size: width * height * 4 (raw RGBA).
|
|
/// Any valid PNG from the configured canvas will be smaller than this.
|
|
pub fn max_drawing_bytes(&self) -> usize {
|
|
self.canvas_width as usize * self.canvas_height as usize * 4
|
|
}
|
|
|
|
pub fn from_env() -> Result<Self, String> {
|
|
Ok(Config {
|
|
port: env::var("BOOK_PORT")
|
|
.unwrap_or_else(|_| "8123".into())
|
|
.parse()
|
|
.map_err(|_| "BOOK_PORT must be a number")?,
|
|
data_dir: env::var("BOOK_DATA_DIR")
|
|
.map(PathBuf::from)
|
|
.unwrap_or_else(|_| PathBuf::from("./data")),
|
|
site_title: env::var("BOOK_SITE_TITLE").unwrap_or_else(|_| "guestbook".into()),
|
|
|
|
telegram_bot_token: env::var("BOOK_TELEGRAM_BOT_TOKEN").ok(),
|
|
telegram_chat_id: env::var("BOOK_TELEGRAM_CHAT_ID")
|
|
.ok()
|
|
.map(|v| v.parse().map_err(|_| "BOOK_TELEGRAM_CHAT_ID must be an integer"))
|
|
.transpose()?,
|
|
enable_honeypot: env::var("BOOK_ENABLE_HONEYPOT")
|
|
.map(|v| v != "false")
|
|
.unwrap_or(true),
|
|
max_name_length: env::var("BOOK_MAX_NAME_LENGTH")
|
|
.unwrap_or_else(|_| "0".into())
|
|
.parse()
|
|
.map_err(|_| "BOOK_MAX_NAME_LENGTH must be a number")?,
|
|
max_message_length: env::var("BOOK_MAX_MESSAGE_LENGTH")
|
|
.unwrap_or_else(|_| "0".into())
|
|
.parse()
|
|
.map_err(|_| "BOOK_MAX_MESSAGE_LENGTH must be a number")?,
|
|
max_website_length: env::var("BOOK_MAX_WEBSITE_LENGTH")
|
|
.unwrap_or_else(|_| "0".into())
|
|
.parse()
|
|
.map_err(|_| "BOOK_MAX_WEBSITE_LENGTH must be a number")?,
|
|
enable_submissions: env::var("BOOK_ENABLE_SUBMISSIONS")
|
|
.map(|v| v != "false")
|
|
.unwrap_or(true),
|
|
enable_website_links: env::var("BOOK_ENABLE_WEBSITE_LINKS")
|
|
.map(|v| v != "false")
|
|
.unwrap_or(true),
|
|
enable_html_injection: env::var("BOOK_ENABLE_HTML_INJECTION")
|
|
.map(|v| v != "false")
|
|
.unwrap_or(false),
|
|
enable_captcha: env::var("BOOK_ENABLE_CAPTCHA")
|
|
.map(|v| v != "false")
|
|
.unwrap_or(false),
|
|
captcha_question: env::var("BOOK_CAPTCHA_QUESTION")
|
|
.unwrap_or_default(),
|
|
captcha_answer: env::var("BOOK_CAPTCHA_ANSWER")
|
|
.unwrap_or_default(),
|
|
captcha_exact: env::var("BOOK_CAPTCHA_EXACT")
|
|
.map(|v| v != "false")
|
|
.unwrap_or(false),
|
|
captcha_casesensitive: env::var("BOOK_CAPTCHA_CASESENSITIVE")
|
|
.map(|v| v != "false")
|
|
.unwrap_or(false),
|
|
enable_drawings: env::var("BOOK_ENABLE_DRAWINGS")
|
|
.map(|v| v != "false")
|
|
.unwrap_or(false),
|
|
label_drawing: env::var("BOOK_LABEL_DRAWING")
|
|
.unwrap_or_else(|_| "Draw (optional):".into()),
|
|
canvas_width: env::var("BOOK_CANVAS_WIDTH")
|
|
.unwrap_or_else(|_| "400".into())
|
|
.parse()
|
|
.map_err(|_| "BOOK_CANVAS_WIDTH must be a number")?,
|
|
canvas_height: env::var("BOOK_CANVAS_HEIGHT")
|
|
.unwrap_or_else(|_| "200".into())
|
|
.parse()
|
|
.map_err(|_| "BOOK_CANVAS_HEIGHT must be a number")?,
|
|
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}"))
|
|
}),
|
|
style: env::var("BOOK_STYLE_FILE")
|
|
.ok()
|
|
.map(|path| {
|
|
std::fs::read_to_string(&path)
|
|
.unwrap_or_else(|e| panic!("failed to read style file {path}: {e}"))
|
|
})
|
|
.or_else(|| env::var("BOOK_STYLE").ok())
|
|
.unwrap_or_default(),
|
|
form_prompt: env::var("BOOK_FORM_PROMPT")
|
|
.unwrap_or_else(|_| "Thanks for visiting. Sign the guestbook!".into()),
|
|
button_text: env::var("BOOK_BUTTON_TEXT")
|
|
.unwrap_or_else(|_| "sign".into()),
|
|
label_name: env::var("BOOK_LABEL_NAME")
|
|
.unwrap_or_else(|_| "Your name:".into()),
|
|
label_website: env::var("BOOK_LABEL_WEBSITE")
|
|
.unwrap_or_else(|_| "Your website (optional):".into()),
|
|
label_message: env::var("BOOK_LABEL_MESSAGE")
|
|
.unwrap_or_else(|_| "Your message:".into()),
|
|
textarea_rows: env::var("BOOK_TEXTAREA_ROWS")
|
|
.unwrap_or_else(|_| "8".into())
|
|
.parse()
|
|
.map_err(|_| "BOOK_TEXTAREA_ROWS must be a number")?,
|
|
textarea_cols: env::var("BOOK_TEXTAREA_COLS")
|
|
.unwrap_or_else(|_| "60".into())
|
|
.parse()
|
|
.map_err(|_| "BOOK_TEXTAREA_COLS must be a number")?,
|
|
})
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use std::sync::Mutex;
|
|
|
|
static ENV_LOCK: Mutex<()> = Mutex::new(());
|
|
|
|
#[test]
|
|
fn test_from_env() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
env::set_var("BOOK_PORT", "9999");
|
|
env::set_var("BOOK_DATA_DIR", "/tmp/gb");
|
|
env::set_var("BOOK_SITE_TITLE", "test.rs");
|
|
env::set_var("BOOK_TELEGRAM_BOT_TOKEN", "123:ABC");
|
|
env::set_var("BOOK_TELEGRAM_CHAT_ID", "12345");
|
|
|
|
let config = Config::from_env().unwrap();
|
|
assert_eq!(config.port, 9999);
|
|
assert_eq!(config.listen_addr(), "127.0.0.1:9999");
|
|
assert_eq!(config.data_dir, PathBuf::from("/tmp/gb"));
|
|
assert_eq!(config.site_title, "test.rs");
|
|
assert_eq!(config.telegram_bot_token.as_deref(), Some("123:ABC"));
|
|
assert_eq!(config.telegram_chat_id, Some(12345));
|
|
|
|
// Clean up
|
|
env::remove_var("BOOK_PORT");
|
|
env::remove_var("BOOK_DATA_DIR");
|
|
env::remove_var("BOOK_SITE_TITLE");
|
|
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
|
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
|
}
|
|
|
|
#[test]
|
|
fn test_defaults() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
env::set_var("BOOK_TELEGRAM_BOT_TOKEN", "123:ABC");
|
|
env::set_var("BOOK_TELEGRAM_CHAT_ID", "12345");
|
|
|
|
let config = Config::from_env().unwrap();
|
|
assert_eq!(config.port, 8123);
|
|
assert_eq!(config.data_dir, PathBuf::from("./data"));
|
|
assert_eq!(config.site_title, "guestbook");
|
|
|
|
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
|
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
|
}
|
|
|
|
#[test]
|
|
fn test_telegram_optional() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
|
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
|
|
|
let config = Config::from_env().unwrap();
|
|
assert!(config.telegram_bot_token.is_none());
|
|
assert!(config.telegram_chat_id.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn test_enable_website_links_default() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
env::set_var("BOOK_TELEGRAM_BOT_TOKEN", "123:ABC");
|
|
env::set_var("BOOK_TELEGRAM_CHAT_ID", "12345");
|
|
env::remove_var("BOOK_ENABLE_WEBSITE_LINKS");
|
|
|
|
let config = Config::from_env().unwrap();
|
|
assert!(config.enable_website_links);
|
|
|
|
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
|
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
|
}
|
|
|
|
#[test]
|
|
fn test_enable_website_links_false() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
env::set_var("BOOK_TELEGRAM_BOT_TOKEN", "123:ABC");
|
|
env::set_var("BOOK_TELEGRAM_CHAT_ID", "12345");
|
|
env::set_var("BOOK_ENABLE_WEBSITE_LINKS", "false");
|
|
|
|
let config = Config::from_env().unwrap();
|
|
assert!(!config.enable_website_links);
|
|
|
|
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
|
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
|
env::remove_var("BOOK_ENABLE_WEBSITE_LINKS");
|
|
}
|
|
|
|
#[test]
|
|
fn test_enable_html_injection_default() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
env::set_var("BOOK_TELEGRAM_BOT_TOKEN", "123:ABC");
|
|
env::set_var("BOOK_TELEGRAM_CHAT_ID", "12345");
|
|
env::remove_var("BOOK_ENABLE_HTML_INJECTION");
|
|
|
|
let config = Config::from_env().unwrap();
|
|
assert!(!config.enable_html_injection);
|
|
|
|
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
|
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
|
}
|
|
|
|
#[test]
|
|
fn test_enable_html_injection_true() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
env::set_var("BOOK_TELEGRAM_BOT_TOKEN", "123:ABC");
|
|
env::set_var("BOOK_TELEGRAM_CHAT_ID", "12345");
|
|
env::set_var("BOOK_ENABLE_HTML_INJECTION", "true");
|
|
|
|
let config = Config::from_env().unwrap();
|
|
assert!(config.enable_html_injection);
|
|
|
|
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
|
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
|
env::remove_var("BOOK_ENABLE_HTML_INJECTION");
|
|
}
|
|
|
|
#[test]
|
|
fn test_enable_drawings_default() {
|
|
let _lock = ENV_LOCK.lock().unwrap();
|
|
env::remove_var("BOOK_ENABLE_DRAWINGS");
|
|
env::remove_var("BOOK_TELEGRAM_BOT_TOKEN");
|
|
env::remove_var("BOOK_TELEGRAM_CHAT_ID");
|
|
|
|
let config = Config::from_env().unwrap();
|
|
assert!(!config.enable_drawings);
|
|
assert_eq!(config.canvas_width, 400);
|
|
assert_eq!(config.canvas_height, 200);
|
|
assert_eq!(config.max_drawing_bytes(), 400 * 200 * 4);
|
|
assert_eq!(config.label_drawing, "Draw (optional):");
|
|
}
|
|
}
|