- Tasks Email Queue

Signed-off-by: Dave Richer <dave@imexsystems.ca>
This commit is contained in:
Dave Richer
2024-04-17 19:49:49 -04:00
parent 9f92347936
commit b8c34762ed
5 changed files with 110 additions and 26 deletions

View File

@@ -76,7 +76,7 @@ const generateEmailTemplate = (strings) => {
<table>
<tbody>
<tr>
<td><p style="font-size:70%;">${strings?.dateLine || now()}</p></td>
<td><p style="font-size:70%; padding-right:10px">${strings?.dateLine || now()}</p></td>
</tr>
</tbody>
</table>

View File

@@ -11,7 +11,7 @@ const client = require("../graphql-client/graphql-client").client;
const queries = require("../graphql-client/queries");
const generateEmailTemplate = require("./generateTemplate");
const moment = require("moment");
const { UPDATE_TASKS_REMIND_AT_SENT } = require("../graphql-client/queries");
const { taskEmailQueue } = require("./tasksEmailsQueue");
const ses = new aws.SES({
apiVersion: "latest",
@@ -27,6 +27,9 @@ const transporter = nodemailer.createTransport({
sendingRate: 40 // 40 emails per second.
});
// Initialize the Tasks Email Queue
const tasksEmailQueue = taskEmailQueue();
const fromEmails = InstanceManager({
imex: "ImEX Online <noreply@imex.online>",
rome: "Rome Online <noreply@romeonline.io>",
@@ -76,25 +79,27 @@ const generateTemplateArgs = (title, createdBy, dueDate, taskId) => {
const sendMail = (type, to, subject, html, taskIds, successCallback) => {
// Push next messages to Nodemailer
transporter.once("idle", () => {
if (transporter.isIdle()) {
transporter.sendMail(
{
from: fromEmails,
to,
subject,
html
},
(error, info) => {
if (info) {
if (typeof successCallback === "function" && taskIds && taskIds.length) {
successCallback(taskIds);
}
} else {
logger.log(`task-${type}-email-failure`, "error", null, null, error);
// Note: This is commented out because despite being in the documentation, it does not work
// and stackoverflow suggests it is not needed
// if (transporter.isIdle()) {
transporter.sendMail(
{
from: fromEmails,
to,
subject,
html
},
(error, info) => {
if (info) {
if (typeof successCallback === "function" && taskIds && taskIds.length) {
successCallback(taskIds);
}
} else {
logger.log(`task-${type}-email-failure`, "error", null, null, error);
}
);
}
}
);
// }
});
};
@@ -194,9 +199,8 @@ const tasksRemindEmail = async (req, res) => {
subHeader: `Please sign in to your account to view the Task details.`,
body: `<ul>
${allTasks
.map(
(task) =>
`<li><a href="${endPoints}/manage/tasks/alltasks?taskid=${task.id}">${task.title} - ${formatDate(task.due_date)}</a></li>`
.map((task) =>
`<li><a href="${endPoints}/manage/tasks/alltasks?taskid=${task.id}">${task.title} ${task.due_date ? `- ${formatDate(task.due_date)}` : ""}</a></li>`.trim()
)
.join("")}
</ul>`
@@ -206,10 +210,9 @@ const tasksRemindEmail = async (req, res) => {
if (emailData?.subject && emailData?.html) {
// Send Email
sendMail("remind", emailData.to, emailData.subject, emailData.html, taskIds, (taskIds) => {
client.request(UPDATE_TASKS_REMIND_AT_SENT, {
taskIds,
now: moment().toISOString()
});
for (const taskId of taskIds) {
taskEmailQueue.push(taskId);
}
});
}
});

View File

@@ -0,0 +1,59 @@
const path = require("path");
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
const Queue = require("better-queue");
const moment = require("moment/moment");
const { client } = require("../graphql-client/graphql-client");
const { UPDATE_TASKS_REMIND_AT_SENT } = require("../graphql-client/queries");
const logger = require("../utils/logger");
const taskEmailQueue = () =>
new Queue(
(taskIds, cb) => {
console.log("Processing reminds for taskIds: ", taskIds.join(", "));
// Set the remind_at_sent to the current time.
const now = moment().toISOString();
client
.request(UPDATE_TASKS_REMIND_AT_SENT, { taskIds, now })
.then((taskResponse) => {
logger.log("task-remind-email-queue", "info", null, null, taskResponse);
cb(null, taskResponse);
})
.catch((err) => {
logger.log("task-remind-email-queue", "error", null, null, err);
cb(err);
});
},
{
batchSize: 50,
batchDelay: 5000,
// The lower this is, the more likely we are to hit the rate limit.
batchDelayTimeout: 1000
}
);
// Handle process signals
process.on("SIGINT", () => {
taskEmailQueue.destroy();
process.exit(0);
});
process.on("SIGTERM", () => {
taskEmailQueue.destroy();
process.exit(0);
});
process.on("uncaughtException", (err) => {
console.error("Unhandled Exception:", err);
taskEmailQueue.destroy();
// Perform cleanup or log before exit
process.exit(1); // Exit with a 'failure' code
});
process.on("unhandledRejection", (reason, promise) => {
console.error("Unhandled Rejection:", reason);
taskEmailQueue.destroy();
process.exit(1);
});
module.exports = { taskEmailQueue };