1#[cfg(feature = "plugin_auth")]
2use crate::auth::mail::{
3 auth_activated, auth_password_changed, auth_password_reset, auth_recover_existent_account,
4 auth_recover_nonexistent_account, auth_register,
5};
6#[cfg(feature = "plugin_auth")]
7use dyn_clone::{clone_trait_object, DynClone};
8
9use lettre::message::{Message, MultiPart};
10use lettre::transport::smtp::authentication::Credentials;
11use lettre::transport::stub::StubTransport;
12use lettre::{SmtpTransport, Transport};
13
14#[cfg(feature = "plugin_auth")]
17pub trait EmailTemplates: DynClone + Sync + Send {
19 fn send_activated(&self, mailer: &Mailer, to_email: &str);
20 fn send_password_changed(&self, mailer: &Mailer, to_email: &str);
21 fn send_password_reset(&self, mailer: &Mailer, to_email: &str);
22 fn send_recover_existent_account(&self, mailer: &Mailer, to_email: &str, link: &str);
23 fn send_recover_nonexistent_account(&self, mailer: &Mailer, to_email: &str, link: &str);
24 fn send_register(&self, mailer: &Mailer, to_email: &str, link: &str);
25}
26
27#[cfg(feature = "plugin_auth")]
28clone_trait_object!(EmailTemplates);
29
30#[derive(Clone)]
31pub struct Mailer {
33 pub from_address: String,
37 pub smtp_server: String,
41 pub smtp_username: String,
45 pub smtp_password: String,
49 pub actually_send: bool,
56 #[cfg(feature = "plugin_auth")]
57 pub templates: Box<dyn EmailTemplates + Sync + Send>,
59}
60
61impl Default for Mailer {
62 #[cfg(feature = "plugin_auth")]
63 fn default() -> Self {
64 Self::new(Box::<DefaultMailTemplates>::default())
65 }
66 #[cfg(not(feature = "plugin_auth"))]
67 fn default() -> Self {
68 Self::new()
69 }
70}
71
72impl Mailer {
73 #[cfg(not(feature = "plugin_auth"))]
79 pub fn new() -> Self {
80 Mailer::check_environment_variables();
81
82 let from_address: String = std::env::var("SMTP_FROM_ADDRESS")
83 .unwrap_or_else(|_| "create-rust-app@localhost".to_string());
84 let smtp_server: String = std::env::var("SMTP_SERVER").unwrap_or_else(|_| "".to_string());
85 let smtp_username: String =
86 std::env::var("SMTP_USERNAME").unwrap_or_else(|_| "".to_string());
87 let smtp_password: String =
88 std::env::var("SMTP_PASSWORD").unwrap_or_else(|_| "".to_string());
89 let actually_send: bool = std::env::var("SEND_MAIL")
90 .unwrap_or_else(|_| "false".to_string())
91 .eq_ignore_ascii_case("true");
92 Mailer {
93 from_address,
94 smtp_server,
95 smtp_username,
96 smtp_password,
97 actually_send,
98 }
99 }
100
101 #[cfg(feature = "plugin_auth")]
102 #[must_use]
103 pub fn new(templates: Box<dyn EmailTemplates + Sync + Send>) -> Self {
104 Self::check_environment_variables();
105
106 let from_address: String = std::env::var("SMTP_FROM_ADDRESS")
107 .unwrap_or_else(|_| "create-rust-app@localhost".to_string());
108 let smtp_server: String = std::env::var("SMTP_SERVER").unwrap_or_else(|_| String::new());
109 let smtp_username: String =
110 std::env::var("SMTP_USERNAME").unwrap_or_else(|_| String::new());
111 let smtp_password: String =
112 std::env::var("SMTP_PASSWORD").unwrap_or_else(|_| String::new());
113 let actually_send: bool = std::env::var("SEND_MAIL")
114 .unwrap_or_else(|_| "false".to_string())
115 .eq_ignore_ascii_case("true");
116 Self {
117 from_address,
118 smtp_server,
119 smtp_username,
120 smtp_password,
121 actually_send,
122 templates,
123 }
124 }
125
126 pub fn check_environment_variables() {
137 let vars = vec![
138 "SMTP_FROM_ADDRESS",
139 "SMTP_SERVER",
140 "SMTP_USERNAME",
141 "SMTP_PASSWORD",
142 "SEND_MAIL",
143 ];
144
145 let unset_vars = vars
146 .into_iter()
147 .filter(|v| std::env::var(v).is_err())
148 .collect::<Vec<_>>();
149
150 if !unset_vars.is_empty() {
151 println!(
152 "Warning: Mailing disabled; the following variables must be set: {}",
153 unset_vars.join(", ")
154 );
155 }
156
157 let send_mail_value = std::env::var("SEND_MAIL").unwrap_or_default();
158 if !send_mail_value.eq_ignore_ascii_case("true")
159 && !send_mail_value.eq_ignore_ascii_case("false")
160 {
161 println!("Warning: SEND_MAIL must be `true` or `false`");
162 }
163 }
164
165 pub fn send(&self, to: &str, subject: &str, text: &str, html: &str) {
182 let email = Message::builder()
183 .to(to.parse().unwrap())
184 .from(self.from_address.parse().unwrap())
185 .subject(subject)
186 .multipart(MultiPart::alternative_plain_html(
187 String::from(text),
188 String::from(html),
189 ))
190 .unwrap();
191
192 if self.actually_send {
193 let mailer = SmtpTransport::relay(&self.smtp_server)
194 .unwrap()
195 .credentials(Credentials::new(
196 self.smtp_username.to_string(),
197 self.smtp_password.to_string(),
198 ))
199 .build();
200
201 let result = mailer.send(&email);
202 println!(
203 r#"====================
204Sent email {:#?}
205--------------------
206to: {:?}
207from: {}
208message:
209{}
210===================="#,
211 result, to, self.from_address, text
212 );
213 } else {
214 let mailer = StubTransport::new_ok();
215 let result = mailer.send(&email);
216 println!(
217 r#"====================
218Sent email {:#?}
219--------------------
220to: {:?}
221from: {}
222message:
223{}
224===================="#,
225 result, to, self.from_address, text
226 );
227 }
228 }
229}
230
231#[cfg(feature = "plugin_auth")]
232#[derive(Clone)]
233pub struct DefaultMailTemplates {
234 pub base_url: String,
235}
236#[cfg(feature = "plugin_auth")]
237impl DefaultMailTemplates {
238 #[must_use]
239 pub fn new(base_url: &str) -> Self {
240 Self {
241 base_url: base_url.to_string(),
242 }
243 }
244}
245#[cfg(feature = "plugin_auth")]
246impl Default for DefaultMailTemplates {
247 fn default() -> Self {
248 Self::new("http://localhost:3000/")
249 }
250}
251#[cfg(feature = "plugin_auth")]
252impl EmailTemplates for DefaultMailTemplates {
253 fn send_activated(&self, mailer: &Mailer, to_email: &str) {
254 auth_activated::send(mailer, to_email);
255 }
256 fn send_password_changed(&self, mailer: &Mailer, to_email: &str) {
257 auth_password_changed::send(mailer, to_email);
258 }
259 fn send_password_reset(&self, mailer: &Mailer, to_email: &str) {
260 auth_password_reset::send(mailer, to_email);
261 }
262 fn send_recover_existent_account(&self, mailer: &Mailer, to_email: &str, url_path: &str) {
263 auth_recover_existent_account::send(
264 mailer,
265 to_email,
266 format!("{base_url}{url_path}", base_url = self.base_url).as_str(),
267 );
268 }
269 fn send_recover_nonexistent_account(&self, mailer: &Mailer, to_email: &str, url_path: &str) {
270 auth_recover_nonexistent_account::send(
271 mailer,
272 to_email,
273 format!("{base_url}{url_path}", base_url = self.base_url).as_str(),
274 );
275 }
276 fn send_register(&self, mailer: &Mailer, to_email: &str, url_path: &str) {
277 auth_register::send(
278 mailer,
279 to_email,
280 format!("{base_url}{url_path}", base_url = self.base_url).as_str(),
281 );
282 }
283}