const path = require("path"); require("dotenv").config({ path: path.resolve(process.cwd(), `.env.${process.env.NODE_ENV || "development"}`) }); 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-timezone"); const { taskEmailQueue } = require("./tasksEmailsQueue"); const mailer = require("./mailer"); // Initialize the Tasks Email Queue const tasksEmailQueue = taskEmailQueue(); // Cleanup function for the Tasks Email Queue const tasksEmailQueueCleanup = async () => { try { // Example async operation // console.log("Performing Tasks Email Reminder process cleanup..."); await new Promise((resolve) => tasksEmailQueue.destroy(() => resolve())); } catch (err) { // console.error("Tasks Email Reminder process cleanup failed:", err); } }; // if (process.env.NODE_ENV !== "development") { // // Handling SIGINT (e.g., Ctrl+C) // process.on("SIGINT", async () => { // console.log("Handling SIGNIT For Tasks Cleanup"); // await tasksEmailQueueCleanup(); // process.exit(0); // }); // // Handling SIGTERM (e.g., sent by system shutdown) // process.on("SIGTERM", async () => { // console.log("Handling SIGTERM For Tasks Cleanup"); // await tasksEmailQueueCleanup(); // process.exit(0); // }); // // Handling uncaught exceptions // process.on("uncaughtException", async (err) => { // console.log("Handling uncaughtException For Tasks Cleanup"); // await tasksEmailQueueCleanup(); // process.exit(1); // }); // // Handling unhandled promise rejections // process.on("unhandledRejection", async (reason, promise) => { // console.log("Handling unhandledRejection For Tasks Cleanup"); // await tasksEmailQueueCleanup(); // process.exit(1); // }); // } /** * Format the date for the email. * @param date * @returns {string|string} */ const formatDate = (date) => { return date ? `| Due on: ${moment(date).format("MM/DD/YYYY")}` : ""; }; const formatPriority = (priority) => { if (priority === 1) { return "High"; } else if (priority === 3) { return "Low"; } else { return "Medium"; } }; /** * Generate the email template arguments. * @param title * @param priority * @param description * @param dueDate * @param createdBy * @param bodyshop * @param job * @param taskId * @returns {{header, body: string, subHeader: string}} */ const getEndpoints = (bodyshop) => InstanceManager({ imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online", rome: bodyshop?.convenient_company === "promanager" ? process.env?.NODE_ENV === "test" ? "https//test.promanager.web-est.com" : "https://promanager.web-est.com" : process.env?.NODE_ENV === "test" ? "https//test.romeonline.io" : "https://romeonline.io" }); const generateTemplateArgs = (title, priority, description, dueDate, bodyshop, job, taskId, dateLine, createdBy) => { const endPoints = getEndpoints(bodyshop); return { header: title, subHeader: `Body Shop: ${bodyshop.shopname} | Priority: ${formatPriority(priority)} ${formatDate(dueDate)} | Created By: ${createdBy || "N/A"}`, body: `Reference: ${job.ro_number || "N/A"} | ${job.ownr_co_nm ? job.ownr_co_nm : `${job.ownr_fn || ""} ${job.ownr_ln || ""}`.trim()} | ${`${job.v_model_yr || ""} ${job.v_make_desc || ""} ${job.v_model_desc || ""}`.trim()}
${description ? description.concat("
") : ""}View this task.`, dateLine }; }; /** * Send the email. * @param type * @param to * @param subject * @param html * @param taskIds * @param successCallback * @param requestInstance */ const sendMail = (type, to, subject, html, taskIds, successCallback, requestInstance) => { const fromEmails = InstanceManager({ imex: "ImEX Online ", rome: requestInstance === "promanager" ? "ProManager " : "Rome Online " }); mailer.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?.body?.event?.data?.new) { return res.status(400).json({ message: "No data in the event body" }); } const { new: newTask } = req.body.event.data; // This is not a new task, but a reassignment. const dirty = req.body.event.data?.old && req.body.event.data?.old?.assigned_to; //Query to get the employee assigned currently. const { tasks_by_pk } = await client.request(queries.QUERY_TASK_BY_ID, { id: newTask.id }); const dateLine = moment().tz(tasks_by_pk.bodyshop.timezone).format("M/DD/YYYY @ hh:mm a"); sendMail( "assigned", tasks_by_pk.assigned_to_employee.user_email, `A ${formatPriority(newTask.priority)} priority task has been ${dirty ? "reassigned to" : "created for"} you - ${newTask.title}`, generateEmailTemplate( generateTemplateArgs( newTask.title, newTask.priority, newTask.description, newTask.due_date, tasks_by_pk.bodyshop, tasks_by_pk.job, newTask.id, dateLine, newTask.created_by ) ), null, null, tasks_by_pk.bodyshop.convenient_company ); // 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_employee.user_email; 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 fromEmails = InstanceManager({ imex: "ImEX Online ", rome: tasksRequest?.tasks[0].bodyshop.convenient_company === "promanager" ? "ProManager " : "Rome Online " }); const emailData = { from: fromEmails, to: recipient.email }; const taskIds = groupedTasks[recipient.email].map((task) => task.id); const dateLine = moment().tz(tasksRequest?.tasks[0].bodyshop.timezone).format("M/DD/YYYY @ hh:mm a"); // There is only the one email to send to this author. if (recipient.count === 1) { const onlyTask = groupedTasks[recipient.email][0]; emailData.subject = `New ${formatPriority(onlyTask.priority)} Priority Task Reminder - ${onlyTask.title} ${onlyTask.due_date ? `- ${formatDate(onlyTask.due_date)}` : ""}`.trim(); emailData.html = generateEmailTemplate( generateTemplateArgs( onlyTask.title, onlyTask.priority, onlyTask.description, onlyTask.due_date, onlyTask.bodyshop, onlyTask.job, onlyTask.id, dateLine, onlyTask.created_by ) ); } // There are multiple emails to send to this author. else { const endPoints = InstanceManager({ imex: process.env?.NODE_ENV === "test" ? "https://test.imex.online" : "https://imex.online", rome: tasksRequest?.tasks[0].bodyshop.convenient_company === "promanager" ? process.env?.NODE_ENV === "test" ? "https//test.promanager.web-est.com" : "https://promanager.web-est.com" : process.env?.NODE_ENV === "test" ? "https//test.romeonline.io" : "https://romeonline.io" }); const allTasks = groupedTasks[recipient.email]; emailData.subject = `New Tasks Reminder - ${allTasks.length} Tasks require your attention`; emailData.html = generateEmailTemplate({ header: `${allTasks.length} Tasks require your attention`, subHeader: `Please click on the Tasks below to view the Task.`, dateLine, body: `` }); } if (emailData?.subject && emailData?.html) { // Send Email sendMail( "remind", emailData.to, emailData.subject, emailData.html, taskIds, (taskIds) => { for (const taskId of taskIds) { tasksEmailQueue.push(taskId); } }, tasksRequest?.tasks[0].bodyshop.convenient_company ); } }); // 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"}` }); } }; // Note: Uncomment this to test locally, it will call the remind_at email check every 20 seconds // const callTaskRemindEmailInternally = () => { // const req = { // body: { // // You can mock any request data here if needed // } // }; // // const res = { // status: (code) => { // return { // json: (data) => { // console.log(`Response Status: ${code}`, data); // } // }; // } // }; // // // Call the taskRemindEmail function with mock req and res // tasksRemindEmail(req, res); // }; // setInterval(callTaskRemindEmailInternally, 20000); module.exports = { taskAssignedEmail, tasksRemindEmail, getEndpoints };