feat: mailer adapter: bootstrap smtp client

This commit is contained in:
Aravinth Manivannan 2024-05-17 23:25:03 +05:30
parent e60a78054e
commit ef2c2fec2f
Signed by: realaravinth
GPG key ID: F8F50389936984FF
4 changed files with 128 additions and 0 deletions

View file

@ -0,0 +1,65 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// 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;
// }
//}

View file

@ -0,0 +1,55 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// 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<Tokio1Executor>,
from: String,
reply_to: String,
to: String,
}
impl LettreMailer {
pub async fn new(s: &Settings) -> Self {
let email = Message::builder()
.from("NoBody <nobody@domain.tld>".parse().unwrap())
.reply_to("Yuin <yuin@domain.tld>".parse().unwrap())
.to("Hei <hei@domain.tld>".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<Tokio1Executor> =
AsyncSmtpTransport::<Tokio1Executor>::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:?}"),
// };
}
}

View file

@ -0,0 +1,5 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
pub mod lettre;

View file

@ -1,3 +1,6 @@
// SPDX-FileCopyrightText: 2024 Aravinth Manivannan <realaravinth@batsense.net>
//
// SPDX-License-Identifier: AGPL-3.0-or-later
pub mod db;
pub mod mailer;