Skip to main content

rusmes_cli/commands/
init.rs

1//! Initialize a new RusMES installation
2
3use anyhow::{Context, Result};
4use std::fs;
5use std::path::Path;
6
7/// Initialize RusMES with default configuration
8pub fn run(domain: &str) -> Result<()> {
9    println!("Initializing RusMES for domain: {}", domain);
10
11    // Create default configuration
12    let config = generate_default_config(domain);
13
14    // Write rusmes.toml
15    fs::write("rusmes.toml", config).context("Failed to write rusmes.toml")?;
16    println!("Created rusmes.toml");
17
18    // Create data directories
19    create_directories()?;
20
21    println!("Initialization complete!");
22    println!("\nNext steps:");
23    println!("  1. Review rusmes.toml and adjust settings");
24    println!("  2. Create a user: rusmes user add <email> --password <password>");
25    println!("  3. Start the server: rusmes start");
26
27    Ok(())
28}
29
30/// Generate default configuration file content
31fn generate_default_config(domain: &str) -> String {
32    format!(
33        r#"# RusMES Configuration
34# Generated by: rusmes init
35
36domain = "{domain}"
37postmaster = "postmaster@{domain}"
38
39[smtp]
40host = "0.0.0.0"
41port = 25
42tls_port = 587
43max_message_size = "50MB"
44require_auth = false
45enable_starttls = true
46
47[imap]
48host = "0.0.0.0"
49port = 143
50tls_port = 993
51
52[jmap]
53host = "0.0.0.0"
54port = 8080
55base_url = "http://localhost:8080"
56
57[storage]
58backend = "filesystem"
59path = "./data/mailboxes"
60
61[auth]
62backend = "file"
63path = "./data/users.db"
64
65[logging]
66level = "info"
67format = "text"
68output = "stdout"
69
70[queue]
71initial_delay = "60s"
72max_delay = "3600s"
73backoff_multiplier = 2.0
74max_attempts = 5
75worker_threads = 4
76batch_size = 100
77
78[security]
79relay_networks = ["127.0.0.0/8"]
80blocked_ips = []
81check_recipient_exists = true
82reject_unknown_recipients = true
83
84[domains]
85local_domains = ["{domain}"]
86
87[domains.aliases]
88"abuse@{domain}" = "postmaster@{domain}"
89"webmaster@{domain}" = "postmaster@{domain}"
90
91[metrics]
92enabled = true
93bind_address = "0.0.0.0:9090"
94path = "/metrics"
95
96# Processing chain
97[[processors]]
98name = "root"
99state = "root"
100
101[[processors.mailets]]
102matcher = "All"
103mailet = "AddHeader"
104
105[processors.mailets.params]
106name = "X-RusMES"
107value = "1.0"
108
109[[processors]]
110name = "transport"
111state = "transport"
112
113[[processors.mailets]]
114matcher = "RecipientIsLocal"
115mailet = "LocalDelivery"
116
117[[processors.mailets]]
118matcher = "All"
119mailet = "RemoteDelivery"
120"#,
121        domain = domain
122    )
123}
124
125/// Create necessary data directories
126fn create_directories() -> Result<()> {
127    let dirs = ["./data", "./data/mailboxes", "./data/queue", "./data/logs"];
128
129    for dir in &dirs {
130        let path = Path::new(dir);
131        if !path.exists() {
132            fs::create_dir_all(path)
133                .with_context(|| format!("Failed to create directory: {}", dir))?;
134            println!("Created directory: {}", dir);
135        }
136    }
137
138    Ok(())
139}
140
141#[cfg(test)]
142mod tests {
143    use super::*;
144
145    #[test]
146    fn test_generate_default_config() {
147        let config = generate_default_config("example.com");
148        assert!(config.contains("example.com"));
149        assert!(config.contains("postmaster@example.com"));
150        assert!(config.contains("[smtp]"));
151        assert!(config.contains("[imap]"));
152        assert!(config.contains("[jmap]"));
153    }
154
155    #[test]
156    fn test_config_contains_all_sections() {
157        let config = generate_default_config("test.org");
158        assert!(config.contains("[storage]"));
159        assert!(config.contains("[auth]"));
160        assert!(config.contains("[logging]"));
161        assert!(config.contains("[queue]"));
162        assert!(config.contains("[security]"));
163        assert!(config.contains("[domains]"));
164        assert!(config.contains("[metrics]"));
165        assert!(config.contains("[[processors]]"));
166    }
167
168    #[test]
169    fn test_config_smtp_defaults() {
170        let config = generate_default_config("mail.example.com");
171        assert!(config.contains("port = 25"));
172        assert!(config.contains("tls_port = 587"));
173        assert!(config.contains("max_message_size = \"50MB\""));
174    }
175
176    #[test]
177    fn test_config_imap_defaults() {
178        let config = generate_default_config("mail.example.com");
179        assert!(config.contains("port = 143"));
180        assert!(config.contains("tls_port = 993"));
181    }
182
183    #[test]
184    fn test_config_aliases() {
185        let config = generate_default_config("example.org");
186        assert!(config.contains("abuse@example.org"));
187        assert!(config.contains("webmaster@example.org"));
188    }
189
190    #[test]
191    fn test_config_processors() {
192        let config = generate_default_config("test.com");
193        assert!(config.contains("name = \"root\""));
194        assert!(config.contains("state = \"root\""));
195        assert!(config.contains("LocalDelivery"));
196        assert!(config.contains("RemoteDelivery"));
197    }
198}