rusmes_cli/commands/
init.rs1use anyhow::{Context, Result};
4use std::fs;
5use std::path::Path;
6
7pub fn run(domain: &str) -> Result<()> {
9 println!("Initializing RusMES for domain: {}", domain);
10
11 let config = generate_default_config(domain);
13
14 fs::write("rusmes.toml", config).context("Failed to write rusmes.toml")?;
16 println!("Created rusmes.toml");
17
18 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
30fn 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
125fn 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}