mirror of
https://github.com/juspay/hyperswitch.git
synced 2025-10-28 20:23:43 +08:00
feat(types): add email types for sending emails (#3020)
This commit is contained in:
@ -81,9 +81,10 @@ pub async fn connect_account(
|
||||
|
||||
use crate::services::email::types as email_types;
|
||||
|
||||
let email_contents = email_types::WelcomeEmail {
|
||||
let email_contents = email_types::VerifyEmail {
|
||||
recipient_email: domain::UserEmail::from_pii_email(user_from_db.get_email())?,
|
||||
settings: state.conf.clone(),
|
||||
subject: "Welcome to the Hyperswitch community!",
|
||||
};
|
||||
|
||||
let send_email_result = state
|
||||
|
||||
@ -2,20 +2,16 @@
|
||||
<title>Login to Hyperswitch</title>
|
||||
<body style="background-color: #ececec">
|
||||
<style>
|
||||
.apple-footer a {
|
||||
{
|
||||
text-decoration: none !important;
|
||||
color: #999 !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
.apple-email a {
|
||||
{
|
||||
text-decoration: none !important;
|
||||
color: #448bff !important;
|
||||
border: none !important;
|
||||
}
|
||||
}
|
||||
.apple-footer a {{
|
||||
text-decoration: none !important;
|
||||
color: #999 !important;
|
||||
border: none !important;
|
||||
}}
|
||||
.apple-email a {{
|
||||
text-decoration: none !important;
|
||||
color: #448bff !important;
|
||||
border: none !important;
|
||||
}}
|
||||
</style>
|
||||
<div
|
||||
id="wrapper"
|
||||
@ -106,8 +102,8 @@
|
||||
>
|
||||
Welcome to Hyperswitch!
|
||||
<p style="font-size: 18px">Dear {user_name},</p>
|
||||
<span style="font-size: 18px"
|
||||
>We are thrilled to welcome you into our community!
|
||||
<span style="font-size: 18px">
|
||||
We are thrilled to welcome you into our community!
|
||||
</span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
@ -5,10 +5,13 @@ use masking::ExposeInterface;
|
||||
|
||||
use crate::{configs, consts};
|
||||
#[cfg(feature = "olap")]
|
||||
use crate::{core::errors::UserErrors, services::jwt, types::domain::UserEmail};
|
||||
use crate::{core::errors::UserErrors, services::jwt, types::domain};
|
||||
|
||||
pub enum EmailBody {
|
||||
Verify { link: String },
|
||||
Reset { link: String, user_name: String },
|
||||
MagicLink { link: String, user_name: String },
|
||||
InviteUser { link: String, user_name: String },
|
||||
}
|
||||
|
||||
pub mod html {
|
||||
@ -19,6 +22,27 @@ pub mod html {
|
||||
EmailBody::Verify { link } => {
|
||||
format!(include_str!("assets/verify.html"), link = link)
|
||||
}
|
||||
EmailBody::Reset { link, user_name } => {
|
||||
format!(
|
||||
include_str!("assets/reset.html"),
|
||||
link = link,
|
||||
username = user_name
|
||||
)
|
||||
}
|
||||
EmailBody::MagicLink { link, user_name } => {
|
||||
format!(
|
||||
include_str!("assets/magic_link.html"),
|
||||
user_name = user_name,
|
||||
link = link
|
||||
)
|
||||
}
|
||||
EmailBody::InviteUser { link, user_name } => {
|
||||
format!(
|
||||
include_str!("assets/invite.html"),
|
||||
username = user_name,
|
||||
link = link
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -31,7 +55,7 @@ pub struct EmailToken {
|
||||
|
||||
impl EmailToken {
|
||||
pub async fn new_token(
|
||||
email: UserEmail,
|
||||
email: domain::UserEmail,
|
||||
settings: &configs::settings::Settings,
|
||||
) -> CustomResult<String, UserErrors> {
|
||||
let expiration_duration = std::time::Duration::from_secs(consts::EMAIL_TOKEN_TIME_IN_SECS);
|
||||
@ -44,35 +68,126 @@ impl EmailToken {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct WelcomeEmail {
|
||||
pub recipient_email: UserEmail,
|
||||
pub settings: std::sync::Arc<configs::settings::Settings>,
|
||||
}
|
||||
|
||||
pub fn get_email_verification_link(
|
||||
pub fn get_link_with_token(
|
||||
base_url: impl std::fmt::Display,
|
||||
token: impl std::fmt::Display,
|
||||
action: impl std::fmt::Display,
|
||||
) -> String {
|
||||
format!("{base_url}/user/verify_email/?token={token}")
|
||||
format!("{base_url}/user/{action}/?token={token}")
|
||||
}
|
||||
|
||||
pub struct VerifyEmail {
|
||||
pub recipient_email: domain::UserEmail,
|
||||
pub settings: std::sync::Arc<configs::settings::Settings>,
|
||||
pub subject: &'static str,
|
||||
}
|
||||
|
||||
/// Currently only HTML is supported
|
||||
#[async_trait::async_trait]
|
||||
impl EmailData for WelcomeEmail {
|
||||
impl EmailData for VerifyEmail {
|
||||
async fn get_email_data(&self) -> CustomResult<EmailContents, EmailError> {
|
||||
let token = EmailToken::new_token(self.recipient_email.clone(), &self.settings)
|
||||
.await
|
||||
.change_context(EmailError::TokenGenerationFailure)?;
|
||||
|
||||
let verify_email_link = get_email_verification_link(&self.settings.server.base_url, token);
|
||||
let verify_email_link =
|
||||
get_link_with_token(&self.settings.server.base_url, token, "verify_email");
|
||||
|
||||
let body = html::get_html_body(EmailBody::Verify {
|
||||
link: verify_email_link,
|
||||
});
|
||||
let subject = "Welcome to the Hyperswitch community!".to_string();
|
||||
|
||||
Ok(EmailContents {
|
||||
subject,
|
||||
subject: self.subject.to_string(),
|
||||
body: external_services::email::IntermediateString::new(body),
|
||||
recipient: self.recipient_email.clone().into_inner(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ResetPassword {
|
||||
pub recipient_email: domain::UserEmail,
|
||||
pub user_name: domain::UserName,
|
||||
pub settings: std::sync::Arc<configs::settings::Settings>,
|
||||
pub subject: &'static str,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EmailData for ResetPassword {
|
||||
async fn get_email_data(&self) -> CustomResult<EmailContents, EmailError> {
|
||||
let token = EmailToken::new_token(self.recipient_email.clone(), &self.settings)
|
||||
.await
|
||||
.change_context(EmailError::TokenGenerationFailure)?;
|
||||
|
||||
let reset_password_link =
|
||||
get_link_with_token(&self.settings.server.base_url, token, "set_password");
|
||||
|
||||
let body = html::get_html_body(EmailBody::Reset {
|
||||
link: reset_password_link,
|
||||
user_name: self.user_name.clone().get_secret().expose(),
|
||||
});
|
||||
|
||||
Ok(EmailContents {
|
||||
subject: self.subject.to_string(),
|
||||
body: external_services::email::IntermediateString::new(body),
|
||||
recipient: self.recipient_email.clone().into_inner(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MagicLink {
|
||||
pub recipient_email: domain::UserEmail,
|
||||
pub user_name: domain::UserName,
|
||||
pub settings: std::sync::Arc<configs::settings::Settings>,
|
||||
pub subject: &'static str,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EmailData for MagicLink {
|
||||
async fn get_email_data(&self) -> CustomResult<EmailContents, EmailError> {
|
||||
let token = EmailToken::new_token(self.recipient_email.clone(), &self.settings)
|
||||
.await
|
||||
.change_context(EmailError::TokenGenerationFailure)?;
|
||||
|
||||
let magic_link_login = get_link_with_token(&self.settings.server.base_url, token, "login");
|
||||
|
||||
let body = html::get_html_body(EmailBody::MagicLink {
|
||||
link: magic_link_login,
|
||||
user_name: self.user_name.clone().get_secret().expose(),
|
||||
});
|
||||
|
||||
Ok(EmailContents {
|
||||
subject: self.subject.to_string(),
|
||||
body: external_services::email::IntermediateString::new(body),
|
||||
recipient: self.recipient_email.clone().into_inner(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub struct InviteUser {
|
||||
pub recipient_email: domain::UserEmail,
|
||||
pub user_name: domain::UserName,
|
||||
pub settings: std::sync::Arc<configs::settings::Settings>,
|
||||
pub subject: &'static str,
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl EmailData for InviteUser {
|
||||
async fn get_email_data(&self) -> CustomResult<EmailContents, EmailError> {
|
||||
let token = EmailToken::new_token(self.recipient_email.clone(), &self.settings)
|
||||
.await
|
||||
.change_context(EmailError::TokenGenerationFailure)?;
|
||||
|
||||
let invite_user_link =
|
||||
get_link_with_token(&self.settings.server.base_url, token, "set_password");
|
||||
|
||||
let body = html::get_html_body(EmailBody::MagicLink {
|
||||
link: invite_user_link,
|
||||
user_name: self.user_name.clone().get_secret().expose(),
|
||||
});
|
||||
|
||||
Ok(EmailContents {
|
||||
subject: self.subject.to_string(),
|
||||
body: external_services::email::IntermediateString::new(body),
|
||||
recipient: self.recipient_email.clone().into_inner(),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user