webcomment/src/config.rs

210 lines
6.7 KiB
Rust

use serde::{Deserialize, Serialize};
use std::{
net::{IpAddr, Ipv4Addr, SocketAddr},
path::Path,
};
const CONFIG_FILE: &str = "config.toml";
#[derive(Deserialize, Serialize)]
pub struct Config {
#[serde(default = "Config::default_admin_passwords")]
pub admin_passwords: Vec<String>,
/// (seconds)
#[serde(default = "Config::default_antispam_duration")]
pub antispam_duration: u64,
#[serde(default = "Config::default_antispam_enable")]
pub antispam_enable: bool,
/// Maximum number of mutations by IP within antispam_duration
#[serde(default = "Config::default_antispam_mutation_limit")]
pub antispam_mutation_limit: u32,
#[serde(default = "Config::default_antispam_whitelist")]
pub antispam_whitelist: Vec<IpAddr>,
/// New or edited comments need admin's approval before being public
#[serde(default = "Config::default_comment_approve")]
pub comment_approve: bool,
/// Duration for which a comment can be edited
#[serde(default = "Config::default_comment_edit_timeout")]
pub comment_edit_timeout: u64,
#[serde(default = "Config::default_comment_author_max_len")]
pub comment_author_max_len: usize,
#[serde(default = "Config::default_comment_email_max_len")]
pub comment_email_max_len: usize,
#[serde(default = "Config::default_comment_text_max_len")]
pub comment_text_max_len: usize,
#[serde(default = "Config::default_cookies_https_only")]
pub cookies_https_only: bool,
#[serde(default = "Config::default_cookies_domain")]
pub cookies_domain: Option<String>,
#[serde(default = "Config::default_cors_allow_origin")]
pub cors_allow_origin: String,
/// Format: "language_REGION"
#[serde(default = "Config::default_default_lang")]
pub default_lang: String,
#[serde(default = "Config::default_listen")]
pub listen: SocketAddr,
/// Send a matrix message on new comment
#[serde(default = "Config::default_matrix_notify")]
pub matrix_notify: bool,
#[serde(default = "Config::default_matrix_password")]
pub matrix_password: String,
/// If connection fails, retry connecting after minimum this duration (seconds)
#[serde(default = "Config::default_matrix_retry_timeout")]
pub matrix_retry_timeout: u64,
#[serde(default = "Config::default_matrix_room")]
pub matrix_room: String,
#[serde(default = "Config::default_matrix_server")]
pub matrix_server: String,
#[serde(default = "Config::default_matrix_user")]
pub matrix_user: String,
/// Our address, with protocol and hostname but without path or trailing `/`
/// It is used to make hyperlinks that should be clickable from anywhere.
#[serde(default = "Config::default_public_address")]
pub public_address: String,
/// Are we behind a reverse proxy?
/// Determines whether we assume client address is in a Forwarded header or socket address.
#[serde(default = "Config::default_reverse_proxy")]
pub reverse_proxy: bool,
/// Our root url path (without protocol and hostname)
/// Useful when used behind a reverse proxy in a pseudo-subdirectory.
/// Should start and end with `/`.
#[serde(default = "Config::default_root_url")]
pub root_url: String,
/// Templates directory. May be absolute or relative to config/data directory.
#[serde(default = "Config::default_templates_dir")]
pub templates_dir: String,
}
impl Config {
fn default_admin_passwords() -> Vec<String> {
vec![]
}
fn default_antispam_duration() -> u64 {
3600
}
fn default_antispam_enable() -> bool {
true
}
fn default_antispam_mutation_limit() -> u32 {
10
}
fn default_antispam_whitelist() -> Vec<IpAddr> {
vec![[127u8, 0, 0, 1].into(), [0u8; 4].into(), [0u8; 16].into()]
}
fn default_comment_approve() -> bool {
true
}
fn default_comment_edit_timeout() -> u64 {
7 * 86400
}
fn default_comment_author_max_len() -> usize {
64
}
fn default_comment_email_max_len() -> usize {
128
}
fn default_comment_text_max_len() -> usize {
128 * 1024
}
fn default_cookies_https_only() -> bool {
false
}
fn default_cookies_domain() -> Option<String> {
None
}
fn default_cors_allow_origin() -> String {
"*".into()
}
fn default_default_lang() -> String {
"en_US".into()
}
fn default_listen() -> SocketAddr {
SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 31720)
}
fn default_matrix_notify() -> bool {
false
}
fn default_matrix_password() -> String {
"".into()
}
fn default_matrix_retry_timeout() -> u64 {
3600
}
fn default_matrix_room() -> String {
"#maintenance:matrix.txmn.tk".into()
}
fn default_matrix_server() -> String {
"https://matrix.txmn.tk".into()
}
fn default_matrix_user() -> String {
"@tuxmain:matrix.txmn.tk".into()
}
fn default_public_address() -> String {
"http://127.0.0.1:31720".into()
}
fn default_reverse_proxy() -> bool {
false
}
fn default_root_url() -> String {
"/".into()
}
fn default_templates_dir() -> String {
"templates".into()
}
}
impl Default for Config {
fn default() -> Self {
Self {
admin_passwords: Self::default_admin_passwords(),
antispam_duration: Self::default_antispam_duration(),
antispam_enable: Self::default_antispam_enable(),
antispam_mutation_limit: Self::default_antispam_mutation_limit(),
antispam_whitelist: Self::default_antispam_whitelist(),
comment_approve: Self::default_comment_approve(),
comment_edit_timeout: Self::default_comment_edit_timeout(),
comment_author_max_len: Self::default_comment_author_max_len(),
comment_email_max_len: Self::default_comment_email_max_len(),
comment_text_max_len: Self::default_comment_text_max_len(),
cookies_https_only: Self::default_cookies_https_only(),
cookies_domain: Self::default_cookies_domain(),
cors_allow_origin: Self::default_cors_allow_origin(),
default_lang: Self::default_default_lang(),
listen: Self::default_listen(),
matrix_notify: Self::default_matrix_notify(),
matrix_password: Self::default_matrix_password(),
matrix_retry_timeout: Self::default_matrix_retry_timeout(),
matrix_room: Self::default_matrix_room(),
matrix_server: Self::default_matrix_server(),
matrix_user: Self::default_matrix_user(),
public_address: Self::default_public_address(),
reverse_proxy: Self::default_reverse_proxy(),
root_url: Self::default_root_url(),
templates_dir: Self::default_templates_dir(),
}
}
}
pub fn read_config(dir: &Path) -> Config {
let path = dir.join(CONFIG_FILE);
if !path.is_file() {
let config = Config::default();
std::fs::write(path, toml_edit::easy::to_string_pretty(&config).unwrap())
.expect("Cannot write config file");
config
} else {
toml_edit::easy::from_str(
std::str::from_utf8(&std::fs::read(path).expect("Cannot read config file"))
.expect("Bad encoding in config file"),
)
.expect("Bad TOML in config file")
}
}
pub fn write_config(dir: &Path, config: &Config) {
let path = dir.join(CONFIG_FILE);
std::fs::write(path, toml_edit::easy::to_string_pretty(&config).unwrap())
.expect("Cannot write config file");
}