diff options
| author | benj <benj@rse8.com> | 2022-12-24 00:43:38 -0800 |
|---|---|---|
| committer | benj <benj@rse8.com> | 2022-12-24 00:43:38 -0800 |
| commit | c2268c285648ef02ece04de0d9df0813c6d70ff8 (patch) | |
| tree | f84ec7ee42f97d78245f26d0c5a0c559cd35e89d /crates/secd/src/client/email | |
| parent | de6339da72af1d61ca5908b780977e2b037ce014 (diff) | |
| download | secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.gz secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.bz2 secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.lz secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.xz secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.tar.zst secdiam-c2268c285648ef02ece04de0d9df0813c6d70ff8.zip | |
refactor everything with more abstraction and a nicer interface
Diffstat (limited to '')
| -rw-r--r-- | crates/secd/src/client/email.rs | 67 | ||||
| -rw-r--r-- | crates/secd/src/client/email/mod.rs | 68 |
2 files changed, 68 insertions, 67 deletions
diff --git a/crates/secd/src/client/email.rs b/crates/secd/src/client/email.rs deleted file mode 100644 index 2712037..0000000 --- a/crates/secd/src/client/email.rs +++ /dev/null @@ -1,67 +0,0 @@ -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(()) - } -} diff --git a/crates/secd/src/client/email/mod.rs b/crates/secd/src/client/email/mod.rs new file mode 100644 index 0000000..915d18c --- /dev/null +++ b/crates/secd/src/client/email/mod.rs @@ -0,0 +1,68 @@ +use email_address::EmailAddress; +use lettre::Transport; +use log::error; +use std::collections::HashMap; + +#[derive(Debug, thiserror::Error, derive_more::Display)] +pub enum EmailMessengerError { + FailedToSendEmail, +} + +pub struct EmailValidationMessage { + pub recipient: EmailAddress, + pub subject: String, + pub body: String, +} + +#[async_trait::async_trait] +pub(crate) trait EmailMessenger { + async fn send_email( + &self, + email_address: &EmailAddress, + template: &str, + template_vars: HashMap<&str, &str>, + ) -> Result<(), EmailMessengerError>; +} + +pub(crate) struct LocalMailer {} + +#[async_trait::async_trait] +impl EmailMessenger for LocalMailer { + async fn send_email( + &self, + email_address: &EmailAddress, + template: &str, + template_vars: HashMap<&str, &str>, + ) -> Result<(), EmailMessengerError> { + todo!() + } +} + +#[async_trait::async_trait] +pub(crate) trait Sendable { + async fn send(&self) -> Result<(), EmailMessengerError>; +} + +#[async_trait::async_trait] +impl Sendable for EmailValidationMessage { + // TODO: We need to break this up as before, especially so we can feature + // gate unwanted things like Lettre... + async fn send(&self) -> Result<(), EmailMessengerError> { + // TODO: Get these things from the template... + let email = lettre::Message::builder() + .from("BranchControl <iam@branchcontrol.com>".parse().unwrap()) + .reply_to("BranchControl <iam@branchcontrol.com>".parse().unwrap()) + .to(self.recipient.to_string().parse().unwrap()) + .subject(self.subject.clone()) + .body(self.body.clone()) + .unwrap(); + + let mailer = lettre::SmtpTransport::unencrypted_localhost(); + + mailer.send(&email).map_err(|e| { + error!("failed to send email {:?}", e); + EmailMessengerError::FailedToSendEmail + })?; + Ok(()) + } +} |
