From ef2c2fec2f42bb7e1a178c8781261017e6c65b87 Mon Sep 17 00:00:00 2001 From: Aravinth Manivannan Date: Fri, 17 May 2024 23:25:03 +0530 Subject: [PATCH] feat: mailer adapter: bootstrap smtp client --- .../mailer/lettre/account_validation_link.rs | 65 +++++++++++++++++++ .../adapters/output/mailer/lettre/mod.rs | 55 ++++++++++++++++ src/identity/adapters/output/mailer/mod.rs | 5 ++ src/identity/adapters/output/mod.rs | 3 + 4 files changed, 128 insertions(+) create mode 100644 src/identity/adapters/output/mailer/lettre/account_validation_link.rs create mode 100644 src/identity/adapters/output/mailer/lettre/mod.rs create mode 100644 src/identity/adapters/output/mailer/mod.rs diff --git a/src/identity/adapters/output/mailer/lettre/account_validation_link.rs b/src/identity/adapters/output/mailer/lettre/account_validation_link.rs new file mode 100644 index 0000000..01d5f45 --- /dev/null +++ b/src/identity/adapters/output/mailer/lettre/account_validation_link.rs @@ -0,0 +1,65 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use super::*; +use crate::identity::application::port::output::mailer::{account_validation_link::*, errors::*}; + +#[async_trait::async_trait] +impl AccountValidationLinkOutMailerPort for LettreMailer { + async fn account_validation_link( + &self, + to: &str, + username: &str, + validation_secret: &str, + ) -> OutMailerPortResult<()> { + + let email = Message::builder() + .from(&self.from) + .reply_to(&self.reply_to) + .to(to) + .subject("Please validate your account on Vanikam") // TODO: use better title + .header(ContentType::TEXT_PLAIN) + .body(format!(r#"Hello {username}, + Please click here to validate your Vanikam account: {validation_secret} + Warm regards, + Vanikam Admin + "#)) // TODO: change signature + .unwrap(); + mailer.send(email).await.unwrap(); + + } +} +// TODO: mailer tests + +//#[cfg(test)] +//mod tests { +// use super::*; +// +// #[actix_rt::test] +// async fn test_postgres_create_verification_secret() { +// let settings = crate::settings::tests::get_settings().await; +// let db = super::DBOutPostgresAdapter::new( +// sqlx::postgres::PgPool::connect(&settings.database.url) +// .await +// .unwrap(), +// ); +// +// let msg = CreateSecretMsgBuilder::default() +// .secret("secret".into()) +// .purpose("purpose".into()) +// .username("username".into()) +// .build() +// .unwrap(); +// +// db.create_verification_secret(msg.clone()).await.unwrap(); +// +// // duplicate: secret exists +// assert_eq!( +// db.create_verification_secret(msg).await.err(), +// Some(OutDBPortError::VerificationOTPSecretExists) +// ); +// +// settings.drop_db().await; +// } +//} diff --git a/src/identity/adapters/output/mailer/lettre/mod.rs b/src/identity/adapters/output/mailer/lettre/mod.rs new file mode 100644 index 0000000..ef06fa3 --- /dev/null +++ b/src/identity/adapters/output/mailer/lettre/mod.rs @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +use std::sync::Arc; + +use lettre::{ + message::header::ContentType, transport::smtp::authentication::Credentials, AsyncSmtpTransport, + Message, Tokio1Executor, +}; + +use crate::settings::Settings; + +#[derive(Clone)] +pub struct LettreMailer { + mailer: AsyncSmtpTransport, + from: String, + reply_to: String, + to: String, +} + +impl LettreMailer { + pub async fn new(s: &Settings) -> Self { + let email = Message::builder() + .from("NoBody ".parse().unwrap()) + .reply_to("Yuin ".parse().unwrap()) + .to("Hei ".parse().unwrap()) + .subject("Happy new async year") + .header(ContentType::TEXT_PLAIN) + .body(String::from("Be happy with async!")) + .unwrap(); + + let creds = Credentials::new("smtp_username".to_owned(), "smtp_password".to_owned()); + + // Open a remote connection to gmail + let mailer: AsyncSmtpTransport = + AsyncSmtpTransport::::relay("smtp.gmail.com") + .unwrap() + .credentials(creds) + .build(); + + Self { + mailer, + from: String::default(), // TODO: create settings module to read config + to: String::default(), + reply_to: String::default(), + } + + // Send the email + // match mailer.send(email).await { + // Ok(_) => println!("Email sent successfully!"), + // Err(e) => panic!("Could not send email: {e:?}"), + // }; + } +} diff --git a/src/identity/adapters/output/mailer/mod.rs b/src/identity/adapters/output/mailer/mod.rs new file mode 100644 index 0000000..644ce33 --- /dev/null +++ b/src/identity/adapters/output/mailer/mod.rs @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: 2024 Aravinth Manivannan +// +// SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod lettre; diff --git a/src/identity/adapters/output/mod.rs b/src/identity/adapters/output/mod.rs index 56f60de..7462528 100644 --- a/src/identity/adapters/output/mod.rs +++ b/src/identity/adapters/output/mod.rs @@ -1,3 +1,6 @@ // SPDX-FileCopyrightText: 2024 Aravinth Manivannan // // SPDX-License-Identifier: AGPL-3.0-or-later + +pub mod db; +pub mod mailer;