summaryrefslogtreecommitdiff
path: root/src/mailer.rs
blob: 1ea1a8bf8bcea34f536f7206551b8e657c095972 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
use std::time::Duration;

use lettre::{
    message::Mailbox,
    transport::smtp::client::{Tls, TlsParameters},
    AsyncSmtpTransport, Message, Tokio1Executor,
};
use rocket::http::uri::Absolute;
use serde_json::json;

use crate::types::UserID;

pub struct Mailer {
    from: Mailbox,
    verify_base: Absolute<'static>,
    transport: AsyncSmtpTransport<Tokio1Executor>,
}

impl Mailer {
    pub fn new(
        from: Mailbox,
        host: &str,
        port: u16,
        verify_base: Absolute<'static>,
    ) -> anyhow::Result<Self> {
        Ok(Mailer {
            from,
            verify_base,
            transport: AsyncSmtpTransport::<Tokio1Executor>::builder_dangerous(host)
                .port(port)
                .tls(Tls::Opportunistic(TlsParameters::new(host.to_string())?))
                .timeout(Some(Duration::from_secs(5)))
                .build(),
        })
    }

    pub(crate) async fn send_account_verify(
        &self,
        uid: &UserID,
        to: &str,
        code: &str,
    ) -> anyhow::Result<()> {
        let fragment = base64::encode_config(
            serde_json::to_string(&json!({
                "uid": uid,
                "email": to,
                "code": code,
            }))?,
            base64::URL_SAFE,
        );
        let email = Message::builder()
            .from(self.from.clone())
            .to(to.parse()?)
            .subject("account verify code")
            .body(format!("{}/#/verify/{fragment}", self.verify_base))?;
        lettre::AsyncTransport::send(&self.transport, email).await?;
        Ok(())
    }

    pub(crate) async fn send_session_verify(&self, to: &str, code: &str) -> anyhow::Result<()> {
        let email = Message::builder()
            .from(self.from.clone())
            .to(to.parse()?)
            .subject("session verify code")
            .body(format!("{code}"))?;
        lettre::AsyncTransport::send(&self.transport, email).await?;
        Ok(())
    }

    pub(crate) async fn send_password_changed(&self, to: &str) -> anyhow::Result<()> {
        let email = Message::builder()
            .from(self.from.clone())
            .to(to.parse()?)
            .subject("account password has been changed")
            .body(String::from(
                "your account password has been changed. if you haven't done this, \
                 you're probably in trouble now.",
            ))?;
        lettre::AsyncTransport::send(&self.transport, email).await?;
        Ok(())
    }

    pub(crate) async fn send_password_forgot(&self, to: &str, code: &str) -> anyhow::Result<()> {
        let email = Message::builder()
            .from(self.from.clone())
            .to(to.parse()?)
            .subject("account reset code")
            .body(code.to_string())?;
        lettre::AsyncTransport::send(&self.transport, email).await?;
        Ok(())
    }

    pub(crate) async fn send_account_reset(&self, to: &str) -> anyhow::Result<()> {
        let email = Message::builder()
            .from(self.from.clone())
            .to(to.parse()?)
            .subject("account has been reset")
            .body(String::from(
                "your account has been reset. if you haven't done this, \
                 you're probably in trouble now.",
            ))?;
        lettre::AsyncTransport::send(&self.transport, email).await?;
        Ok(())
    }
}