mcaptcha/email/
verification.rs1use lettre::{
8 message::{header, MultiPart, SinglePart},
9 AsyncTransport, Message,
10};
11use sailfish::TemplateOnce;
12
13use crate::errors::*;
14use crate::Data;
15
16const PAGE: &str = "Login";
17
18#[derive(Clone, TemplateOnce)]
19#[template(path = "email/verification/index.html")]
20struct IndexPage<'a> {
21 verification_link: &'a str,
22}
23
24impl<'a> IndexPage<'a> {
25 fn new(verification_link: &'a str) -> Self {
26 Self { verification_link }
27 }
28}
29
30async fn verification(
31 data: &Data,
32 to: &str,
33 verification_link: &str,
34) -> ServiceResult<()> {
35 if let Some(smtp) = data.settings.smtp.as_ref() {
36 let from = format!("mCaptcha Admin <{}>", smtp.from);
37 let reply_to = format!("mCaptcha Admin <{}>", smtp.reply);
38 const SUBJECT: &str = "[mCaptcha] Please verify your email";
39
40 let plain_text = format!(
41 "
42Welcome to mCaptcha!
43
44Please verify your email address to continue.
45
46VERIFICATION LINK: {}
47
48Please ignore this email if you weren't expecting it.
49
50With best regards,
51Admin
52instance: {}
53project website: {}",
54 verification_link,
55 &data.settings.server.domain,
56 crate::PKG_HOMEPAGE
57 );
58
59 let html = IndexPage::new(verification_link).render_once().unwrap();
60
61 let email = Message::builder()
62 .from(from.parse().unwrap())
63 .reply_to(reply_to.parse().unwrap())
64 .to(to.parse().unwrap())
65 .subject(SUBJECT)
66 .multipart(
67 MultiPart::alternative() .singlepart(
69 SinglePart::builder()
70 .header(header::ContentType::TEXT_PLAIN)
71 .body(plain_text), )
73 .singlepart(
74 SinglePart::builder()
75 .header(header::ContentType::TEXT_HTML)
76 .body(html),
77 ),
78 )
79 .unwrap();
80
81 data.mailer.as_ref().unwrap().send(email).await?;
82 }
83 Ok(())
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 use awc::Client;
91
92 #[actix_rt::test]
93 async fn email_verification_works_pg() {
94 let data = crate::tests::pg::get_data().await;
95 email_verification_works(data).await;
96 }
97
98 #[actix_rt::test]
99 async fn email_verification_works_maria() {
100 let data = crate::tests::maria::get_data().await;
101 email_verification_works(data).await;
102 }
103
104 async fn email_verification_works(data: crate::ArcData) {
105 const TO_ADDR: &str = "Hello <realaravinth@localhost>";
106 const VERIFICATION_LINK: &str = "https://localhost";
107 let settings = &data.settings;
108 verification(&data, TO_ADDR, VERIFICATION_LINK)
109 .await
110 .unwrap();
111
112 let client = Client::default();
113 let mut resp = client
114 .get("http://localhost:1080/email")
115 .send()
116 .await
117 .unwrap();
118 let data: serde_json::Value = resp.json().await.unwrap();
119 let data = &data[0];
120 let smtp = settings.smtp.as_ref().unwrap();
121
122 let from_addr = &data["headers"]["from"];
123
124 assert!(from_addr.to_string().contains(&smtp.from));
125
126 let body = &data["html"];
127 assert!(body.to_string().contains(VERIFICATION_LINK));
128 }
129}