use anyhow::{Context, Result};
use std::fs;
use std::path::Path;
pub fn run(domain: &str) -> Result<()> {
println!("Initializing RusMES for domain: {}", domain);
let config = generate_default_config(domain);
fs::write("rusmes.toml", config).context("Failed to write rusmes.toml")?;
println!("Created rusmes.toml");
create_directories()?;
println!("Initialization complete!");
println!("\nNext steps:");
println!(" 1. Review rusmes.toml and adjust settings");
println!(" 2. Create a user: rusmes user add <email> --password <password>");
println!(" 3. Start the server: rusmes start");
Ok(())
}
fn generate_default_config(domain: &str) -> String {
format!(
r#"# RusMES Configuration
# Generated by: rusmes init
domain = "{domain}"
postmaster = "postmaster@{domain}"
[smtp]
host = "0.0.0.0"
port = 25
tls_port = 587
max_message_size = "50MB"
require_auth = false
enable_starttls = true
[imap]
host = "0.0.0.0"
port = 143
tls_port = 993
[jmap]
host = "0.0.0.0"
port = 8080
base_url = "http://localhost:8080"
[storage]
backend = "filesystem"
path = "./data/mailboxes"
[auth]
backend = "file"
path = "./data/users.db"
[logging]
level = "info"
format = "text"
output = "stdout"
[queue]
initial_delay = "60s"
max_delay = "3600s"
backoff_multiplier = 2.0
max_attempts = 5
worker_threads = 4
batch_size = 100
[security]
relay_networks = ["127.0.0.0/8"]
blocked_ips = []
check_recipient_exists = true
reject_unknown_recipients = true
[domains]
local_domains = ["{domain}"]
[domains.aliases]
"abuse@{domain}" = "postmaster@{domain}"
"webmaster@{domain}" = "postmaster@{domain}"
[metrics]
enabled = true
bind_address = "0.0.0.0:9090"
path = "/metrics"
# Processing chain
[[processors]]
name = "root"
state = "root"
[[processors.mailets]]
matcher = "All"
mailet = "AddHeader"
[processors.mailets.params]
name = "X-RusMES"
value = "1.0"
[[processors]]
name = "transport"
state = "transport"
[[processors.mailets]]
matcher = "RecipientIsLocal"
mailet = "LocalDelivery"
[[processors.mailets]]
matcher = "All"
mailet = "RemoteDelivery"
"#,
domain = domain
)
}
fn create_directories() -> Result<()> {
let dirs = ["./data", "./data/mailboxes", "./data/queue", "./data/logs"];
for dir in &dirs {
let path = Path::new(dir);
if !path.exists() {
fs::create_dir_all(path)
.with_context(|| format!("Failed to create directory: {}", dir))?;
println!("Created directory: {}", dir);
}
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_generate_default_config() {
let config = generate_default_config("example.com");
assert!(config.contains("example.com"));
assert!(config.contains("postmaster@example.com"));
assert!(config.contains("[smtp]"));
assert!(config.contains("[imap]"));
assert!(config.contains("[jmap]"));
}
#[test]
fn test_config_contains_all_sections() {
let config = generate_default_config("test.org");
assert!(config.contains("[storage]"));
assert!(config.contains("[auth]"));
assert!(config.contains("[logging]"));
assert!(config.contains("[queue]"));
assert!(config.contains("[security]"));
assert!(config.contains("[domains]"));
assert!(config.contains("[metrics]"));
assert!(config.contains("[[processors]]"));
}
#[test]
fn test_config_smtp_defaults() {
let config = generate_default_config("mail.example.com");
assert!(config.contains("port = 25"));
assert!(config.contains("tls_port = 587"));
assert!(config.contains("max_message_size = \"50MB\""));
}
#[test]
fn test_config_imap_defaults() {
let config = generate_default_config("mail.example.com");
assert!(config.contains("port = 143"));
assert!(config.contains("tls_port = 993"));
}
#[test]
fn test_config_aliases() {
let config = generate_default_config("example.org");
assert!(config.contains("abuse@example.org"));
assert!(config.contains("webmaster@example.org"));
}
#[test]
fn test_config_processors() {
let config = generate_default_config("test.com");
assert!(config.contains("name = \"root\""));
assert!(config.contains("state = \"root\""));
assert!(config.contains("LocalDelivery"));
assert!(config.contains("RemoteDelivery"));
}
}