1 parent
0421994fe6
commit
425ea7449b
138 files changed
+5705
-1012
No files matched your search
+122
-1
@@ -46,6 +46,36 @@ pub struct AppConfig {
|
||||
#[serde(default)]
|
||||
pub log_dir: Option<String>,
|
||||
|
||||
#[serde(default = "default_database_backend")]
|
||||
pub database_backend: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub database_url: Option<String>,
|
||||
|
||||
#[serde(default = "default_session_backend")]
|
||||
pub session_backend: String,
|
||||
|
||||
#[serde(default)]
|
||||
pub session_encryption_key: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub redis_url: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub redis_sentinel_nodes: Vec<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub redis_sentinel_master: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub redis_password: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub redis_sentinel_password: Option<String>,
|
||||
|
||||
#[serde(default)]
|
||||
pub cluster_mode: bool,
|
||||
|
||||
#[serde(default = "default_session_timeout_hours")]
|
||||
pub session_timeout_hours: u64,
|
||||
|
||||
@@ -169,6 +199,14 @@ fn default_data_dir() -> String {
|
||||
"/data".to_string()
|
||||
}
|
||||
|
||||
fn default_database_backend() -> String {
|
||||
"sqlite".to_string()
|
||||
}
|
||||
|
||||
fn default_session_backend() -> String {
|
||||
"database".to_string()
|
||||
}
|
||||
|
||||
fn default_session_timeout_hours() -> u64 {
|
||||
24
|
||||
}
|
||||
@@ -232,6 +270,16 @@ impl Default for AppConfig {
|
||||
max_message_size_mb: default_max_message_size_mb(),
|
||||
data_dir: default_data_dir(),
|
||||
log_dir: None,
|
||||
database_backend: default_database_backend(),
|
||||
database_url: None,
|
||||
session_backend: default_session_backend(),
|
||||
session_encryption_key: None,
|
||||
redis_url: None,
|
||||
redis_sentinel_nodes: Vec::new(),
|
||||
redis_sentinel_master: None,
|
||||
redis_password: None,
|
||||
redis_sentinel_password: None,
|
||||
cluster_mode: false,
|
||||
session_timeout_hours: default_session_timeout_hours(),
|
||||
remember_me_enabled: default_remember_me_enabled(),
|
||||
remember_me_days: default_remember_me_days(),
|
||||
@@ -407,6 +455,64 @@ impl AppConfig {
|
||||
if self.max_attachment_size_mb > self.max_message_size_mb {
|
||||
return Err("MAX_ATTACHMENT_SIZE_MB cannot exceed MAX_MESSAGE_SIZE_MB".to_string());
|
||||
}
|
||||
if !matches!(
|
||||
self.database_backend.as_str(),
|
||||
"sqlite" | "mysql" | "pgsql" | "postgres" | "postgresql"
|
||||
) {
|
||||
return Err("DATABASE_BACKEND must be one of: sqlite, mysql, pgsql".to_string());
|
||||
}
|
||||
if self.database_backend != "sqlite"
|
||||
&& self
|
||||
.database_url
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|v| !v.is_empty())
|
||||
.is_none()
|
||||
{
|
||||
return Err("DATABASE_URL is required for mysql/pgsql".to_string());
|
||||
}
|
||||
if !matches!(self.session_backend.as_str(), "database" | "redis") {
|
||||
return Err("SESSION_BACKEND must be one of: database, redis".to_string());
|
||||
}
|
||||
if self.session_backend == "redis" {
|
||||
let direct = self
|
||||
.redis_url
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|v| !v.is_empty())
|
||||
.is_some();
|
||||
let sentinel = !self.redis_sentinel_nodes.is_empty()
|
||||
&& self
|
||||
.redis_sentinel_master
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|v| !v.is_empty())
|
||||
.is_some();
|
||||
if !direct && !sentinel {
|
||||
return Err("Redis sessions require REDIS_URL or REDIS_SENTINEL_NODES + REDIS_SENTINEL_MASTER".to_string());
|
||||
}
|
||||
}
|
||||
if self.cluster_mode
|
||||
&& self.session_backend == "database"
|
||||
&& self.database_backend == "sqlite"
|
||||
{
|
||||
return Err("CLUSTER_MODE with database sessions requires mysql/pgsql, or configure SESSION_BACKEND=redis".to_string());
|
||||
}
|
||||
if (self.cluster_mode
|
||||
|| self.session_backend == "redis"
|
||||
|| self.database_backend != "sqlite")
|
||||
&& self
|
||||
.session_encryption_key
|
||||
.as_deref()
|
||||
.map(str::trim)
|
||||
.filter(|v| !v.is_empty())
|
||||
.is_none()
|
||||
{
|
||||
return Err(
|
||||
"shared session storage requires SESSION_ENCRYPTION_KEY shared by all nodes"
|
||||
.to_string(),
|
||||
);
|
||||
}
|
||||
if self.session_timeout_hours == 0 || self.session_timeout_hours > 24 * 365 {
|
||||
return Err("SESSION_TIMEOUT_HOURS must be between 1 and 8760".to_string());
|
||||
}
|
||||
@@ -435,8 +541,14 @@ impl AppConfig {
|
||||
| "yahoo"
|
||||
| "hey"
|
||||
| "nord"
|
||||
| "github"
|
||||
| "gitlab"
|
||||
| "discord"
|
||||
| "slack"
|
||||
| "reddit"
|
||||
| "notion"
|
||||
) {
|
||||
return Err("DEFAULT_INTERFACE_SKIN must be one of: rustbox, roundcube-classic, roundcube-larry, roundcube-elastic, clean, gmail, proton, zoho, outlook, minimal, graphite, high-contrast, fastmail, apple-mail, yahoo, hey, nord".to_string());
|
||||
return Err("DEFAULT_INTERFACE_SKIN must be one of: rustbox, roundcube-classic, roundcube-larry, roundcube-elastic, clean, gmail, proton, zoho, outlook, minimal, graphite, high-contrast, fastmail, apple-mail, yahoo, hey, nord, github, gitlab, discord, slack, reddit, notion".to_string());
|
||||
}
|
||||
if !matches!(
|
||||
self.sieve_tls_mode.as_str(),
|
||||
@@ -489,6 +601,15 @@ mod tests {
|
||||
assert_eq!(config.max_message_size_mb, 50);
|
||||
assert_eq!(config.data_dir, "/data");
|
||||
assert!(config.log_dir.is_none());
|
||||
assert_eq!(config.database_backend, "sqlite");
|
||||
assert!(config.database_url.is_none());
|
||||
assert_eq!(config.session_backend, "database");
|
||||
assert!(config.session_encryption_key.is_none());
|
||||
assert!(config.redis_url.is_none());
|
||||
assert!(config.redis_sentinel_nodes.is_empty());
|
||||
assert!(config.redis_sentinel_master.is_none());
|
||||
assert!(config.redis_sentinel_password.is_none());
|
||||
assert!(!config.cluster_mode);
|
||||
assert_eq!(config.session_timeout_hours, 24);
|
||||
assert!(config.remember_me_enabled);
|
||||
assert_eq!(config.remember_me_days, 30);
|
||||
|
||||
Reference in new issue
Block a user