From aae1fefb314a6c7d6857e75d401d0561568cbf9b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 8 Oct 2025 13:45:07 -0700 Subject: [PATCH] add checks endpoint --- server/nodemon.json | 4 +- server/src/config/controllers.js | 2 +- .../src/controllers/v2/MonitorController.ts | 134 +++++++++++------- server/src/routes/v2/monitors.ts | 6 +- .../src/service/v2/business/CheckService.ts | 12 ++ 5 files changed, 103 insertions(+), 55 deletions(-) diff --git a/server/nodemon.json b/server/nodemon.json index 88d6ac686..389209fa7 100755 --- a/server/nodemon.json +++ b/server/nodemon.json @@ -1,5 +1,5 @@ { "ignore": ["src/locales/*", "*.log", "node_modules/*"], - "watch": ["src/**/*.js", "*.json"], - "ext": "js,json" + "watch": ["src/**/*.ts", "src/**/*.js", "*.json"], + "ext": "ts,js,json" } diff --git a/server/src/config/controllers.js b/server/src/config/controllers.js index 78cd7091b..927acd1a2 100644 --- a/server/src/config/controllers.js +++ b/server/src/config/controllers.js @@ -74,7 +74,7 @@ export const initializeControllers = (services) => { controllers.authControllerV2 = new AuthControllerV2(services.authServiceV2, services.inviteServiceV2); controllers.inviteControllerV2 = new InviteControllerV2(services.inviteServiceV2); controllers.maintenanceControllerV2 = new MaintenanceControllerV2(services.maintenanceServiceV2); - controllers.monitorControllerV2 = new MonitorControllerV2(services.monitorServiceV2); + controllers.monitorControllerV2 = new MonitorControllerV2(services.monitorServiceV2, services.checkServiceV2); controllers.notificationChannelControllerV2 = new NotificationChannelControllerV2(services.notificationChannelServiceV2); controllers.queueControllerV2 = new QueueControllerV2(services.jobQueueV2); diff --git a/server/src/controllers/v2/MonitorController.ts b/server/src/controllers/v2/MonitorController.ts index c9a4ce7da..7c0c63d2c 100644 --- a/server/src/controllers/v2/MonitorController.ts +++ b/server/src/controllers/v2/MonitorController.ts @@ -2,10 +2,13 @@ import { Request, Response, NextFunction } from "express"; import ApiError from "../../utils/ApiError.js"; import MonitorService from "../../service/v2/business/MonitorService.js"; import { MonitorType } from "../../db/v2/models/monitors/Monitor.js"; +import CheckService from "../../service/v2/business/CheckService.js"; class MonitorController { private monitorService: MonitorService; - constructor(monitorService: MonitorService) { + private checkService: CheckService; + constructor(monitorService: MonitorService, checkService: CheckService) { this.monitorService = monitorService; + this.checkService = checkService; } create = async (req: Request, res: Response, next: NextFunction) => { @@ -25,6 +28,86 @@ class MonitorController { } }; + getAll = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + + let monitors; + if (req.query.embedChecks === "true") { + const page = Math.max(1, Number(req.query.page) || 1); + const limit = Math.max(1, Number(req.query.limit) || 10); + const type: MonitorType[] = req.query.type as MonitorType[]; + + monitors = await this.monitorService.getAllEmbedChecks(page, limit, type); + } else { + monitors = await this.monitorService.getAll(); + } + + res.status(200).json({ + message: "Monitors retrieved successfully", + data: monitors, + }); + } catch (error) { + next(error); + } + }; + + getChecks = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + + const id = req.params.id; + if (!id) { + throw new ApiError("Monitor ID is required", 400); + } + + const page = Number(req.query.page); + const rowsPerPage = Number(req.query.rowsPerPage); + + if (isNaN(page)) throw new ApiError("Page query parameter must be a number", 400); + if (isNaN(rowsPerPage)) throw new ApiError("rowsPerPage query parameter must be a number", 400); + + if (page < 0) throw new ApiError("Page must be greater than 0", 400); + if (rowsPerPage < 0) throw new ApiError("rowsPerPage must be greater than 0", 400); + + const { count, checks } = await this.checkService.getChecks(id, page, rowsPerPage); + res.status(200).json({ + message: "Checks retrieved successfully", + data: { count, checks }, + }); + } catch (error) { + next(error); + } + }; + + toggleActive = async (req: Request, res: Response, next: NextFunction) => { + try { + const tokenizedUser = req.user; + if (!tokenizedUser) { + return res.status(401).json({ message: "Unauthorized" }); + } + + const id = req.params.id; + if (!id) { + throw new ApiError("Monitor ID is required", 400); + } + + const monitor = await this.monitorService.toggleActive(id, tokenizedUser); + res.status(200).json({ + message: "Monitor paused/unpaused successfully", + data: monitor, + }); + } catch (error) { + next(error); + } + }; + get = async (req: Request, res: Response, next: NextFunction) => { try { const tokenizedUser = req.user; @@ -62,55 +145,6 @@ class MonitorController { } }; - getAll = async (req: Request, res: Response, next: NextFunction) => { - try { - const tokenizedUser = req.user; - if (!tokenizedUser) { - return res.status(401).json({ message: "Unauthorized" }); - } - - let monitors; - if (req.query.embedChecks === "true") { - const page = Math.max(1, Number(req.query.page) || 1); - const limit = Math.max(1, Number(req.query.limit) || 10); - const type: MonitorType[] = req.query.type as MonitorType[]; - - monitors = await this.monitorService.getAllEmbedChecks(page, limit, type); - } else { - monitors = await this.monitorService.getAll(); - } - - res.status(200).json({ - message: "Monitors retrieved successfully", - data: monitors, - }); - } catch (error) { - next(error); - } - }; - - toggleActive = async (req: Request, res: Response, next: NextFunction) => { - try { - const tokenizedUser = req.user; - if (!tokenizedUser) { - return res.status(401).json({ message: "Unauthorized" }); - } - - const id = req.params.id; - if (!id) { - throw new ApiError("Monitor ID is required", 400); - } - - const monitor = await this.monitorService.toggleActive(id, tokenizedUser); - res.status(200).json({ - message: "Monitor paused/unpaused successfully", - data: monitor, - }); - } catch (error) { - next(error); - } - }; - update = async (req: Request, res: Response, next: NextFunction) => { try { const tokenizedUser = req.user; diff --git a/server/src/routes/v2/monitors.ts b/server/src/routes/v2/monitors.ts index eb218f4c6..61e5b61d6 100644 --- a/server/src/routes/v2/monitors.ts +++ b/server/src/routes/v2/monitors.ts @@ -17,12 +17,14 @@ class MonitorRoutes { this.router.get("/", verifyToken, verifyPermission(["monitors.view"]), this.controller.getAll); + this.router.get("/:id/checks", verifyToken, verifyPermission(["monitors.view"]), this.controller.getChecks); + this.router.patch("/:id/active", verifyToken, verifyPermission(["monitors.update"]), this.controller.toggleActive); - this.router.patch("/:id", verifyToken, verifyPermission(["monitors.update"]), this.controller.update); - this.router.get("/:id", verifyToken, verifyPermission(["monitors.view"]), this.controller.get); + this.router.patch("/:id", verifyToken, verifyPermission(["monitors.update"]), this.controller.update); + this.router.delete("/:id", verifyToken, verifyPermission(["monitors.delete"]), this.controller.delete); }; diff --git a/server/src/service/v2/business/CheckService.ts b/server/src/service/v2/business/CheckService.ts index 7187fb5a3..54593ed2b 100644 --- a/server/src/service/v2/business/CheckService.ts +++ b/server/src/service/v2/business/CheckService.ts @@ -5,6 +5,7 @@ import { MonitorType } from "../../../db/v2/models/monitors/Monitor.js"; import { StatusResponse } from "../infrastructure/NetworkService.js"; import type { ICapturePayload, ILighthousePayload } from "../infrastructure/NetworkService.js"; import mongoose from "mongoose"; +import { stat } from "fs"; const SERVICE_NAME = "CheckServiceV2"; export interface ICheckService { @@ -60,6 +61,7 @@ class CheckService implements ICheckService { monitorId: monitorId, type: statusResponse?.type, status: statusResponse?.status, + httpStatusCode: statusResponse?.code, message: statusResponse?.message, responseTime: statusResponse?.responseTime, timings: statusResponse?.timings, @@ -130,6 +132,16 @@ class CheckService implements ICheckService { return false; } }; + + getChecks = async (monitorId: string, page: number, rowsPerPage: number) => { + const count = await Check.countDocuments({ monitorId: new mongoose.Types.ObjectId(monitorId) }); + const checks = await Check.find({ monitorId: new mongoose.Types.ObjectId(monitorId) }) + .sort({ createdAt: -1 }) + .skip(page * rowsPerPage) + .limit(rowsPerPage) + .exec(); + return { checks, count }; + }; } export default CheckService;