mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-10 19:59:39 -06:00
527 lines
18 KiB
JavaScript
527 lines
18 KiB
JavaScript
import {
|
|
getMonitorByIdParamValidation,
|
|
getMonitorByIdQueryValidation,
|
|
getMonitorsByTeamIdValidation,
|
|
createMonitorBodyValidation,
|
|
getMonitorURLByQueryValidation,
|
|
editMonitorBodyValidation,
|
|
getMonitorsAndSummaryByTeamIdParamValidation,
|
|
getMonitorsAndSummaryByTeamIdQueryValidation,
|
|
getMonitorsByTeamIdQueryValidation,
|
|
pauseMonitorParamValidation,
|
|
getMonitorStatsByIdParamValidation,
|
|
getMonitorStatsByIdQueryValidation,
|
|
getCertificateParamValidation,
|
|
} from "../validation/joi.js";
|
|
import sslChecker from "ssl-checker";
|
|
import { errorMessages, successMessages } from "../utils/messages.js";
|
|
import jwt from "jsonwebtoken";
|
|
import { getTokenFromHeaders } from "../utils/utils.js";
|
|
import logger from "../utils/logger.js";
|
|
import { handleError, handleValidationError } from "./controllerUtils.js";
|
|
import dns from "dns";
|
|
|
|
const SERVICE_NAME = "monitorController";
|
|
|
|
/**
|
|
* Returns all monitors
|
|
* @async
|
|
* @param {Express.Request} req
|
|
* @param {Express.Response} res
|
|
* @param {function} next
|
|
* @returns {Promise<Express.Response>}
|
|
* @throws {Error}
|
|
*/
|
|
const getAllMonitors = async (req, res, next) => {
|
|
try {
|
|
const monitors = await req.db.getAllMonitors();
|
|
return res.status(200).json({
|
|
success: true,
|
|
msg: successMessages.MONITOR_GET_ALL,
|
|
data: monitors,
|
|
});
|
|
} catch (error) {
|
|
next(handleError(error, SERVICE_NAME, "getAllMonitors"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns monitor stats for monitor with matching ID
|
|
* @async
|
|
* @param {Express.Request} req
|
|
* @param {Express.Response} res
|
|
* @param {function} next
|
|
* @returns {Promise<Express.Response>}
|
|
* @throws {Error}
|
|
*/
|
|
const getMonitorStatsById = async (req, res, next) => {
|
|
try {
|
|
await getMonitorStatsByIdParamValidation.validateAsync(req.params);
|
|
await getMonitorStatsByIdQueryValidation.validateAsync(req.query);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const monitorStats = await req.db.getMonitorStatsById(req);
|
|
return res.status(200).json({
|
|
success: true,
|
|
msg: successMessages.MONITOR_STATS_BY_ID,
|
|
data: monitorStats,
|
|
});
|
|
} catch (error) {
|
|
next(handleError(error, SERVICE_NAME, "getMonitorStatsById"));
|
|
}
|
|
};
|
|
|
|
const getMonitorCertificate = async (req, res, next, fetchMonitorCertificate) => {
|
|
try {
|
|
await getCertificateParamValidation.validateAsync(req.params);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
}
|
|
|
|
try {
|
|
const { monitorId } = req.params;
|
|
const monitor = await req.db.getMonitorById(monitorId);
|
|
const certificate = await fetchMonitorCertificate(sslChecker, monitor);
|
|
if (certificate && certificate.validTo) {
|
|
return res.status(200).json({
|
|
success: true,
|
|
msg: successMessages.MONITOR_CERTIFICATE,
|
|
data: {
|
|
certificateDate: new Date(certificate.validTo),
|
|
},
|
|
});
|
|
} else {
|
|
return res.status(200).json({
|
|
success: true,
|
|
msg: successMessages.MONITOR_CERTIFICATE,
|
|
data: { certificateDate: "N/A" },
|
|
});
|
|
}
|
|
} catch (error) {
|
|
next(handleError(error, SERVICE_NAME, "getMonitorCertificate"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Retrieves a monitor by its ID.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.params - The parameters of the request.
|
|
* @property {string} req.params.monitorId - The ID of the monitor to be retrieved.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status, a message, and the retrieved monitor data.
|
|
* @throws {Error} If there is an error during the process, especially if the monitor is not found (404) or if there is a validation error (422).
|
|
*/
|
|
const getMonitorById = async (req, res, next) => {
|
|
try {
|
|
await getMonitorByIdParamValidation.validateAsync(req.params);
|
|
await getMonitorByIdQueryValidation.validateAsync(req.query);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
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.status(200).json({
|
|
success: true,
|
|
msg: successMessages.MONITOR_GET_BY_ID,
|
|
data: monitor,
|
|
});
|
|
} catch (error) {
|
|
next(handleError(error, SERVICE_NAME, "getMonitorById"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Retrieves all monitors and a summary for a team based on the team ID.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.params - The parameters of the request.
|
|
* @property {string} req.params.teamId - The ID of the team.
|
|
* @property {Object} req.query - The query parameters of the request.
|
|
* @property {string} req.query.type - The type of the request.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status, a message, and the data containing the monitors and summary for the team.
|
|
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
|
*/
|
|
const getMonitorsAndSummaryByTeamId = async (req, res, next) => {
|
|
try {
|
|
await getMonitorsAndSummaryByTeamIdParamValidation.validateAsync(req.params);
|
|
await getMonitorsAndSummaryByTeamIdQueryValidation.validateAsync(req.query);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
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) {
|
|
next(handleError(error, SERVICE_NAME, "getMonitorsAndSummaryByTeamId"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Retrieves all monitors associated with a team by the team's ID.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.params - The parameters of the request.
|
|
* @property {string} req.params.teamId - The ID of the team.
|
|
* @property {Object} req.query - The query parameters of the request.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status, a message, and the data containing the monitors for the team.
|
|
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
|
*/
|
|
const getMonitorsByTeamId = async (req, res, next) => {
|
|
try {
|
|
await getMonitorsByTeamIdValidation.validateAsync(req.params);
|
|
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const teamId = req.params.teamId;
|
|
const monitors = await req.db.getMonitorsByTeamId(req, res);
|
|
return res.status(200).json({
|
|
success: true,
|
|
msg: successMessages.MONITOR_GET_BY_USER_ID(teamId),
|
|
data: monitors,
|
|
});
|
|
} catch (error) {
|
|
next(handleError(error, SERVICE_NAME, "getMonitorsByTeamId"));
|
|
next(error);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Creates a new monitor and adds it to the job queue.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.body - The body of the request.
|
|
* @property {Array} req.body.notifications - The notifications associated with the monitor.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status, a message indicating the creation of the monitor, and the created monitor data.
|
|
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
|
*/
|
|
const createMonitor = async (req, res, next) => {
|
|
try {
|
|
await createMonitorBodyValidation.validateAsync(req.body);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const notifications = req.body.notifications;
|
|
const monitor = await req.db.createMonitor(req, res);
|
|
|
|
if (notifications && notifications.length !== 0) {
|
|
monitor.notifications = await Promise.all(
|
|
notifications.map(async (notification) => {
|
|
notification.monitorId = monitor._id;
|
|
await req.db.createNotification(notification);
|
|
})
|
|
);
|
|
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) {
|
|
next(handleError(error, SERVICE_NAME, "createMonitor"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Checks if the endpoint can be resolved
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.query - The query parameters of the request.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status, a message, and the resolution result.
|
|
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
|
*/
|
|
const checkEndpointResolution = async (req, res, next) => {
|
|
try {
|
|
await getMonitorURLByQueryValidation.validateAsync(req.query);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
return;
|
|
}
|
|
|
|
try {
|
|
let { monitorURL } = req.query;
|
|
monitorURL = new URL(monitorURL);
|
|
await new Promise((resolve, reject) => {
|
|
dns.resolve(monitorURL.hostname, (error) => {
|
|
if (error) {
|
|
reject(error);
|
|
}
|
|
resolve();
|
|
});
|
|
});
|
|
return res.status(200).json({
|
|
success: true,
|
|
msg: `URL resolved successfully`,
|
|
});
|
|
} catch (error) {
|
|
next(handleError(error, SERVICE_NAME, "checkEndpointResolution"));
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Deletes a monitor by its ID and also deletes associated checks, alerts, and notifications.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.params - The parameters of the request.
|
|
* @property {string} req.params.monitorId - The ID of the monitor to be deleted.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status and a message indicating the deletion of the monitor.
|
|
* @throws {Error} If there is an error during the process, especially if there is a validation error (422) or an error in deleting associated records.
|
|
*/
|
|
const deleteMonitor = async (req, res, next) => {
|
|
try {
|
|
await getMonitorByIdParamValidation.validateAsync(req.params);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
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) {
|
|
next(handleError(error, SERVICE_NAME, "deleteMonitor"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Deletes all monitors associated with a team.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.headers - The headers of the request.
|
|
* @property {string} req.headers.authorization - The authorization header containing the JWT token.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next
|
|
* @returns {Object} The response object with a success status and a message indicating the number of deleted monitors.
|
|
* @throws {Error} If there is an error during the deletion process.
|
|
*/
|
|
const deleteAllMonitors = async (req, res, next) => {
|
|
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 Promise.all(
|
|
monitors.map(async (monitor) => {
|
|
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: "deleteAllMonitors",
|
|
}
|
|
);
|
|
}
|
|
})
|
|
);
|
|
return res
|
|
.status(200)
|
|
.json({ success: true, msg: `Deleted ${deletedCount} monitors` });
|
|
} catch (error) {
|
|
next(handleError(error, SERVICE_NAME, "deleteAllMonitors"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Edits a monitor by its ID, updates its notifications, and updates its job in the job queue.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.params - The parameters of the request.
|
|
* @property {string} req.params.monitorId - The ID of the monitor to be edited.
|
|
* @property {Object} req.body - The body of the request.
|
|
* @property {Array} req.body.notifications - The notifications to be associated with the monitor.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status, a message indicating the editing of the monitor, and the edited monitor data.
|
|
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
|
*/
|
|
const editMonitor = async (req, res, next) => {
|
|
try {
|
|
await getMonitorByIdParamValidation.validateAsync(req.params);
|
|
await editMonitorBodyValidation.validateAsync(req.body);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
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) {
|
|
next(handleError(error, SERVICE_NAME, "editMonitor"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Pauses or resumes a monitor based on its current state.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.params - The parameters of the request.
|
|
* @property {string} req.params.monitorId - The ID of the monitor to be paused or resumed.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status, a message indicating the new state of the monitor, and the updated monitor data.
|
|
* @throws {Error} If there is an error during the process.
|
|
*/
|
|
const pauseMonitor = async (req, res, next) => {
|
|
try {
|
|
await pauseMonitorParamValidation.validateAsync(req.params);
|
|
} catch (error) {
|
|
next(handleValidationError(error, SERVICE_NAME));
|
|
}
|
|
|
|
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) {
|
|
next(handleError(error, SERVICE_NAME, "pauseMonitor"));
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Adds demo monitors for a team.
|
|
* @async
|
|
* @param {Object} req - The Express request object.
|
|
* @property {Object} req.headers - The headers of the request.
|
|
* @property {string} req.headers.authorization - The authorization header containing the JWT token.
|
|
* @param {Object} res - The Express response object.
|
|
* @param {function} next - The next middleware function.
|
|
* @returns {Object} The response object with a success status, a message indicating the addition of demo monitors, and the number of demo monitors added.
|
|
* @throws {Error} If there is an error during the process.
|
|
*/
|
|
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 Promise.all(
|
|
demoMonitors.map((monitor) => req.jobQueue.addJob(monitor._id, monitor))
|
|
);
|
|
|
|
return res.status(200).json({
|
|
success: true,
|
|
msg: successMessages.MONITOR_DEMO_ADDED,
|
|
data: demoMonitors.length,
|
|
});
|
|
} catch (error) {
|
|
next(handleError(error, SERVICE_NAME, "addDemoMonitors"));
|
|
}
|
|
};
|
|
|
|
export {
|
|
getAllMonitors,
|
|
getMonitorStatsById,
|
|
getMonitorCertificate,
|
|
getMonitorById,
|
|
getMonitorsAndSummaryByTeamId,
|
|
getMonitorsByTeamId,
|
|
createMonitor,
|
|
checkEndpointResolution,
|
|
deleteMonitor,
|
|
deleteAllMonitors,
|
|
editMonitor,
|
|
pauseMonitor,
|
|
addDemoMonitors,
|
|
};
|