Files
Checkmate/server/src/controllers/monitorController.ts
T

489 lines
15 KiB
TypeScript
Raw Normal View History

2026-01-09 14:49:44 -08:00
import { Request, Response, NextFunction } from "express";
import { updateNotificationsValidation } from "@/validation/notificationValidation.js";
2026-01-09 14:49:44 -08:00
import {
getMonitorByIdParamValidation,
getMonitorByIdQueryValidation,
getMonitorsByTeamIdQueryValidation,
2026-01-14 21:47:37 +00:00
getMonitorsWithChecksQueryValidation,
2026-01-09 14:49:44 -08:00
createMonitorBodyValidation,
editMonitorBodyValidation,
pauseMonitorParamValidation,
getCertificateParamValidation,
getHardwareDetailsByIdParamValidation,
getHardwareDetailsByIdQueryValidation,
2026-03-11 12:41:27 -07:00
getUptimeDetailsByIdParamValidation,
getUptimeDetailsByIdQueryValidation,
importMonitorsBodyValidation,
bulkPauseMonitorBodyValidation,
2026-03-01 17:35:00 +00:00
} from "@/validation/monitorValidation.js";
2026-01-09 14:49:44 -08:00
import sslChecker from "ssl-checker";
import { fetchMonitorCertificate, requireTeamId, requireUserId } from "@/controllers/controllerUtils.js";
2026-01-09 14:49:44 -08:00
import { AppError } from "@/utils/AppError.js";
import { IMonitorService, INotificationsService } from "@/service/index.js";
2026-01-09 14:49:44 -08:00
const SERVICE_NAME = "monitorController";
2026-03-20 11:47:22 -07:00
export interface IMonitorController {
getMonitorCertificate: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getUptimeDetailsById: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getHardwareDetailsById: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getPageSpeedDetailsById: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getGeoChecksByMonitorId: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getMonitorById: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
createMonitor: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
importMonitorsFromJSON: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
deleteMonitor: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
deleteAllMonitors: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
editMonitor: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
pauseMonitor: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
bulkPauseMonitors: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
2026-03-20 11:47:22 -07:00
addDemoMonitors: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getMonitorsByTeamId: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getMonitorsWithChecksByTeamId: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
exportMonitorsToJSON: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getAllGames: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
getGroupsByTeamId: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
updateNotifications: (req: Request, res: Response, next: NextFunction) => Promise<Response | void>;
2026-03-20 11:47:22 -07:00
}
class MonitorController implements IMonitorController {
2026-01-09 14:49:44 -08:00
static SERVICE_NAME = SERVICE_NAME;
2026-01-14 22:44:23 +00:00
private monitorService: IMonitorService;
private notificationsService: INotificationsService;
2026-01-09 14:49:44 -08:00
constructor(monitorService: IMonitorService, notificationsService: INotificationsService) {
2026-01-09 14:49:44 -08:00
this.monitorService = monitorService;
this.notificationsService = notificationsService;
2026-01-09 14:49:44 -08:00
}
get serviceName() {
return MonitorController.SERVICE_NAME;
}
getMonitorCertificate = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = getCertificateParamValidation.parse(req.params);
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-03-11 12:41:27 -07:00
const monitor = await this.monitorService.getMonitorById({ teamId, monitorId: validatedParams.monitorId });
2026-01-09 14:49:44 -08:00
const certificate = await fetchMonitorCertificate(sslChecker, monitor);
return res.status(200).json({
success: true,
msg: "SSL certificate retrieved successfully",
data: {
certificateDate: new Date(certificate.validTo),
},
});
} catch (error) {
next(error);
}
};
getUptimeDetailsById = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = getUptimeDetailsByIdParamValidation.parse(req.params);
const validatedQuery = getUptimeDetailsByIdQueryValidation.parse(req.query);
const monitorId = validatedParams.monitorId;
const dateRange = validatedQuery.dateRange;
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
const data = await this.monitorService.getUptimeDetailsById({
teamId,
monitorId,
dateRange,
});
return res.status(200).json({
success: true,
msg: "Uptime details retrieved successfully",
data: data,
});
} catch (error) {
next(error);
}
};
getHardwareDetailsById = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = getHardwareDetailsByIdParamValidation.parse(req.params);
const validatedQuery = getHardwareDetailsByIdQueryValidation.parse(req.query);
2026-01-09 14:49:44 -08:00
2026-03-11 12:41:27 -07:00
const monitorId = validatedParams.monitorId;
const dateRange = validatedQuery.dateRange || "recent";
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
2026-01-28 22:00:33 +00:00
const data = await this.monitorService.getHardwareDetailsById({
2026-01-09 14:49:44 -08:00
teamId,
monitorId,
dateRange,
});
return res.status(200).json({
success: true,
msg: "Hardware details retrieved successfully",
2026-01-28 22:00:33 +00:00
data: data,
2026-01-09 14:49:44 -08:00
});
} catch (error) {
next(error);
}
};
2026-01-14 22:44:23 +00:00
getPageSpeedDetailsById = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = getHardwareDetailsByIdParamValidation.parse(req.params);
const validatedQuery = getHardwareDetailsByIdQueryValidation.parse(req.query);
2026-01-14 22:44:23 +00:00
2026-03-11 12:41:27 -07:00
const monitorId = validatedParams.monitorId;
const dateRange = validatedQuery.dateRange || "recent";
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-14 22:44:23 +00:00
2026-01-16 21:04:41 +00:00
const data = await this.monitorService.getPageSpeedDetailsById({
2026-01-14 22:44:23 +00:00
teamId,
monitorId,
dateRange,
});
return res.status(200).json({
success: true,
msg: "Page speed details retrieved successfully",
2026-01-16 21:04:41 +00:00
data,
2026-01-14 22:44:23 +00:00
});
} catch (error) {
next(error);
}
};
2026-01-09 14:49:44 -08:00
2026-02-24 19:19:32 +00:00
getGeoChecksByMonitorId = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = getMonitorByIdParamValidation.parse(req.params);
const validatedQuery = getMonitorByIdQueryValidation.parse(req.query);
2026-02-24 19:19:32 +00:00
2026-03-11 12:41:27 -07:00
const monitorId = validatedParams.monitorId;
const dateRange = validatedQuery.dateRange || "recent";
const continentParam = validatedQuery.continent;
2026-03-11 12:57:27 -07:00
const continents = continentParam ? (Array.isArray(continentParam) ? continentParam : [continentParam]) : undefined;
const teamId = requireTeamId(req.user?.teamId);
2026-02-24 19:19:32 +00:00
const data = await this.monitorService.getGeoChecksByMonitorId({
teamId,
monitorId,
dateRange,
2026-02-25 18:43:12 +00:00
continents,
2026-02-24 19:19:32 +00:00
});
return res.status(200).json({
success: true,
msg: "Geo checks retrieved successfully",
data,
});
} catch (error) {
next(error);
}
};
2026-01-09 14:49:44 -08:00
getMonitorById = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = getMonitorByIdParamValidation.parse(req.params);
2026-01-09 14:49:44 -08:00
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-03-11 12:41:27 -07:00
const monitorId = validatedParams.monitorId;
2026-01-09 14:49:44 -08:00
2026-01-14 22:44:23 +00:00
const monitor = await this.monitorService.getMonitorById({ teamId, monitorId });
2026-01-09 14:49:44 -08:00
return res.status(200).json({
success: true,
msg: "Monitor retrieved successfully",
data: monitor,
});
} catch (error) {
next(error);
}
};
createMonitor = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedBody = createMonitorBodyValidation.parse(req.body);
2026-01-09 14:49:44 -08:00
2026-03-11 12:57:27 -07:00
const userId = requireUserId(req.user?.id);
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
2026-03-11 12:41:27 -07:00
const monitor = await this.monitorService.createMonitor(teamId, userId, validatedBody);
2026-01-09 14:49:44 -08:00
return res.status(200).json({
success: true,
msg: "Monitor created successfully",
data: monitor,
});
} catch (error) {
next(error);
}
};
2026-02-21 03:43:11 +00:00
importMonitorsFromJSON = async (req: Request, res: Response, next: NextFunction) => {
2026-01-09 14:49:44 -08:00
try {
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
const userId = requireUserId(req.user?.id);
2026-03-11 12:41:27 -07:00
const validatedBody = importMonitorsBodyValidation.parse(req.body);
const monitors = validatedBody.monitors;
2026-02-20 15:11:37 -08:00
2026-02-21 03:43:11 +00:00
const result = await this.monitorService.importMonitorsFromJSON({ teamId, userId, monitors });
2026-01-09 14:49:44 -08:00
return res.status(200).json({
success: true,
2026-02-21 03:43:11 +00:00
msg: `Successfully imported ${result.imported} monitor(s)`,
data: result,
2026-01-09 14:49:44 -08:00
});
} catch (error) {
next(error);
}
};
deleteMonitor = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = getMonitorByIdParamValidation.parse(req.params);
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-03-11 12:41:27 -07:00
const monitorId = validatedParams.monitorId;
2026-01-09 14:49:44 -08:00
const deletedMonitor = await this.monitorService.deleteMonitor({ teamId, monitorId });
return res.status(200).json({
success: true,
msg: "Monitor deleted successfully",
data: deletedMonitor,
});
} catch (error) {
next(error);
}
};
deleteAllMonitors = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
const deletedCount = await this.monitorService.deleteAllMonitors({ teamId });
return res.status(200).json({
success: true,
msg: `Deleted ${deletedCount} monitors`,
});
} catch (error) {
next(error);
}
};
editMonitor = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = getMonitorByIdParamValidation.parse(req.params);
const validatedBody = editMonitorBodyValidation.parse(req.body);
const monitorId = validatedParams.monitorId;
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
2026-03-11 12:41:27 -07:00
const editedMonitor = await this.monitorService.editMonitor({ teamId, monitorId, body: validatedBody });
2026-01-09 14:49:44 -08:00
return res.status(200).json({
success: true,
msg: "Monitor edited successfully",
data: editedMonitor,
});
} catch (error) {
next(error);
}
};
pauseMonitor = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedParams = pauseMonitorParamValidation.parse(req.params);
2026-01-09 14:49:44 -08:00
2026-03-11 12:41:27 -07:00
const monitorId = validatedParams.monitorId;
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
const monitor = await this.monitorService.pauseMonitor({ teamId, monitorId });
return res.status(200).json({
success: true,
msg: monitor.isActive ? "Monitor resumed successfully" : "Monitor paused successfully",
data: monitor,
});
} catch (error) {
next(error);
}
};
bulkPauseMonitors = async (req: Request, res: Response, next: NextFunction) => {
try {
const validatedBody = bulkPauseMonitorBodyValidation.parse(req.body);
const { monitorIds, pause } = validatedBody;
const teamId = requireTeamId(req.user?.teamId);
const { monitors, failedCount } = await this.monitorService.bulkPauseMonitors({ teamId, monitorIds, pause });
const action = pause ? "paused" : "resumed";
const monitorStr = monitors.length === 1 ? "monitor" : "monitors";
let msg = `${monitors.length} ${monitorStr} ${action} successfully`;
if (failedCount > 0) {
msg = `${monitors.length} ${monitorStr} ${action} in database, but ${failedCount} failed to sync with the job queue. Please check logs.`;
}
return res.status(200).json({
success: true,
msg,
data: { monitors, failedCount },
});
} catch (error) {
next(error);
}
};
2026-01-09 14:49:44 -08:00
addDemoMonitors = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:57:27 -07:00
const id = requireUserId(req.user?.id);
const teamId = requireTeamId(req.user?.teamId);
2026-01-16 22:41:12 +00:00
const demoMonitors = await this.monitorService.addDemoMonitors({ userId: id, teamId });
2026-01-09 14:49:44 -08:00
return res.status(200).json({
success: true,
msg: "Demo monitors added successfully",
data: demoMonitors?.length ?? 0,
});
} catch (error) {
next(error);
}
};
getMonitorsByTeamId = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedQuery = getMonitorsByTeamIdQueryValidation.parse(req.query);
2026-01-09 14:49:44 -08:00
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-03-11 12:41:27 -07:00
const type = validatedQuery.type;
2026-05-19 14:45:29 -07:00
const tags = validatedQuery.tags;
2026-03-11 12:41:27 -07:00
const filter = validatedQuery.filter;
2026-01-09 14:49:44 -08:00
2026-05-19 14:45:29 -07:00
const monitors = await this.monitorService.getMonitorsByTeamId({ teamId, type, tags, filter });
2026-01-09 14:49:44 -08:00
return res.status(200).json({
success: true,
msg: "Monitors retrieved successfully",
data: monitors,
});
} catch (error) {
next(error);
}
};
getMonitorsWithChecksByTeamId = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const validatedQuery = getMonitorsWithChecksQueryValidation.parse(req.query);
const limit = validatedQuery.limit;
const page = validatedQuery.page;
const rowsPerPage = validatedQuery.rowsPerPage;
const filter = validatedQuery.filter;
const field = validatedQuery.field;
const order = validatedQuery.order;
const type = validatedQuery.type;
2026-05-19 14:45:29 -07:00
const tags = validatedQuery.tags;
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
const monitors = await this.monitorService.getMonitorsWithChecksByTeamId({
teamId,
limit,
type,
2026-05-19 14:45:29 -07:00
tags,
2026-01-09 14:49:44 -08:00
page,
rowsPerPage,
filter,
field,
order,
});
return res.status(200).json({
msg: "Monitors retrieved successfully",
data: monitors,
});
} catch (error) {
next(error);
}
};
exportMonitorsToJSON = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:57:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
const json = await this.monitorService.exportMonitorsToJSON({ teamId });
return res.status(200).json({
success: true,
msg: "Monitors exported successfully",
data: json,
});
} catch (error) {
next(error);
}
};
getAllGames = async (req: Request, res: Response, next: NextFunction) => {
try {
const games = this.monitorService.getAllGames();
return res.status(200).json({
success: true,
msg: "Supported games retrieved successfully",
data: games,
});
} catch (error) {
next(error);
}
};
getGroupsByTeamId = async (req: Request, res: Response, next: NextFunction) => {
try {
2026-03-11 12:41:27 -07:00
const teamId = requireTeamId(req.user?.teamId);
2026-01-09 14:49:44 -08:00
const groups = await this.monitorService.getGroupsByTeamId({ teamId });
return res.status(200).json({
msg: "Groups retrieved successfully",
data: groups,
});
} catch (error) {
next(error);
}
};
updateNotifications = async (req: Request, res: Response, next: NextFunction) => {
try {
updateNotificationsValidation.parse(req.body);
const teamId = requireTeamId(req.user?.teamId);
const { monitorIds, notificationIds, action } = req.body;
// Verify all requested notification IDs actually belong to this team
const teamNotifications = await this.notificationsService.findNotificationsByTeamId(teamId);
const validNotificationIds = teamNotifications.map((n) => n.id);
2026-03-13 21:40:28 +05:30
const invalidIds = notificationIds.filter((id: string) => !validNotificationIds.includes(id));
if (invalidIds.length > 0) {
2026-03-13 21:40:28 +05:30
throw new AppError({
message: `The following notification IDs are invalid or do not belong to your team: ${invalidIds.join(", ")}`,
status: 403,
});
}
const modifiedCount = await this.monitorService.updateNotifications({
teamId,
monitorIds,
notificationIds,
2026-03-13 21:40:28 +05:30
action,
});
return res.status(200).json({
success: true,
msg: `Notifications updated successfully on ${modifiedCount} monitor(s)`,
data: { modifiedCount },
});
} catch (error) {
next(error);
}
};
2026-01-09 14:49:44 -08:00
}
export default MonitorController;