diff --git a/package.json b/package.json index b0eb729..e4cb3b8 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "devDependencies": { "@babel/helper-compilation-targets": "^7.23.6", "@eslint/js": "^9.1.1", + "@types/aws-sdk": "^2.7.0", "@types/bcryptjs": "^2.4.6", "@types/cors": "^2.8.17", "@types/dotenv": "^8.2.0", diff --git a/src/sequelize/migrations/20240527133340-create-failed-email.js b/src/sequelize/migrations/20240527133340-create-failed-email.js new file mode 100644 index 0000000..49f3305 --- /dev/null +++ b/src/sequelize/migrations/20240527133340-create-failed-email.js @@ -0,0 +1,44 @@ +'use strict'; +/** @type {import('sequelize-cli').Migration} */ +module.exports = { + async up(queryInterface, Sequelize) { + await queryInterface.createTable('failedEmails', { + id: { + allowNull: false, + autoIncrement: true, + primaryKey: true, + type: Sequelize.INTEGER + }, + userId: { + type: Sequelize.INTEGER + }, + email: { + type: Sequelize.STRING + }, + subject: { + type: Sequelize.STRING + }, + body: { + type: Sequelize.TEXT + }, + attempts: { + type: Sequelize.INTEGER + }, + lastAttempted: { + type: Sequelize.DATE, + defaultValue: Sequelize.NOW, + }, + createdAt: { + allowNull: false, + type: Sequelize.DATE + }, + updatedAt: { + allowNull: false, + type: Sequelize.DATE + } + }); + }, + async down(queryInterface, Sequelize) { + await queryInterface.dropTable('failedEmails'); + } +}; \ No newline at end of file diff --git a/src/sequelize/models/failedemail.ts b/src/sequelize/models/failedemail.ts new file mode 100644 index 0000000..76595f4 --- /dev/null +++ b/src/sequelize/models/failedemail.ts @@ -0,0 +1,59 @@ +import { DataTypes, Model } from 'sequelize'; +import sequelize from '../../config/dbConnection'; + +interface FailedEmailAttributes { + id: number; + userId: number; + email: string; + subject: string; + body: string; + attempts: number; + lastAttempted: Date; +} + +class FailedEmail extends Model implements FailedEmailAttributes { + id!: number; + userId!: number; + email!: string; + subject!: string; + body!: string; + attempts!: number; + lastAttempted!: Date; +} + +FailedEmail.init({ + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + }, + userId: { + type: DataTypes.INTEGER, + allowNull: false, + }, + email: { + type: DataTypes.STRING, + allowNull: false, + }, + subject: { + type: DataTypes.STRING, + allowNull: false, + }, + body: { + type: DataTypes.TEXT, + allowNull: false, + }, + attempts: { + type: DataTypes.INTEGER, + defaultValue: 0, + }, + lastAttempted: { + type: DataTypes.DATE, + defaultValue: DataTypes.NOW, + }, +}, { + sequelize, + modelName: 'failedEmails', +}); + +export default FailedEmail; diff --git a/src/services/mail.service.ts b/src/services/mail.service.ts index 556e4dd..a63ea3c 100644 --- a/src/services/mail.service.ts +++ b/src/services/mail.service.ts @@ -1,23 +1,38 @@ +import FailedEmail from "../sequelize/models/failedemail"; import { IUser } from "../types"; import { env } from "../utils/env"; import transporter from "../utils/transporter"; +import axios from "axios"; +import cron from "node-cron"; export const sendEmailService = async (user: IUser, subject: string, template: any, token?: number) => { try { - const mailOptions = { - from: env.smtp_user, + const apiEndpoint: any = process.env.URL_SEND_EMAIL; + const emailData = { to: user.email, subject: subject, - html: template, + body: template, }; - - - const info = await transporter.sendMail(mailOptions); - //@ts-ignore + + const response = await axios.post(apiEndpoint, emailData); } catch (error: any) { - throw new Error(error.message); + try { + const rs = await FailedEmail.create({ + // @ts-ignore + userId: user.id, + email: user.email, + subject: subject, + body: template, + attempts: 1, + lastAttempted: new Date(), + }); + } catch (error: any) { + throw new Error(error.message); + } + throw error; } }; + export const sendNotification = async (email: string | undefined, subject: string, template: any) => { try { const mailOptions = { @@ -33,3 +48,31 @@ export const sendNotification = async (email: string | undefined, subject: strin throw new Error(error.message); } }; + +const retryFailedEmail = async () => { + const allFailedEmail = await FailedEmail.findAll(); + + for (const email of allFailedEmail) { + try { + const apiEndpoint: any = process.env.URL_SEND_EMAIL; + + const failedEmailData = { + to: email.dataValues.email, + subject: email.dataValues.subject, + body: email.dataValues.body, + }; + + const response = await axios.post(apiEndpoint, failedEmailData); + await email.destroy(); + } catch (error: any) { + email.attempts = email.dataValues.attempts + 1; + email.dataValues.lastAttempted = new Date(); + await email.save(); + throw new Error(error.message); + } + } +}; + +cron.schedule("0 * * * *", () => { + retryFailedEmail(); +});