feat: implement email verification and password reset features
- Add EmailTokenService for handling email verification and password reset tokens. - Create EmailService for sending verification and reset emails via SMTP. - Update AuthService to handle email verification status during login. - Modify user registration to redirect to a check email screen instead of issuing a token. - Implement resend verification email functionality. - Add deep link handling for email verification and password reset in the mobile app. - Update mobile app routes and components to support new email verification flow. - Enhance error handling for unverified emails during login attempts. - Update configuration to include SMTP settings for email service.
This commit is contained in:
@@ -0,0 +1,67 @@
|
||||
use lettre::{
|
||||
message::header::ContentType,
|
||||
transport::smtp::authentication::Credentials,
|
||||
AsyncSmtpTransport, AsyncTransport, Message, Tokio1Executor,
|
||||
};
|
||||
|
||||
use crate::{config::Config, error::AppError};
|
||||
|
||||
pub struct EmailService {
|
||||
mailer: AsyncSmtpTransport<Tokio1Executor>,
|
||||
from: String,
|
||||
}
|
||||
|
||||
impl EmailService {
|
||||
pub fn new(config: &Config) -> Result<Self, AppError> {
|
||||
let creds = Credentials::new(config.smtp_user.clone(), config.smtp_pass.clone());
|
||||
|
||||
let mailer = AsyncSmtpTransport::<Tokio1Executor>::starttls_relay(&config.smtp_host)
|
||||
.map_err(|e| AppError::Internal(anyhow::anyhow!("SMTP config error: {e}")))?
|
||||
.port(config.smtp_port)
|
||||
.credentials(creds)
|
||||
.build();
|
||||
|
||||
Ok(Self {
|
||||
mailer,
|
||||
from: config.email_from.clone(),
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn send_password_reset(&self, to: &str, reset_link: &str) -> Result<(), AppError> {
|
||||
let body = format!(
|
||||
"Você solicitou a redefinição de senha do MeowSpool.\n\nClique no link abaixo para redefinir sua senha (válido por 1 hora):\n\n{reset_link}\n\nSe você não solicitou isso, ignore este email."
|
||||
);
|
||||
|
||||
self.send(to, "Redefinição de senha — MeowSpool", &body).await
|
||||
}
|
||||
|
||||
pub async fn send_email_verification(&self, to: &str, verify_link: &str) -> Result<(), AppError> {
|
||||
let body = format!(
|
||||
"Bem-vindo ao MeowSpool! Confirme seu endereço de email clicando no link abaixo:\n\n{verify_link}\n\nO link expira em 24 horas."
|
||||
);
|
||||
|
||||
self.send(to, "Confirme seu email — MeowSpool", &body).await
|
||||
}
|
||||
|
||||
async fn send(&self, to: &str, subject: &str, body: &str) -> Result<(), AppError> {
|
||||
let email = Message::builder()
|
||||
.from(
|
||||
self.from
|
||||
.parse()
|
||||
.map_err(|e| AppError::Internal(anyhow::anyhow!("Invalid from address: {e}")))?,
|
||||
)
|
||||
.to(to
|
||||
.parse()
|
||||
.map_err(|e| AppError::Internal(anyhow::anyhow!("Invalid to address: {e}")))?)
|
||||
.subject(subject)
|
||||
.header(ContentType::TEXT_PLAIN)
|
||||
.body(body.to_string())
|
||||
.map_err(|e| AppError::Internal(anyhow::anyhow!("Email build error: {e}")))?;
|
||||
|
||||
AsyncTransport::send(&self.mailer, email)
|
||||
.await
|
||||
.map_err(|e| AppError::Internal(anyhow::anyhow!("SMTP send error: {e}")))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod email_service;
|
||||
|
||||
pub use email_service::EmailService;
|
||||
Reference in New Issue
Block a user