2024-10-15 11:57:37 +08:00
import {
2024-10-16 11:47:12 +08:00
getMonitorByIdParamValidation ,
getMonitorByIdQueryValidation ,
2025-01-08 13:42:08 -08:00
getMonitorsByTeamIdParamValidation ,
getMonitorsByTeamIdQueryValidation ,
2024-10-16 11:47:12 +08:00
createMonitorBodyValidation ,
2024-10-24 11:36:56 +08:00
getMonitorURLByQueryValidation ,
2024-10-16 11:47:12 +08:00
editMonitorBodyValidation ,
pauseMonitorParamValidation ,
getMonitorStatsByIdParamValidation ,
getMonitorStatsByIdQueryValidation ,
getCertificateParamValidation ,
2025-01-01 09:08:07 -08:00
getHardwareDetailsByIdParamValidation ,
getHardwareDetailsByIdQueryValidation ,
2024-10-15 11:57:37 +08:00
} from "../validation/joi.js" ;
2024-10-17 11:57:37 +08:00
import sslChecker from "ssl-checker" ;
2024-12-24 11:58:42 -08:00
import { successMessages } from "../utils/messages.js" ;
2024-10-15 11:57:37 +08:00
import jwt from "jsonwebtoken" ;
import { getTokenFromHeaders } from "../utils/utils.js" ;
import logger from "../utils/logger.js" ;
2024-10-17 11:57:37 +08:00
import { handleError , handleValidationError } from "./controllerUtils.js" ;
2024-11-20 10:37:01 +08:00
import axios from "axios" ;
2024-10-17 10:35:41 +05:30
2024-05-24 13:44:42 -07:00
const SERVICE_NAME = "monitorController" ;
2024-09-11 11:03:39 +08:00
2024-12-24 11:58:42 -08:00
class MonitorController {
constructor ( db , settingsService , jobQueue ) {
this . db = db ;
this . settingsService = settingsService ;
this . jobQueue = jobQueue ;
2024-10-16 11:47:12 +08:00
}
2024-08-12 12:05:57 -07:00
2024-12-24 11:58:42 -08:00
/**
* Returns all monitors
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @param {function} next
* @returns {Promise<Express.Response>}
* @throws {Error}
*/
getAllMonitors = async ( req , res , next ) => {
try {
const monitors = await this . db . getAllMonitors ();
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-24 11:58:42 -08:00
msg : successMessages . MONITOR_GET_ALL ,
data : monitors ,
});
} catch ( error ) {
next ( handleError ( error , SERVICE_NAME , "getAllMonitors" ));
}
};
/**
* Returns all monitors with uptime stats for 1,7,30, and 90 days
* @async
* @param {Express.Request} req
* @param {Express.Response} res
* @param {function} next
* @returns {Promise<Express.Response>}
* @throws {Error}
*/
getAllMonitorsWithUptimeStats = async ( req , res , next ) => {
try {
const monitors = await this . db . getAllMonitorsWithUptimeStats ();
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-24 11:58:42 -08:00
msg : successMessages . MONITOR_GET_ALL ,
data : monitors ,
});
} catch ( error ) {
next ( handleError ( error , SERVICE_NAME , "getAllMonitorsWithUptimeStats" ));
}
};
2024-12-30 14:56:50 -08:00
getUptimeDetailsById = async ( req , res , next ) => {
try {
const monitor = await this . db . getUptimeDetailsById ( req );
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-30 14:56:50 -08:00
msg : successMessages . MONITOR_GET_BY_ID ,
data : monitor ,
});
} catch ( error ) {
next ( handleError ( error , SERVICE_NAME , "getMonitorDetailsById" ));
}
};
2024-12-24 11:58:42 -08:00
/**
* 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}
*/
getMonitorStatsById = async ( req , res , next ) => {
try {
await getMonitorStatsByIdParamValidation . validateAsync ( req . params );
await getMonitorStatsByIdQueryValidation . validateAsync ( req . query );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
return ;
}
2024-08-12 15:15:13 -07:00
2024-12-24 11:58:42 -08:00
try {
const monitorStats = await this . db . getMonitorStatsById ( req );
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-24 11:58:42 -08:00
msg : successMessages . MONITOR_STATS_BY_ID ,
data : monitorStats ,
});
} catch ( error ) {
next ( handleError ( error , SERVICE_NAME , "getMonitorStatsById" ));
}
};
2024-05-10 13:36:04 -07:00
2025-01-01 09:08:07 -08:00
/**
* Get hardware details for a specific monitor by ID
* @async
* @param {Express.Request} req - Express request object containing monitorId in params
* @param {Express.Response} res - Express response object
* @param {Express.NextFunction} next - Express next middleware function
* @returns {Promise<Express.Response>}
* @throws {Error} - Throws error if monitor not found or other database errors
*/
2024-12-31 15:05:03 -08:00
getHardwareDetailsById = async ( req , res , next ) => {
2025-01-01 09:08:07 -08:00
try {
await getHardwareDetailsByIdParamValidation . validateAsync ( req . params );
await getHardwareDetailsByIdQueryValidation . validateAsync ( req . query );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
return ;
}
2024-12-31 15:05:03 -08:00
try {
const monitor = await this . db . getHardwareDetailsById ( req );
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-31 15:05:03 -08:00
msg : successMessages . MONITOR_GET_BY_ID ,
data : monitor ,
});
} catch ( error ) {
next ( handleError ( error , SERVICE_NAME , "getHardwareDetailsById" ));
}
};
2024-12-24 11:58:42 -08:00
getMonitorCertificate = async ( req , res , next , fetchMonitorCertificate ) => {
try {
await getCertificateParamValidation . validateAsync ( req . params );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
}
2024-09-05 11:17:31 -07:00
2024-12-24 11:58:42 -08:00
try {
const { monitorId } = req . params ;
const monitor = await this . db . getMonitorById ( monitorId );
const certificate = await fetchMonitorCertificate ( sslChecker , monitor );
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-24 11:58:42 -08:00
msg : successMessages . MONITOR_CERTIFICATE ,
data : {
certificateDate : new Date ( certificate . validTo ),
},
});
} 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).
*/
getMonitorById = async ( req , res , next ) => {
try {
await getMonitorByIdParamValidation . validateAsync ( req . params );
await getMonitorByIdQueryValidation . validateAsync ( req . query );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
return ;
}
2024-07-30 13:36:53 -07:00
2024-12-24 11:58:42 -08:00
try {
const monitor = await this . db . getMonitorById ( req . params . monitorId );
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-24 11:58:42 -08:00
msg : successMessages . MONITOR_GET_BY_ID ,
data : monitor ,
});
} catch ( error ) {
next ( handleError ( error , SERVICE_NAME , "getMonitorById" ));
}
};
/**
* 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).
*/
createMonitor = async ( req , res , next ) => {
try {
await createMonitorBodyValidation . validateAsync ( req . body );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
return ;
2024-10-26 18:45:51 +08:00
}
2024-12-24 11:58:42 -08:00
try {
const notifications = req . body . notifications ;
const monitor = await this . db . createMonitor ( req , res );
if ( notifications && notifications . length > 0 ) {
monitor . notifications = await Promise . all (
notifications . map ( async ( notification ) => {
notification . monitorId = monitor . _id ;
return await this . db . createNotification ( notification );
})
);
}
await monitor . save ();
// Add monitor to job queue
this . jobQueue . addJob ( monitor . _id , monitor );
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-24 11:58:42 -08:00
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).
*/
checkEndpointResolution = async ( req , res , next ) => {
try {
await getMonitorURLByQueryValidation . validateAsync ( req . query );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
return ;
}
2024-10-17 12:35:15 +05:30
2024-12-24 11:58:42 -08:00
try {
const { monitorURL } = req . query ;
const parsedUrl = new URL ( monitorURL );
const response = await axios . get ( parsedUrl , {
timeout : 5000 ,
validateStatus : () => true ,
});
2025-01-19 12:01:27 -08:00
return res . success ({
status : response . status ,
msg : response . statusText ,
2024-12-24 11:58:42 -08:00
});
} 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.
*/
deleteMonitor = async ( req , res , next ) => {
try {
await getMonitorByIdParamValidation . validateAsync ( req . params );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
return ;
}
2024-05-27 10:47:14 -07:00
2024-10-16 11:47:12 +08:00
try {
2024-12-24 11:58:42 -08:00
const monitor = await this . db . deleteMonitor ( req , res , next );
// Delete associated checks,alerts,and notifications
try {
await this . jobQueue . deleteJob ( monitor );
await this . db . deleteChecks ( monitor . _id );
await this . db . deletePageSpeedChecksByMonitorId ( monitor . _id );
await this . db . deleteNotificationsByMonitorId ( monitor . _id );
await this . db . deleteHardwareChecksByMonitorId ( monitor . _id );
} catch ( error ) {
logger . error ({
message : `Error deleting associated records for monitor ${ monitor . _id } with name ${ monitor . name } ` ,
service : SERVICE_NAME ,
method : "deleteMonitor" ,
stack : error . stack ,
});
}
2025-01-19 12:01:27 -08:00
return res . success ({ msg : successMessages . MONITOR_DELETE });
2024-10-16 11:47:12 +08:00
} catch ( error ) {
2024-12-24 11:58:42 -08:00
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.
*/
deleteAllMonitors = async ( req , res , next ) => {
try {
const token = getTokenFromHeaders ( req . headers );
const { jwtSecret } = this . settingsService . getSettings ();
const { teamId } = jwt . verify ( token , jwtSecret );
const { monitors , deletedCount } = await this . db . deleteAllMonitors ( teamId );
await Promise . all (
monitors . map ( async ( monitor ) => {
try {
await this . jobQueue . deleteJob ( monitor );
await this . db . deleteChecks ( monitor . _id );
await this . db . deletePageSpeedChecksByMonitorId ( monitor . _id );
await this . db . deleteNotificationsByMonitorId ( monitor . _id );
} catch ( error ) {
logger . error ({
message : `Error deleting associated records for monitor ${ monitor . _id } with name ${ monitor . name } ` ,
service : SERVICE_NAME ,
method : "deleteAllMonitors" ,
stack : error . stack ,
});
}
})
);
2025-01-19 12:01:27 -08:00
return res . success ({ msg : `Deleted ${ deletedCount } monitors` });
2024-12-24 11:58:42 -08:00
} 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).
*/
editMonitor = async ( req , res , next ) => {
try {
await getMonitorByIdParamValidation . validateAsync ( req . params );
await editMonitorBodyValidation . validateAsync ( req . body );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
return ;
2024-10-16 11:47:12 +08:00
}
2024-05-21 13:52:28 -07:00
2024-12-24 11:58:42 -08:00
try {
const { monitorId } = req . params ;
const monitorBeforeEdit = await this . db . getMonitorById ( monitorId );
2024-07-31 13:59:14 -07:00
2024-12-24 11:58:42 -08:00
// Get notifications from the request body
const notifications = req . body . notifications ;
2024-07-31 13:59:14 -07:00
2024-12-24 11:58:42 -08:00
const editedMonitor = await this . db . editMonitor ( monitorId , req . body );
2024-07-31 13:59:14 -07:00
2024-12-24 11:58:42 -08:00
await this . db . deleteNotificationsByMonitorId ( editedMonitor . _id );
2024-07-31 13:59:14 -07:00
2024-12-24 11:58:42 -08:00
await Promise . all (
notifications &&
notifications . map ( async ( notification ) => {
notification . monitorId = editedMonitor . _id ;
await this . db . createNotification ( notification );
})
);
2024-08-28 15:21:59 -07:00
2024-12-24 11:58:42 -08:00
// Delete the old job(editedMonitor has the same ID as the old monitor)
await this . jobQueue . deleteJob ( monitorBeforeEdit );
// Add the new job back to the queue
await this . jobQueue . addJob ( editedMonitor . _id , editedMonitor );
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-24 11:58:42 -08:00
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.
*/
pauseMonitor = async ( req , res , next ) => {
try {
await pauseMonitorParamValidation . validateAsync ( req . params );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
}
try {
const monitor = await this . db . getMonitorById ( req . params . monitorId );
monitor . isActive === true
? await this . jobQueue . deleteJob ( monitor )
: await this . jobQueue . addJob ( monitor . _id , monitor );
monitor . isActive = ! monitor . isActive ;
monitor . status = undefined ;
monitor . save ();
2025-01-19 12:01:27 -08:00
return res . ssuccess ({
2024-12-24 11:58:42 -08:00
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.
*/
addDemoMonitors = async ( req , res , next ) => {
try {
const token = getTokenFromHeaders ( req . headers );
const { jwtSecret } = this . settingsService . getSettings ();
const { _id , teamId } = jwt . verify ( token , jwtSecret );
const demoMonitors = await this . db . addDemoMonitors ( _id , teamId );
await Promise . all (
demoMonitors . map (( monitor ) => this . jobQueue . addJob ( monitor . _id , monitor ))
);
2025-01-19 12:01:27 -08:00
return res . success ({
2024-12-24 11:58:42 -08:00
msg : successMessages . MONITOR_DEMO_ADDED ,
data : demoMonitors . length ,
});
} catch ( error ) {
next ( handleError ( error , SERVICE_NAME , "addDemoMonitors" ));
}
};
2025-01-08 13:42:08 -08:00
getMonitorsByTeamId = async ( req , res , next ) => {
try {
await getMonitorsByTeamIdParamValidation . validateAsync ( req . params );
await getMonitorsByTeamIdQueryValidation . validateAsync ( req . query );
} catch ( error ) {
next ( handleValidationError ( error , SERVICE_NAME ));
}
try {
const monitors = await this . db . getMonitorsByTeamId ( req );
2025-01-19 12:01:27 -08:00
return res . success ({
msg : successMessages . MONITOR_GET_BY_TEAM_ID ,
2025-01-08 13:42:08 -08:00
data : monitors ,
});
} catch ( error ) {
2025-01-09 10:42:01 -08:00
next ( handleError ( error , SERVICE_NAME , "getMonitorsByTeamId" ));
2025-01-08 13:42:08 -08:00
}
};
2024-12-24 11:58:42 -08:00
}
export default MonitorController ;