aboutsummaryrefslogtreecommitdiff
path: root/crates/secd/src/client/email.rs
blob: 2712037fb9214b8d11c7399d847f1188fd258b12 (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
use std::{path::PathBuf, str::FromStr};

use email_address::EmailAddress;
use time::OffsetDateTime;

use super::{
    EmailMessenger, EmailMessengerError, EmailType, EMAIL_TEMPLATE_DEFAULT_LOGIN,
    EMAIL_TEMPLATE_DEFAULT_SIGNUP,
};

pub(crate) struct LocalEmailStubber {
    pub(crate) email_template_login: Option<String>,
    pub(crate) email_template_signup: Option<String>,
}

#[async_trait::async_trait]
impl EmailMessenger for LocalEmailStubber {
    // TODO: this module really shouldn't be called client, it should be called services... the client is sqlx/mailgun/sns wrapper or whatever...
    async fn send_email(
        &self,
        email_address: &str,
        validation_id: &str,
        secret_code: &str,
        t: EmailType,
    ) -> Result<(), EmailMessengerError> {
        let login_template = self
            .email_template_login
            .clone()
            .unwrap_or(EMAIL_TEMPLATE_DEFAULT_LOGIN.to_string());
        let signup_template = self
            .email_template_signup
            .clone()
            .unwrap_or(EMAIL_TEMPLATE_DEFAULT_SIGNUP.to_string());

        let replace_template = |s: &str| {
            s.replace(
                "%secd_link%",
                &format!("{}?code={}", validation_id, secret_code),
            )
            .replace("%secd_email_address%", email_address)
            .replace("%secd_code%", secret_code)
        };

        if !EmailAddress::is_valid(email_address) {
            return Err(EmailMessengerError::InvalidEmailAddress);
        }

        let body = match t {
            EmailType::Login => replace_template(&login_template),
            EmailType::Signup => replace_template(&signup_template),
        };

        // TODO: write to the system mailbox instead?
        std::fs::write(
            PathBuf::from_str(&format!(
                "/tmp/{}_{}.localmail",
                OffsetDateTime::now_utc(),
                validation_id
            ))
            .map_err(|_| EmailMessengerError::Unknown)?,
            body,
        )
        .map_err(|_| EmailMessengerError::FailedToSendEmail)?;

        Ok(())
    }
}