225 lines
6.7 KiB
JavaScript
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
|
|
};
|