const { getMonitorByIdParamValidation, getMonitorByIdQueryValidation, getMonitorsByTeamIdValidation, createMonitorBodyValidation, editMonitorBodyValidation, getMonitorsAndSummaryByTeamIdParamValidation, getMonitorsAndSummaryByTeamIdQueryValidation, getMonitorsByTeamIdQueryValidation, pauseMonitorParamValidation, getMonitorStatsByIdParamValidation, getMonitorStatsByIdQueryValidation, getCertificateParamValidation, getMonitorAggregateStatsParamValidation, getMonitorAggregateStatsQueryValidation, } = require("../validation/joi"); const sslChecker = require("ssl-checker"); const SERVICE_NAME = "monitorController"; const { errorMessages, successMessages } = require("../utils/messages"); const jwt = require("jsonwebtoken"); const { getTokenFromHeaders } = require("../utils/utils"); const logger = require("../utils/logger"); /** * Returns all monitors * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @throws {Error} */ const getAllMonitors = async (req, res, next) => { try { const monitors = await req.db.getAllMonitors(); return res.json({ success: true, msg: successMessages.MONITOR_GET_ALL, data: monitors, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "getAllMonitors") : null; next(error); } }; /** * Returns monitor stats for monitor with matching ID * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @throws {Error} */ const getMonitorStatsById = async (req, res, next) => { try { await getMonitorStatsByIdParamValidation.validateAsync(req.params); await getMonitorStatsByIdQueryValidation.validateAsync(req.query); } catch (error) { error.status = 422; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); return; } try { const monitorStats = await req.db.getMonitorStatsById(req); return res.json({ success: true, msg: successMessages.MONTIOR_STATS_BY_ID, data: monitorStats, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "getMonitorStatsById") : null; next(error); } }; const getMonitorCertificate = async (req, res, next) => { try { await getCertificateParamValidation.validateAsync(req.params); } catch (error) { error.status = 422; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); } try { const { monitorId } = req.params; const monitor = await req.db.getMonitorById(monitorId); const monitorUrl = new URL(monitor.url); const certificate = await sslChecker(monitorUrl.hostname); if (certificate && certificate.validTo) { return res.json({ success: true, msg: successMessages.MONITOR_CERTIFICATE, data: { certificateDate: new Date(certificate.validTo).toLocaleDateString(), }, }); } else { return res.json({ success: true, msg: successMessages.MONITOR_CERTIFICATE, data: { certificateDate: "N/A" }, }); } } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "getMonitorCertificate") : null; next(error); } }; /** * Returns monitor with matching ID * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @throws {Error} */ const getMonitorById = async (req, res, next) => { try { await getMonitorByIdParamValidation.validateAsync(req.params); await getMonitorByIdQueryValidation.validateAsync(req.query); } catch (error) { error.status = 422; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); return; } try { const monitor = await req.db.getMonitorById(req.params.monitorId); if (!monitor) { const error = new Error(errorMessages.MONITOR_GET_BY_ID); error.status = 404; throw error; } return res.json({ success: true, msg: successMessages.MONTIOR_GET_BY_ID, data: monitor, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "getMonitorById") : null; next(error); } }; /** * Returns all monitors and a sumamry for a team with TeamID * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @throws {Error} */ const getMonitorsAndSummaryByTeamId = async (req, res, next) => { try { await getMonitorsAndSummaryByTeamIdParamValidation.validateAsync( req.params ); await getMonitorsAndSummaryByTeamIdQueryValidation.validateAsync(req.query); //validation } catch (error) { error.status = 422; error.service = SERVICE_NAME; error.method === undefined && error.method === "getMonitorsAndSummaryByTeamId"; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); return; } try { const { teamId } = req.params; const { type } = req.query; const monitorsSummary = await req.db.getMonitorsAndSummaryByTeamId( teamId, type ); return res.status(200).json({ success: true, msg: successMessages.MONITOR_GET_BY_USER_ID(teamId), data: monitorsSummary, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "getMonitorsAndSummaryByTeamId") : null; next(error); } }; /** * Returns all monitors belong to team with TeamID * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @throws {Error} */ const getMonitorsByTeamId = async (req, res, next) => { try { await getMonitorsByTeamIdValidation.validateAsync(req.params); await getMonitorsByTeamIdQueryValidation.validateAsync(req.query); } catch (error) { error.status = 422; error.service = SERVICE_NAME; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); return; } try { const teamId = req.params.teamId; const monitors = await req.db.getMonitorsByTeamId(req, res); return res.json({ success: true, msg: successMessages.MONITOR_GET_BY_USER_ID(teamId), data: monitors, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "getMonitorsByTeamId") : null; next(error); } }; /** * Creates a new monitor * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @throws {Error} */ const createMonitor = async (req, res, next) => { try { await createMonitorBodyValidation.validateAsync(req.body); } catch (error) { error.status = 422; error.service = SERVICE_NAME; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); return; } try { const notifications = req.body.notifications; const monitor = await req.db.createMonitor(req, res); if (notifications && notifications.length !== 0) { const setNotifications = await Promise.all( notifications.map(async (notification) => { notification.monitorId = monitor._id; await req.db.createNotification(notification); }) ); monitor.notifications = setNotifications; await monitor.save(); } // Add monitor to job queue req.jobQueue.addJob(monitor._id, monitor); return res.status(201).json({ success: true, msg: successMessages.MONITOR_CREATE, data: monitor, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "createMonitor") : null; next(error); } }; /** * Delete a monitor by ID * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @throws {Error} */ const deleteMonitor = async (req, res, next) => { try { await getMonitorByIdParamValidation.validateAsync(req.params); } catch (error) { error.status = 422; error.service = SERVICE_NAME; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); return; } try { const monitor = await req.db.deleteMonitor(req, res, next); // Delete associated checks,alerts,and notifications try { await req.jobQueue.deleteJob(monitor); await req.db.deleteChecks(monitor._id); await req.db.deletePageSpeedChecksByMonitorId(monitor._id); await req.db.deleteNotificationsByMonitorId(monitor._id); } catch (error) { logger.error( `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`, { method: "deleteMonitor", service: SERVICE_NAME, error: error.message, } ); } return res .status(200) .json({ success: true, msg: successMessages.MONITOR_DELETE }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "deleteMonitor") : null; next(error); } }; const deleteAllMonitors = async (req, res) => { try { const token = getTokenFromHeaders(req.headers); const { jwtSecret } = req.settingsService.getSettings(); const { teamId } = jwt.verify(token, jwtSecret); const { monitors, deletedCount } = await req.db.deleteAllMonitors(teamId); await monitors.forEach(async (monitor) => { await req.jobQueue.deleteJob(monitor); await req.db.deleteChecks(monitor._id); await req.db.deleteAlertByMonitorId(monitor._id); await req.db.deletePageSpeedChecksByMonitorId(monitor._id); await req.db.deleteNotificationsByMonitorId(monitor._id); }); return res .status(200) .json({ success: true, msg: `Deleted ${deletedCount} monitors` }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "deleteAllMonitors") : null; next(error); } }; /** * Edit a monitor by ID * @async * @param {Express.Request} req * @param {Express.Response} res * @returns {Promise} * @throws {Error} */ const editMonitor = async (req, res, next) => { try { await getMonitorByIdParamValidation.validateAsync(req.params); await editMonitorBodyValidation.validateAsync(req.body); } catch (error) { error.status = 422; error.service = SERVICE_NAME; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); return; } try { const { monitorId } = req.params; const monitorBeforeEdit = await req.db.getMonitorById(monitorId); // Get notifications from the request body const notifications = req.body.notifications; const editedMonitor = await req.db.editMonitor(monitorId, req.body); await req.db.deleteNotificationsByMonitorId(editedMonitor._id); if (notifications && notifications.length !== 0) { await Promise.all( notifications.map(async (notification) => { notification.monitorId = editedMonitor._id; await req.db.createNotification(notification); }) ); } // Delete the old job(editedMonitor has the same ID as the old monitor) await req.jobQueue.deleteJob(monitorBeforeEdit); // Add the new job back to the queue await req.jobQueue.addJob(editedMonitor._id, editedMonitor); return res.status(200).json({ success: true, msg: successMessages.MONITOR_EDIT, data: editedMonitor, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "editMonitor") : null; next(error); } }; const pauseMonitor = async (req, res, next) => { try { await pauseMonitorParamValidation.validateAsync(req.params); } catch (error) { error.status = 422; error.service = SERVICE_NAME; error.message = error.details?.[0]?.message || error.message || "Validation Error"; next(error); } try { const monitor = await req.db.getMonitorById(req.params.monitorId); if (monitor.isActive) { await req.jobQueue.deleteJob(monitor); } else { await req.jobQueue.addJob(monitor._id, monitor); } monitor.isActive = !monitor.isActive; monitor.status = undefined; monitor.save(); return res.status(200).json({ success: true, msg: monitor.isActive ? successMessages.MONITOR_RESUME : successMessages.MONITOR_PAUSE, data: monitor, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "pauseMonitor") : null; next(error); } }; const addDemoMonitors = async (req, res, next) => { try { const token = getTokenFromHeaders(req.headers); const { jwtSecret } = req.settingsService.getSettings(); const { _id, teamId } = jwt.verify(token, jwtSecret); const demoMonitors = await req.db.addDemoMonitors(_id, teamId); await demoMonitors.forEach(async (monitor) => { await req.jobQueue.addJob(monitor._id, monitor); }); return res.status(200).json({ success: true, message: successMessages.MONITOR_DEMO_ADDED, data: demoMonitors.length, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; error.method === undefined ? (error.method = "addDemoMonitors") : null; next(error); } }; module.exports = { getAllMonitors, getMonitorStatsById, getMonitorCertificate, getMonitorById, getMonitorsAndSummaryByTeamId, getMonitorsByTeamId, createMonitor, deleteMonitor, deleteAllMonitors, editMonitor, pauseMonitor, addDemoMonitors, };