Files
bodyshop/server/email/tasksEmails.js
Dave Richer 470eb19a2f - Final Push Prior to Live Testing
Signed-off-by: Dave Richer <dave@imexsystems.ca>
2024-04-16 16:11:41 -04:00

225 lines
6.7 KiB
JavaScript

const path = require("path");
require("dotenv").config({
path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`)
});
let nodemailer = require("nodemailer");
let aws = require("@aws-sdk/client-ses");
let { defaultProvider } = require("@aws-sdk/credential-provider-node");
const InstanceManager = require("../utils/instanceMgr").default;
const logger = require("../utils/logger");
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 ses = new aws.SES({
apiVersion: "latest",
defaultProvider,
region: InstanceManager({
imex: "ca-central-1",
rome: "us-east-2"
})
});
const transporter = nodemailer.createTransport({
SES: { ses, aws },
sendingRate: 40 // 40 emails per second.
});
const fromEmails = InstanceManager({
imex: `ImEX Online <noreply@imex.online>`,
rome: `Rome Online <noreply@romeonline.io>`,
promanager: `ProManager <noreply@promanager.web-est.com>`
});
/**
* Format the date for the email.
* @param date
* @returns {string|string}
*/
const formatDate = (date) => {
return date ? `| Due on ${moment(date).format("MM/DD/YYYY")}` : "";
};
/**
* Generate the email template arguments.
* @param title
* @param createdBy
* @param dueDate
* @param taskId
* @returns {{header, body: string, subHeader: string}}
*/
const generateTemplateArgs = (title, createdBy, dueDate, taskId) => {
return {
header: title,
subHeader: `Assigned by ${createdBy} ${formatDate(dueDate)}`,
body: `<a href="${fromEmails}/manage/tasks/alltasks?taskid=${taskId}">Please sign in to your account to view the Task details.</a>`
};
};
/**
* Send the email.
* @param type
* @param to
* @param subject
* @param html
* @param taskIds
* @param successCallback
*/
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);
}
}
);
}
});
};
/**
* Send an email to the assigned user.
* @param req
* @param res
* @returns {Promise<*>}
*/
const taskAssignedEmail = async (req, res) => {
// We have no event Data, bail
if (!req?.payload?.event?.data?.new) {
return res.status(400).json({ message: "No data in the event payload" });
}
const { new: newTask } = req.payload.event.data;
// This is not a new task, but a reassignment.
const dirty = req.payload.event.data?.old && req.payload.event.data?.old?.assigned_to;
sendMail(
"assigned",
newTask.assigned_to,
`A Task has been ${dirty ? "reassigned" : "created"} for you - ${newTask.title}`,
generateEmailTemplate(generateTemplateArgs(newTask.title, newTask.created_by, newTask.due_date, newTask.id))
);
// We return success regardless because we don't want to block the event trigger.
res.status(200).json({ success: true });
};
/**
* Send an email to remind the user of their tasks.
* @param req
* @param res
* @returns {Promise<*>}
*/
const tasksRemindEmail = async (req, res) => {
try {
const tasksRequest = await client.request(queries.QUERY_REMIND_TASKS, {
time: moment().add(1, "minutes").toISOString()
});
// No tasks present in the database, bail.
if (!tasksRequest?.tasks || !tasksRequest?.tasks.length) {
return res.status(200).json({ message: "No tasks to remind" });
}
// Group tasks by assigned_to, to avoid sending multiple emails to the same recipient.
const groupedTasks = tasksRequest.tasks.reduce((acc, task) => {
const key = task.assigned_to;
if (!acc[key]) {
acc[key] = [];
}
acc[key].push(task);
return acc;
}, {});
// No grouped tasks, bail.
if (Object.keys(groupedTasks).length === 0) {
return res.status(200).json({ message: "No tasks to remind" });
}
// Build an aggregate data object containing email and the count of tasks assigned to them.
const recipientCounts = Object.keys(groupedTasks).map((key) => {
return {
email: key,
count: groupedTasks[key].length
};
});
// Iterate over all recipients and send the email.
recipientCounts.forEach((recipient) => {
const emailData = {
from: fromEmails,
to: recipient.email
};
const taskIds = groupedTasks[recipient.email].map((task) => task.id);
// There is only the one email to send to this author.
if (recipient.count === 1) {
const onlyTask = groupedTasks[recipient.email][0];
emailData.subject = `New Task Reminder - ${onlyTask.title} - ${formatDate(onlyTask.due_date)}`;
emailData.html = generateEmailTemplate(
generateTemplateArgs(onlyTask.title, onlyTask.created_by, onlyTask.due_date, onlyTask.id)
);
}
// There are multiple emails to send to this author.
else {
const allTasks = groupedTasks[recipient.email];
emailData.subject = `New Task Reminder - ${allTasks.length} Tasks require your attention`;
emailData.html = generateEmailTemplate({
header: `${allTasks.length} Tasks require your attention`,
subHeader: `Please sign in to your account to view the Task details.`,
body: `<ul>
${allTasks
.map(
(task) =>
`<li><a href="${fromEmails}/manage/tasks/alltasks?taskid=${task.id}">${task.title} - ${formatDate(task.due_date)}</a></li>`
)
.join("")}
</ul>`
});
}
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()
});
});
}
});
// Sixth step would be to set the remind_at_sent to the current time.
res.status(200).json({ status: "success" });
} catch (err) {
res.status(500).json({
status: "error",
message: `Something went wrong sending Task Reminders: ${err.message || "An error occurred"}`
});
}
};
module.exports = {
taskAssignedEmail,
tasksRemindEmail
};