From e7ba3cb5f9bc066a0cd24a776381e1eed7cb202a Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 24 Feb 2026 23:20:36 +0000 Subject: [PATCH] checks route, checks table --- .../Uptime/Details/Components/ChecksTable.tsx | 2 +- .../Details/Components/GeoChecksTable.tsx | 110 ++++++++++++++++++ client/src/Pages/Uptime/Details/index.tsx | 62 ++++++++-- client/src/Types/GeoCheck.ts | 5 + client/src/locales/en.json | 7 +- server/src/config/controllers.ts | 3 + server/src/config/routes.ts | 3 + server/src/config/services.ts | 9 +- server/src/controllers/geoCheckController.ts | 41 +++++++ .../geo-checks/IGeoChecksRepository.ts | 13 +++ .../geo-checks/MongoGeoChecksRepository.ts | 97 ++++++++++++++- server/src/routes/geoCheckRoutes.ts | 22 ++++ .../src/service/business/geoChecksService.ts | 55 ++++++++- server/src/service/business/monitorService.ts | 9 ++ server/src/validation/joi.ts | 1 + 15 files changed, 420 insertions(+), 19 deletions(-) create mode 100644 client/src/Pages/Uptime/Details/Components/GeoChecksTable.tsx create mode 100644 server/src/controllers/geoCheckController.ts create mode 100644 server/src/routes/geoCheckRoutes.ts diff --git a/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx b/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx index 26905e7cc..0aaede394 100644 --- a/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx +++ b/client/src/Pages/Uptime/Details/Components/ChecksTable.tsx @@ -20,7 +20,7 @@ const getHeaders = (t: Function, uiTimezone: string) => { }, { id: "date", - content: t("pages.checks.table.headers.dateTime"), + content: t("common.table.headers.dateTime"), render: (row) => { return formatDateWithTz(row.createdAt, "ddd, MMMM D, YYYY, HH:mm A", uiTimezone); }, diff --git a/client/src/Pages/Uptime/Details/Components/GeoChecksTable.tsx b/client/src/Pages/Uptime/Details/Components/GeoChecksTable.tsx new file mode 100644 index 000000000..eec7dbc4a --- /dev/null +++ b/client/src/Pages/Uptime/Details/Components/GeoChecksTable.tsx @@ -0,0 +1,110 @@ +import { Table, Pagination, StatusLabel } from "@/Components/design-elements"; +import Box from "@mui/material/Box"; +import type { Header } from "@/Components/design-elements"; +import type { GeoCheck } from "@/Types/GeoCheck"; +import { useTranslation } from "react-i18next"; +import { formatDateWithTz } from "@/Utils/TimeUtils"; +import type { RootState } from "@/Types/state"; +import { useSelector } from "react-redux"; +import prettyMilliseconds from "pretty-ms"; + +const getHeaders = (t: Function, uiTimezone: string) => { + const headers: Header[] = [ + { + id: "status", + content: t("common.table.headers.status"), + render: (row) => { + const firstResult = row.results[0]; + const status = firstResult?.status ? "up" : "down"; + return ; + }, + }, + { + id: "date", + content: t("common.table.headers.dateTime"), + render: (row) => { + return formatDateWithTz(row.createdAt, "ddd, MMMM D, YYYY, HH:mm A", uiTimezone); + }, + }, + { + id: "statusCode", + content: t("pages.checks.table.headers.statusCode"), + render: (row) => { + const firstResult = row.results[0]; + return firstResult?.statusCode || "N/A"; + }, + }, + { + id: "location", + content: t("pages.checks.table.headers.location"), + render: (row) => { + const firstResult = row.results[0]; + if (!firstResult) return "N/A"; + const { continent, country, city } = firstResult.location; + return `${continent} - ${country}, ${city}`; + }, + }, + { + id: "responseTime", + content: t("common.table.headers.responseTime"), + render: (row) => { + const firstResult = row.results[0]; + if (!firstResult?.timings?.total) return "N/A"; + return prettyMilliseconds(firstResult.timings.total, { compact: true }); + }, + }, + ]; + return headers; +}; + +export const GeoChecksTable = ({ + geoChecks, + count, + page, + setPage, + rowsPerPage, + setRowsPerPage, +}: { + geoChecks: GeoCheck[]; + count: number; + page: number; + setPage: (page: number) => void; + rowsPerPage: number; + setRowsPerPage: (rowsPerPage: number) => void; +}) => { + const { t } = useTranslation(); + const uiTimezone = useSelector((state: RootState) => state.ui.timezone); + const headers = getHeaders(t, uiTimezone); + + const handlePageChange = ( + _e: React.MouseEvent | null, + newPage: number + ) => { + setPage(newPage); + }; + + const handleRowsPerPageChange = ( + e: React.ChangeEvent + ) => { + const value = Number(e.target.value); + setPage(0); + setRowsPerPage(value); + }; + + return ( + + + + + ); +}; diff --git a/client/src/Pages/Uptime/Details/index.tsx b/client/src/Pages/Uptime/Details/index.tsx index 5f6d347b1..45cf2e9b9 100644 --- a/client/src/Pages/Uptime/Details/index.tsx +++ b/client/src/Pages/Uptime/Details/index.tsx @@ -10,6 +10,7 @@ import { } from "@/Components/monitors"; import { TrendingUp, AlertTriangle } from "lucide-react"; import { ChecksTable } from "@/Pages/Uptime/Details/Components/ChecksTable"; +import { GeoChecksTable } from "@/Pages/Uptime/Details/Components/GeoChecksTable"; import { MonitorStatBoxes } from "@/Components/monitors"; import { useTheme } from "@mui/material/styles"; @@ -20,7 +21,7 @@ import { useSelector } from "react-redux"; import { useGet } from "@/Hooks/UseApi"; import type { MonitorDetailsResponse } from "@/Types/Monitor"; import type { ChecksResponse } from "@/Types/Check"; -import type { GeoChecksResult, GeoContinent } from "@/Types/GeoCheck"; +import type { GeoChecksResult, GeoChecksResponse, GeoContinent } from "@/Types/GeoCheck"; import type { RootState } from "@/Types/state"; import { formatDateWithTz } from "@/Utils/TimeUtils"; import { t } from "i18next"; @@ -40,6 +41,8 @@ const UptimeDetailsPage = () => { const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(5); + const [geoPage, setGeoPage] = useState(0); + const [geoRowsPerPage, setGeoRowsPerPage] = useState(5); const [dateRange, setDateRange] = useState("recent"); const [selectedLocation, setSelectedLocation] = useState("NA"); @@ -113,7 +116,6 @@ const UptimeDetailsPage = () => { { keepPreviousData: true, revalidateOnFocus: false } ); - // Geo checks fetch - only for HTTP monitors with geo checks enabled const geoChecksUrl = useMemo(() => { if (!monitorId || monitor?.type !== "http" || !monitor?.geoCheckEnabled) { return null; @@ -124,14 +126,46 @@ const UptimeDetailsPage = () => { return `/monitors/${monitorId}/geo-checks?${params.toString()}`; }, [monitorId, monitor?.type, monitor?.geoCheckEnabled, dateRange, selectedLocation]); - const { data: _geoChecksData, isLoading: _geoChecksIsLoading } = - useGet( - geoChecksUrl, - {}, - { keepPreviousData: true, revalidateOnFocus: false } - ); + const { data: geoGroupedData } = useGet( + geoChecksUrl, + {}, + { keepPreviousData: true, revalidateOnFocus: false } + ); - const geoChecks = _geoChecksData?.groupedGeoChecks ?? []; + const geoGroupedChecks = geoGroupedData?.groupedGeoChecks ?? []; + + // Fetch paginated geo checks for the table + const geoChecksTableUrl = useMemo(() => { + if (!monitorId || monitor?.type !== "http" || !monitor?.geoCheckEnabled) { + return null; + } + const params = new URLSearchParams(); + params.append("sortOrder", "desc"); + params.append("dateRange", dateRange); + params.append("page", String(geoPage)); + params.append("rowsPerPage", String(geoRowsPerPage)); + if (selectedLocation) { + params.append("continent", selectedLocation); + } + return `/geo-checks/${monitorId}?${params.toString()}`; + }, [ + monitorId, + monitor?.type, + monitor?.geoCheckEnabled, + dateRange, + geoPage, + geoRowsPerPage, + selectedLocation, + ]); + + const { data: geoChecksTableData } = useGet( + geoChecksTableUrl, + {}, + { keepPreviousData: true, revalidateOnFocus: false } + ); + + const geoChecksForTable = geoChecksTableData?.geoChecks ?? []; + const geoChecksCount = geoChecksTableData?.geoChecksCount ?? 0; // Extract unique locations from geo checks data const geoLocations = monitor?.geoCheckLocations; @@ -203,9 +237,17 @@ const UptimeDetailsPage = () => { onLocationChange={setSelectedLocation} /> + )} diff --git a/client/src/Types/GeoCheck.ts b/client/src/Types/GeoCheck.ts index a90dea58f..af625ec5b 100644 --- a/client/src/Types/GeoCheck.ts +++ b/client/src/Types/GeoCheck.ts @@ -55,3 +55,8 @@ export interface GeoChecksResult { monitorType: string; groupedGeoChecks: GroupedGeoCheck[]; } + +export interface GeoChecksResponse { + geoChecks: GeoCheck[]; + geoChecksCount: number; +} diff --git a/client/src/locales/en.json b/client/src/locales/en.json index dff21384e..c60869c7d 100644 --- a/client/src/locales/en.json +++ b/client/src/locales/en.json @@ -115,7 +115,8 @@ "type": "Type", "url": "Url", "interval": "Interval", - "active": "Active" + "active": "Active", + "responseTime": "Response time" } } }, @@ -372,8 +373,8 @@ "table": { "empty": "No down checks in this time range", "headers": { - "dateTime": "Date & time", - "statusCode": "Status code" + "statusCode": "Status code", + "location": "Location" } } }, diff --git a/server/src/config/controllers.ts b/server/src/config/controllers.ts index 8a83a0f63..98e6ded8e 100644 --- a/server/src/config/controllers.ts +++ b/server/src/config/controllers.ts @@ -2,6 +2,7 @@ import MonitorController from "../controllers/monitorController.js"; import AuthController from "../controllers/authController.js"; import SettingsController from "../controllers/settingsController.js"; import CheckController from "../controllers/checkController.js"; +import GeoCheckController from "../controllers/geoCheckController.js"; import InviteController from "../controllers/inviteController.js"; import MaintenanceWindowController from "../controllers/maintenanceWindowController.js"; import QueueController from "../controllers/queueController.js"; @@ -17,6 +18,7 @@ export interface InitializedControllers { monitorController: MonitorController; settingsController: SettingsController; checkController: CheckController; + geoCheckController: GeoCheckController; inviteController: InviteController; maintenanceWindowController: MaintenanceWindowController; queueController: QueueController; @@ -32,6 +34,7 @@ export const initializeControllers = (services: InitializedServices): Initialize monitorController: new MonitorController(services.monitorService), settingsController: new SettingsController(services.settingsService, services.emailService), checkController: new CheckController(services.checkService), + geoCheckController: new GeoCheckController(services.geoChecksService), inviteController: new InviteController(services.inviteService), maintenanceWindowController: new MaintenanceWindowController(services.maintenanceWindowService), queueController: new QueueController(services.jobQueue), diff --git a/server/src/config/routes.ts b/server/src/config/routes.ts index 8707009a8..672866dc1 100644 --- a/server/src/config/routes.ts +++ b/server/src/config/routes.ts @@ -6,6 +6,7 @@ import AuthRoutes from "../routes/authRoute.js"; import InviteRoutes from "../routes/inviteRoute.js"; import MonitorRoutes from "../routes/monitorRoute.js"; import CheckRoutes from "../routes/checkRoute.js"; +import GeoCheckRoutes from "../routes/geoCheckRoutes.js"; import SettingsRoutes from "../routes/settingsRoute.js"; import MaintenanceWindowRoutes from "../routes/maintenanceWindowRoute.js"; import StatusPageRoutes from "../routes/statusPageRoute.js"; @@ -22,6 +23,7 @@ export const setupRoutes = (app: any, controllers: Record, services const monitorRoutes = new MonitorRoutes(controllers.monitorController); const settingsRoutes = new SettingsRoutes(controllers.settingsController); const checkRoutes = new CheckRoutes(controllers.checkController); + const geoCheckRoutes = new GeoCheckRoutes(controllers.geoCheckController); const inviteRoutes = new InviteRoutes(controllers.inviteController, verifyJWT); const maintenanceWindowRoutes = new MaintenanceWindowRoutes(controllers.maintenanceWindowController); const queueRoutes = new QueueRoutes(controllers.queueController); @@ -36,6 +38,7 @@ export const setupRoutes = (app: any, controllers: Record, services app.use("/api/v1/monitors", verifyJWT, monitorRoutes.getRouter()); app.use("/api/v1/settings", verifyJWT, settingsRoutes.getRouter()); app.use("/api/v1/checks", verifyJWT, checkRoutes.getRouter()); + app.use("/api/v1/geo-checks", verifyJWT, geoCheckRoutes.getRouter()); app.use("/api/v1/invite", inviteRoutes.getRouter()); app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRoutes.getRouter()); app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter()); diff --git a/server/src/config/services.ts b/server/src/config/services.ts index db6016b04..7cc908d0b 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -154,7 +154,12 @@ export const initializeServices = async ({ const globalPingService = new GlobalPingService(logger); - const geoChecksService = new GeoChecksService(logger, geoChecksRepository, globalPingService); + const geoChecksService = new GeoChecksService({ + logger, + geoChecksRepository, + globalPingService, + monitorsRepository, + }); const bufferService = new BufferService(logger, checkService, geoChecksService, settingsService); @@ -252,12 +257,12 @@ export const initializeServices = async ({ jobQueue: superSimpleQueue, userService, checkService, + geoChecksService, diagnosticService, inviteService, maintenanceWindowService, monitorService, incidentService, - geoChecksService, logger, notificationsService, statusPageService, diff --git a/server/src/controllers/geoCheckController.ts b/server/src/controllers/geoCheckController.ts new file mode 100644 index 000000000..ec9d489f1 --- /dev/null +++ b/server/src/controllers/geoCheckController.ts @@ -0,0 +1,41 @@ +import { Request, Response, NextFunction } from "express"; +import { getChecksParamValidation, getChecksQueryValidation } from "@/validation/joi.js"; +import type { IGeoChecksService } from "@/service/business/geoChecksService.js"; + +const SERVICE_NAME = "geoCheckController"; + +class GeoCheckController { + static SERVICE_NAME = SERVICE_NAME; + + private geoChecksService: IGeoChecksService; + constructor(geoChecksService: IGeoChecksService) { + this.geoChecksService = geoChecksService; + } + + get serviceName() { + return GeoCheckController.SERVICE_NAME; + } + + getGeoChecksByMonitor = async (req: Request, res: Response, next: NextFunction) => { + try { + await getChecksParamValidation.validateAsync(req.params); + await getChecksQueryValidation.validateAsync(req.query); + + const result = await this.geoChecksService.getGeoChecksByMonitor({ + monitorId: req?.params?.monitorId as string, + query: req?.query, + teamId: req?.user?.teamId as string, + }); + + return res.status(200).json({ + success: true, + msg: "Geo checks retrieved successfully", + data: result, + }); + } catch (error) { + next(error); + } + }; +} + +export default GeoCheckController; diff --git a/server/src/repositories/geo-checks/IGeoChecksRepository.ts b/server/src/repositories/geo-checks/IGeoChecksRepository.ts index 31b2dd2a5..69c9c68b6 100644 --- a/server/src/repositories/geo-checks/IGeoChecksRepository.ts +++ b/server/src/repositories/geo-checks/IGeoChecksRepository.ts @@ -1,8 +1,21 @@ import type { GeoCheck, GroupedGeoCheck } from "@/types/geoCheck.js"; import type { GeoContinent } from "@/types/geoCheck.js"; +export interface GeoChecksQueryResult { + geoChecksCount: number; + geoChecks: GeoCheck[]; +} + export interface IGeoChecksRepository { createGeoChecks(geoChecks: Omit[]): Promise; + findByMonitorId( + monitorId: string, + sortOrder: string, + dateRange: string, + page: number, + rowsPerPage: number, + continent?: GeoContinent + ): Promise; findByMonitorIdAndDateRange(monitorId: string, startDate: Date, endDate: Date): Promise; findGroupedByMonitorIdAndDateRange( monitorId: string, diff --git a/server/src/repositories/geo-checks/MongoGeoChecksRepository.ts b/server/src/repositories/geo-checks/MongoGeoChecksRepository.ts index b40dc2cf8..6f7f4d064 100644 --- a/server/src/repositories/geo-checks/MongoGeoChecksRepository.ts +++ b/server/src/repositories/geo-checks/MongoGeoChecksRepository.ts @@ -1,10 +1,18 @@ import { IGeoChecksRepository } from "./IGeoChecksRepository.js"; -import type { GeoCheck, GeoCheckMetadata, GeoCheckResult, GroupedGeoCheck } from "@/types/geoCheck.js"; +import type { GeoCheck, GeoCheckMetadata, GeoCheckResult, GroupedGeoCheck, GeoContinent } from "@/types/geoCheck.js"; +import type { GeoChecksQueryResult } from "./IGeoChecksRepository.js"; import { GeoCheckModel, type GeoCheckDocument } from "@/db/models/index.js"; import mongoose from "mongoose"; const SERVICE_NAME = "GeoChecksRepository"; +const dateRangeLookup: Record = { + recent: new Date(Date.now() - 60 * 60 * 1000), + day: new Date(Date.now() - 24 * 60 * 60 * 1000), + week: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), + month: new Date(Date.now() - 30 * 24 * 60 * 60 * 1000), +}; + class MongoGeoChecksRepository implements IGeoChecksRepository { static SERVICE_NAME = SERVICE_NAME; @@ -97,6 +105,93 @@ class MongoGeoChecksRepository implements IGeoChecksRepository { } }; + findByMonitorId = async ( + monitorId: string, + sortOrder: string, + dateRange: string, + page: number, + rowsPerPage: number, + continent?: GeoContinent + ): Promise => { + try { + const matchStage: Record = { + "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), + ...(dateRangeLookup[dateRange] && { + createdAt: { + $gte: dateRangeLookup[dateRange], + }, + }), + }; + + const convertedSortOrder = sortOrder === "asc" ? 1 : -1; + + let skip = 0; + if (page && rowsPerPage) { + skip = page * rowsPerPage; + } + + if (continent) { + const pipeline: any[] = [ + { $match: matchStage }, + { $unwind: "$results" }, + { + $match: { + "results.location.continent": continent, + }, + }, + { + $group: { + _id: "$_id", + doc: { $first: "$$ROOT" }, + results: { $push: "$results" }, + }, + }, + { + $replaceRoot: { + newRoot: { + $mergeObjects: ["$doc", { results: "$results" }], + }, + }, + }, + { $sort: { createdAt: convertedSortOrder } }, + { $skip: skip }, + { $limit: rowsPerPage }, + ]; + + const [countPipeline, dataResults] = await Promise.all([ + GeoCheckModel.aggregate([ + { $match: matchStage }, + { $unwind: "$results" }, + { $match: { "results.location.continent": continent } }, + { $group: { _id: "$_id" } }, + { $count: "count" }, + ]), + GeoCheckModel.aggregate(pipeline), + ]); + + const geoChecksCount = countPipeline[0]?.count || 0; + const geoChecks = dataResults.map(this.toEntity); + + return { geoChecksCount, geoChecks }; + } else { + const [geoChecksCount, docs] = await Promise.all([ + GeoCheckModel.countDocuments(matchStage), + GeoCheckModel.find(matchStage).sort({ createdAt: convertedSortOrder }).skip(skip).limit(rowsPerPage).lean() as Promise, + ]); + + return { geoChecksCount, geoChecks: docs.map(this.toEntity) }; + } + } catch (error: any) { + this.logger.error({ + message: `Error finding geo checks by monitor ID: ${error.message}`, + service: SERVICE_NAME, + method: "findByMonitorId", + stack: error.stack, + }); + throw error; + } + }; + findByMonitorIdAndDateRange = async (monitorId: string, startDate: Date, endDate: Date): Promise => { try { const docs = await GeoCheckModel.find({ diff --git a/server/src/routes/geoCheckRoutes.ts b/server/src/routes/geoCheckRoutes.ts new file mode 100644 index 000000000..4c34df56e --- /dev/null +++ b/server/src/routes/geoCheckRoutes.ts @@ -0,0 +1,22 @@ +import { Router } from "express"; + +class GeoCheckRoutes { + private router: Router; + private geoCheckController: any; + + constructor(geoCheckController: any) { + this.router = Router(); + this.geoCheckController = geoCheckController; + this.initRoutes(); + } + + initRoutes() { + this.router.get("/:monitorId", this.geoCheckController.getGeoChecksByMonitor); + } + + getRouter() { + return this.router; + } +} + +export default GeoCheckRoutes; diff --git a/server/src/service/business/geoChecksService.ts b/server/src/service/business/geoChecksService.ts index a40ce82c7..e104b9ea4 100644 --- a/server/src/service/business/geoChecksService.ts +++ b/server/src/service/business/geoChecksService.ts @@ -2,9 +2,10 @@ import type { Monitor, GeoCheck } from "@/types/index.js"; import type { GeoCheckResult } from "@/types/geoCheck.js"; import { Types } from "mongoose"; import type { IGeoChecksRepository } from "@/repositories/index.js"; +import type { IMonitorsRepository } from "@/repositories/index.js"; import type { IGlobalPingService } from "@/service/infrastructure/globalPingService.js"; import type { ILogger } from "@/utils/logger.js"; -import type { ISettingsService } from "@/service/system/settingsService.js"; +import { AppError } from "@/utils/AppError.js"; const SERVICE_NAME = "GeoChecksService"; @@ -12,6 +13,7 @@ export interface IGeoChecksService { readonly serviceName: string; buildGeoCheck(monitor: Monitor): Promise; createGeoChecks(geoChecks: GeoCheck[]): Promise; + getGeoChecksByMonitor(args: { monitorId: string; query: any; teamId: string }): Promise; } class GeoChecksService implements IGeoChecksService { @@ -20,11 +22,23 @@ class GeoChecksService implements IGeoChecksService { private logger: ILogger; private geoChecksRepository: IGeoChecksRepository; private globalPingService: IGlobalPingService; + private monitorsRepository: IMonitorsRepository; - constructor(logger: ILogger, geoChecksRepository: IGeoChecksRepository, globalPingService: IGlobalPingService) { + constructor({ + logger, + geoChecksRepository, + globalPingService, + monitorsRepository, + }: { + logger: ILogger; + geoChecksRepository: IGeoChecksRepository; + globalPingService: IGlobalPingService; + monitorsRepository: IMonitorsRepository; + }) { this.logger = logger; this.geoChecksRepository = geoChecksRepository; this.globalPingService = globalPingService; + this.monitorsRepository = monitorsRepository; } get serviceName() { @@ -133,6 +147,43 @@ class GeoChecksService implements IGeoChecksService { createGeoChecks = async (geoChecks: GeoCheck[]) => { return this.geoChecksRepository.createGeoChecks(geoChecks); }; + + getGeoChecksByMonitor = async ({ monitorId, query, teamId }: { monitorId: string; query: any; teamId: string }) => { + if (!monitorId) { + throw new AppError({ + message: "No monitor ID in request", + service: SERVICE_NAME, + method: "getGeoChecksByMonitor", + status: 400, + }); + } + if (!teamId) { + throw new AppError({ + message: "No team ID in request", + service: SERVICE_NAME, + method: "getGeoChecksByMonitor", + status: 400, + }); + } + + const monitor = await this.monitorsRepository.findById(monitorId, teamId); + if (!monitor) { + throw new AppError({ + message: `Monitor with ID ${monitorId} not found.`, + service: SERVICE_NAME, + method: "getGeoChecksByMonitor", + status: 404, + }); + } + + let { sortOrder, dateRange, page, rowsPerPage, continent } = query; + const parsedPage = page ? parseInt(page) : page; + const parsedRowsPerPage = rowsPerPage ? parseInt(rowsPerPage) : rowsPerPage; + + const result = await this.geoChecksRepository.findByMonitorId(monitorId, sortOrder, dateRange, parsedPage, parsedRowsPerPage, continent); + + return result; + }; } export default GeoChecksService; diff --git a/server/src/service/business/monitorService.ts b/server/src/service/business/monitorService.ts index 2a1e9d0ed..7006d7bc8 100644 --- a/server/src/service/business/monitorService.ts +++ b/server/src/service/business/monitorService.ts @@ -479,6 +479,14 @@ export class MonitorService implements IMonitorService { }); }); + await this.geoChecksRepository.deleteByMonitorId(monitor.id).catch((err: any) => { + this.logger.warn({ + message: `Error deleting geo checks for monitor ${monitor.id} with name ${monitor.name}`, + service: SERVICE_NAME, + stack: err.stack, + }); + }); + await this.jobQueue.deleteJob(monitor); return monitor; }; @@ -490,6 +498,7 @@ export class MonitorService implements IMonitorService { try { await this.jobQueue.deleteJob(monitor); await this.checksRepository.deleteByMonitorId(monitor.id); + await this.geoChecksRepository.deleteByMonitorId(monitor.id); await this.statusPagesRepository.removeMonitorFromStatusPages(monitor.id); await this.monitorStatsRepository.deleteByMonitorId(monitor.id); } catch (error: any) { diff --git a/server/src/validation/joi.ts b/server/src/validation/joi.ts index 34283f461..26ba7b26a 100755 --- a/server/src/validation/joi.ts +++ b/server/src/validation/joi.ts @@ -324,6 +324,7 @@ const getChecksQueryValidation = joi.object({ page: joi.number(), rowsPerPage: joi.number(), status: joi.boolean(), + continent: joi.string().valid(...GeoContinents), }); const getTeamChecksQueryValidation = joi.object({