add OTP email sending functionality and integrate with user authentication flow

This commit is contained in:
faris Aljohari
2025-04-20 22:19:08 +03:00
parent 1e6503c072
commit 2b449e61ea
3 changed files with 134 additions and 70 deletions

View File

@ -19,5 +19,7 @@ export default registerAs(
process.env.MAILTRAP_DELETE_USER_TEMPLATE_UUID, process.env.MAILTRAP_DELETE_USER_TEMPLATE_UUID,
MAILTRAP_EDIT_USER_TEMPLATE_UUID: MAILTRAP_EDIT_USER_TEMPLATE_UUID:
process.env.MAILTRAP_EDIT_USER_TEMPLATE_UUID, process.env.MAILTRAP_EDIT_USER_TEMPLATE_UUID,
MAILTRAP_SEND_OTP_TEMPLATE_UUID:
process.env.MAILTRAP_SEND_OTP_TEMPLATE_UUID,
}), }),
); );

View File

@ -181,6 +181,49 @@ export class EmailService {
); );
} }
} }
async sendOtpEmailWithTemplate(
email: string,
emailEditData: any,
): Promise<void> {
const isProduction = process.env.NODE_ENV === 'production';
const API_TOKEN = this.configService.get<string>(
'email-config.MAILTRAP_API_TOKEN',
);
const API_URL = isProduction
? SEND_EMAIL_API_URL_PROD
: SEND_EMAIL_API_URL_DEV;
const TEMPLATE_UUID = this.configService.get<string>(
'email-config.MAILTRAP_SEND_OTP_TEMPLATE_UUID',
);
const emailData = {
from: {
email: this.smtpConfig.sender,
},
to: [
{
email: email,
},
],
template_uuid: TEMPLATE_UUID,
template_variables: emailEditData,
};
try {
await axios.post(API_URL, emailData, {
headers: {
Authorization: `Bearer ${API_TOKEN}`,
'Content-Type': 'application/json',
},
});
} catch (error) {
throw new HttpException(
error.response?.data?.message ||
'Error sending email using Mailtrap template',
error.response?.status || HttpStatus.INTERNAL_SERVER_ERROR,
);
}
}
generateUserChangesEmailBody( generateUserChangesEmailBody(
addedSpaceNames: string[], addedSpaceNames: string[],
removedSpaceNames: string[], removedSpaceNames: string[],

View File

@ -181,25 +181,25 @@ export class UserAuthService {
otpCode: string; otpCode: string;
cooldown: number; cooldown: number;
}> { }> {
try {
const otpLimiter = new Date(); const otpLimiter = new Date();
otpLimiter.setDate( otpLimiter.setDate(
otpLimiter.getDate() - this.configService.get<number>('OTP_LIMITER'), otpLimiter.getDate() - this.configService.get<number>('OTP_LIMITER'),
); );
const userExists = await this.userRepository.exists({ const user = await this.userRepository.findOne({
where: { where: {
region: data.regionUuid region: data.regionUuid ? { uuid: data.regionUuid } : undefined,
? {
uuid: data.regionUuid,
}
: undefined,
email: data.email, email: data.email,
isUserVerified: data.type === OtpType.PASSWORD ? true : undefined, isUserVerified: data.type === OtpType.PASSWORD ? true : undefined,
}, },
}); });
if (!userExists) { if (!user) {
throw new BadRequestException('User not found'); throw new BadRequestException('User not found');
} }
await this.otpRepository.softDelete({ email: data.email, type: data.type }); await this.otpRepository.softDelete({
email: data.email,
type: data.type,
});
await this.otpRepository.delete({ await this.otpRepository.delete({
email: data.email, email: data.email,
type: data.type, type: data.type,
@ -250,10 +250,29 @@ export class UserAuthService {
}, },
}); });
cooldown = 30 * Math.pow(2, countOfOtpToReturn - 1); cooldown = 30 * Math.pow(2, countOfOtpToReturn - 1);
const subject = 'OTP send successfully';
const message = `Your OTP code is ${otpCode}`; const [otp1, otp2, otp3, otp4, otp5, otp6] = otpCode.split('');
this.emailService.sendEmail(data.email, subject, message);
await this.emailService.sendOtpEmailWithTemplate(data.email, {
name: user.firstName,
otp1,
otp2,
otp3,
otp4,
otp5,
otp6,
});
return { otpCode, cooldown }; return { otpCode, cooldown };
} catch (error) {
if (error instanceof BadRequestException) {
throw error;
}
console.error('OTP generation error:', error);
throw new BadRequestException(
'An unexpected error occurred while generating the OTP.',
);
}
} }
async verifyOTP( async verifyOTP(