From d0b3bc60dffa9f4f8cb48637b641cca013dc1a40 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 13 Jan 2026 20:20:00 +0000 Subject: [PATCH] create and update --- server/src/config/services.js | 6 +- server/src/controllers/monitorController.ts | 2 +- .../monitors/IMonitorsRepository.ts | 4 + .../monitors/MongoMonitorsRepository.ts | 46 +- .../{monitorService.js => monitorService.ts} | 436 ++++++++++++------ server/src/service/index.ts | 1 + .../SuperSimpleQueue/SuperSimpleQueue.js | 27 +- .../SuperSimpleQueueHelper.js | 4 +- .../service/infrastructure/networkService.js | 12 +- .../service/infrastructure/statusService.js | 32 +- 10 files changed, 384 insertions(+), 186 deletions(-) rename server/src/service/business/{monitorService.js => monitorService.ts} (53%) create mode 100644 server/src/service/index.ts diff --git a/server/src/config/services.js b/server/src/config/services.js index 0916752ab..6878026d6 100644 --- a/server/src/config/services.js +++ b/server/src/config/services.js @@ -16,7 +16,7 @@ import CheckService from "../service/business/checkService.js"; import DiagnosticService from "../service/business/diagnosticService.js"; import InviteService from "../service/business/inviteService.js"; import MaintenanceWindowService from "../service/business/maintenanceWindowService.js"; -import MonitorService from "../service/business/monitorService.js"; +import { MonitorService } from "@/service/index.js"; import IncidentService from "../service/business/incidentService.js"; import papaparse from "papaparse"; import axios from "axios"; @@ -152,7 +152,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService const bufferService = new BufferService({ db, logger, envSettings, incidentService }); - const statusService = new StatusService({ db, logger, buffer: bufferService, incidentService }); + const statusService = new StatusService({ db, logger, buffer: bufferService, incidentService, monitorsRepository }); const notificationUtils = new NotificationUtils({ stringService, @@ -182,6 +182,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService db, logger, helper: superSimpleQueueHelper, + monitorsRepository, }); // Business services @@ -218,7 +219,6 @@ export const initializeServices = async ({ logger, envSettings, settingsService }); const monitorService = new MonitorService({ db, - settingsService, jobQueue: superSimpleQueue, stringService, emailService, diff --git a/server/src/controllers/monitorController.ts b/server/src/controllers/monitorController.ts index 425979062..ba0a237bf 100644 --- a/server/src/controllers/monitorController.ts +++ b/server/src/controllers/monitorController.ts @@ -193,7 +193,7 @@ class MonitorController { const userId = req?.user?._id; const teamId = req?.user?.teamId; - const monitor = await this.monitorService.createMonitor({ teamId, userId, body: req.body }); + const monitor = await this.monitorService.createMonitor(teamId, userId, req.body); return res.status(200).json({ success: true, diff --git a/server/src/repositories/monitors/IMonitorsRepository.ts b/server/src/repositories/monitors/IMonitorsRepository.ts index 865d7f8e4..99131cb4b 100644 --- a/server/src/repositories/monitors/IMonitorsRepository.ts +++ b/server/src/repositories/monitors/IMonitorsRepository.ts @@ -12,11 +12,15 @@ export interface TeamQueryConfig { export interface IMonitorsRepository { // create + create(monitor: Monitor, teamId: string, userId: string): Promise; // single fetch + findById(monitorId: string): Promise; + // collection fetch findAll(): Promise; findByTeamId(teamId: string, config: TeamQueryConfig): Promise; // update + update(monitorId: string, updates: Partial): Promise; // delete // counts diff --git a/server/src/repositories/monitors/MongoMonitorsRepository.ts b/server/src/repositories/monitors/MongoMonitorsRepository.ts index dd85f9a3d..49687c339 100644 --- a/server/src/repositories/monitors/MongoMonitorsRepository.ts +++ b/server/src/repositories/monitors/MongoMonitorsRepository.ts @@ -3,11 +3,31 @@ import type { MonitorDocument } from "@/db/models/Monitor.js"; import type { Monitor, MonitorType } from "@/types/monitor.js"; import mongoose, { type FilterQuery } from "mongoose"; import type { IMonitorsRepository, TeamQueryConfig } from "./IMonitorsRepository.js"; +import { AppError } from "@/utils/AppError.js"; class MongoMonitorsRepository implements IMonitorsRepository { - findAll = async (): Promise => { - const documents = await MonitorModel.find().exec(); - return this.mapDocuments(documents); + create = async (monitor: Monitor, teamId: string, userId: string) => { + const monitorModel = new MonitorModel({ ...monitor, teamId, userId }); + const saved = await monitorModel.save(); + return this.toEntity(saved); + }; + + findById = async (MonitorModelId: string): Promise => { + const monitor = await MonitorModel.findById(MonitorModelId); + if (!monitor) { + if (monitor === null || monitor === undefined) { + throw new AppError({ + message: `Monitor with ID ${MonitorModelId} not found.`, + status: 404, + }); + } + } + return this.toEntity(monitor); + }; + + findAll = async (): Promise => { + const monitors = await MonitorModel.find(); + return this.mapDocuments(monitors); }; findByTeamId = async (teamId: string, config: TeamQueryConfig): Promise => { @@ -64,9 +84,25 @@ class MongoMonitorsRepository implements IMonitorsRepository { return count; }; - private mapDocuments = (documents: MonitorDocument[]): Monitor[] | null => { + update = async (monitorId: string, patch: Partial) => { + const updatedMonitor = await MonitorModel.findOneAndUpdate( + { _id: monitorId }, + { + $set: { + ...patch, + }, + }, + { new: true, runValidators: true } + ); + if (!updatedMonitor) { + throw new AppError({ message: `Failed to update monitor with id ${monitorId}`, status: 500 }); + } + return this.toEntity(updatedMonitor); + }; + + private mapDocuments = (documents: MonitorDocument[]): Monitor[] => { if (!documents?.length) { - return null; + return []; } return documents.map((doc) => this.toEntity(doc)); }; diff --git a/server/src/service/business/monitorService.js b/server/src/service/business/monitorService.ts similarity index 53% rename from server/src/service/business/monitorService.js rename to server/src/service/business/monitorService.ts index 4bdc45eb4..45a2fc235 100644 --- a/server/src/service/business/monitorService.js +++ b/server/src/service/business/monitorService.ts @@ -1,13 +1,88 @@ import { createMonitorsBodyValidation } from "@/validation/joi.js"; import { NormalizeData } from "@/utils/dataUtils.js"; - +import { type Monitor } from "@/types/index.js"; +import type { IMonitorsRepository } from "@/repositories/index.js"; +import { AppError } from "../infrastructure/errorService.js"; const SERVICE_NAME = "MonitorService"; -class MonitorService { + +export interface IMonitorService { + readonly serviceName: string; + verifyTeamAccess(args: { teamId: string; monitorId: string }): Promise; + + // create + createMonitor(teamId: string, userId: string, body: Monitor): Promise; + createBulkMonitors(args: { fileData: string; userId: string; teamId: string }): Promise; + addDemoMonitors(args: { userId: string; teamId: string }): Promise; + + // read + getAllMonitors(): Promise; + getUptimeDetailsById(args: { teamId: string; monitorId: string; dateRange: string; normalize?: boolean }): Promise; + getMonitorStatsById(args: { + teamId: string; + monitorId: string; + limit?: number; + sortOrder?: 1 | -1; + dateRange?: string; + numToDisplay?: number; + normalize?: boolean; + }): Promise; + getHardwareDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise; + getMonitorById(args: { teamId: string; monitorId: string }): Promise; + getMonitorsByTeamId(args: { + teamId: string; + limit?: number; + type?: string | string[]; + page?: number; + rowsPerPage?: number; + filter?: string; + field?: string; + order?: "asc" | "desc"; + }): Promise; + getMonitorsAndSummaryByTeamId(args: { teamId: string; type?: string | string[]; explain?: boolean }): Promise; + getMonitorsWithChecksByTeamId(args: { + teamId: string; + limit?: number; + type?: string | string[]; + page?: number; + rowsPerPage?: number; + filter?: string; + field?: string; + order?: "asc" | "desc"; + explain?: boolean; + }): Promise<{ count: number; monitors: any[] }>; + getAllGames(): any; + getGroupsByTeamId(args: { teamId: string }): Promise; + + // update + editMonitor(args: { teamId: string; monitorId: string; body: any }): Promise; + pauseMonitor(args: { teamId: string; monitorId: string }): Promise; + + // delete + deleteMonitor(args: { teamId: string; monitorId: string }): Promise; + deleteAllMonitors(args: { teamId: string }): Promise; + + // other + sendTestEmail(args: { to: string }): Promise; + exportMonitorsToCSV(args: { teamId: string }): Promise; + exportMonitorsToJSON(args: { teamId: string }): Promise; +} + +export class MonitorService implements IMonitorService { static SERVICE_NAME = SERVICE_NAME; + private db: any; + private jobQueue: any; + private stringService: any; + private emailService: any; + private papaparse: any; + private logger: any; + private errorService: any; + private games: any; + private monitorsRepository: IMonitorsRepository; + private checksRepository: any; + constructor({ db, - settingsService, jobQueue, stringService, emailService, @@ -17,9 +92,19 @@ class MonitorService { games, monitorsRepository, checksRepository, + }: { + db: any; + jobQueue: any; + stringService: any; + emailService: any; + papaparse: any; + logger: any; + errorService: any; + games: any; + monitorsRepository: IMonitorsRepository; + checksRepository: any; }) { this.db = db; - this.settingsService = settingsService; this.jobQueue = jobQueue; this.stringService = stringService; this.emailService = emailService; @@ -31,79 +116,34 @@ class MonitorService { this.checksRepository = checksRepository; } - get serviceName() { + get serviceName(): string { return MonitorService.SERVICE_NAME; } - verifyTeamAccess = async ({ teamId, monitorId }) => { + verifyTeamAccess = async ({ teamId, monitorId }: { teamId: string; monitorId: string }): Promise => { const monitor = await this.db.monitorModule.getMonitorById(monitorId); if (!monitor?.teamId?.equals(teamId)) { throw this.errorService.createAuthorizationError(); } }; - getAllMonitors = async () => { - const monitors = await this.db.monitorModule.getAllMonitors(); - return monitors; + createMonitor = async (teamId: string, userId: string, body: Monitor): Promise => { + const monitor = await this.monitorsRepository.create(body, teamId, userId); + if (!monitor) { + throw new AppError("Failed to create monitor", 500); + } + + this.jobQueue.addJob(monitor.id, monitor); }; - getUptimeDetailsById = async ({ teamId, monitorId, dateRange, normalize }) => { - await this.verifyTeamAccess({ teamId, monitorId }); - const data = await this.db.monitorModule.getUptimeDetailsById({ - monitorId, - dateRange, - normalize, - }); - - return data; - }; - - getMonitorStatsById = async ({ teamId, monitorId, limit, sortOrder, dateRange, numToDisplay, normalize }) => { - await this.verifyTeamAccess({ teamId, monitorId }); - const monitorStats = await this.db.monitorModule.getMonitorStatsById({ - monitorId, - limit, - sortOrder, - dateRange, - numToDisplay, - normalize, - }); - - return monitorStats; - }; - - getHardwareDetailsById = async ({ teamId, monitorId, dateRange }) => { - await this.verifyTeamAccess({ teamId, monitorId }); - const monitor = await this.db.monitorModule.getHardwareDetailsById({ monitorId, dateRange }); - - return monitor; - }; - - getMonitorById = async ({ teamId, monitorId }) => { - await this.verifyTeamAccess({ teamId, monitorId }); - const monitor = await this.db.monitorModule.getMonitorById(monitorId); - - return monitor; - }; - - createMonitor = async ({ teamId, userId, body }) => { - const monitor = await this.db.monitorModule.createMonitor({ - body, - teamId, - userId, - }); - - this.jobQueue.addJob(monitor._id, monitor); - }; - - createBulkMonitors = async ({ fileData, userId, teamId }) => { + createBulkMonitors = async ({ fileData, userId, teamId }: { fileData: string; userId: string; teamId: string }): Promise => { const { parse } = this.papaparse; - return new Promise((resolve, reject) => { + return new Promise((resolve, reject) => { parse(fileData, { header: true, skipEmptyLines: true, - transform: (value, header) => { + transform: (value: string, header: string): string | number | undefined => { if (value === "") return undefined; // Empty fields become undefined // Handle 'port' and 'interval' fields, check if they're valid numbers @@ -117,7 +157,7 @@ class MonitorService { return value; }, - complete: async ({ data, errors }) => { + complete: async ({ data, errors }: { data: any[]; errors: Error[] }): Promise => { try { if (errors.length > 0) { throw this.errorService.createServerError("Error parsing CSV"); @@ -127,7 +167,7 @@ class MonitorService { throw this.errorService.createServerError("CSV file contains no data rows"); } - const enrichedData = data.map((monitor) => ({ + const enrichedData = data.map((monitor: any) => ({ userId, teamId, ...monitor, @@ -141,7 +181,7 @@ class MonitorService { const monitors = await this.db.monitorModule.createBulkMonitors(enrichedData); await Promise.all( - monitors.map(async (monitor) => { + monitors.map(async (monitor: any) => { this.jobQueue.addJob(monitor._id, monitor); }) ); @@ -155,71 +195,102 @@ class MonitorService { }); }; - deleteMonitor = async ({ teamId, monitorId }) => { - await this.verifyTeamAccess({ teamId, monitorId }); - const monitor = await this.db.monitorModule.deleteMonitor({ teamId, monitorId }); - await this.jobQueue.deleteJob(monitor); - await this.db.statusPageModule.deleteStatusPagesByMonitorId(monitor._id); - return monitor; - }; - - deleteAllMonitors = async ({ teamId }) => { - const { monitors, deletedCount } = await this.db.monitorModule.deleteAllMonitors(teamId); - await Promise.all( - monitors.map(async (monitor) => { - try { - await this.jobQueue.deleteJob(monitor); - await this.db.checkModule.deleteChecks(monitor._id); - await this.db.pageSpeedCheckModule.deletePageSpeedChecksByMonitorId(monitor._id); - await this.db.notificationsModule.deleteNotificationsByMonitorId(monitor._id); - } catch (error) { - this.logger.warn({ - message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`, - service: SERVICE_NAME, - method: "deleteAllMonitors", - stack: error.stack, - }); - } - }) - ); - return deletedCount; - }; - - editMonitor = async ({ teamId, monitorId, body }) => { - await this.verifyTeamAccess({ teamId, monitorId }); - const editedMonitor = await this.db.monitorModule.editMonitor({ monitorId, body }); - await this.jobQueue.updateJob(editedMonitor); - }; - - pauseMonitor = async ({ teamId, monitorId }) => { - await this.verifyTeamAccess({ teamId, monitorId }); - const monitor = await this.db.monitorModule.pauseMonitor({ monitorId }); - monitor.isActive === true ? await this.jobQueue.resumeJob(monitor._id, monitor) : await this.jobQueue.pauseJob(monitor); - return monitor; - }; - - addDemoMonitors = async ({ userId, teamId }) => { + addDemoMonitors = async ({ userId, teamId }: { userId: string; teamId: string }): Promise => { const demoMonitors = await this.db.monitorModule.addDemoMonitors(userId, teamId); - await Promise.all(demoMonitors.map((monitor) => this.jobQueue.addJob(monitor._id, monitor))); + await Promise.all(demoMonitors.map((monitor: any) => this.jobQueue.addJob(monitor._id, monitor))); return demoMonitors; }; - sendTestEmail = async ({ to }) => { - const subject = this.stringService.testEmailSubject; - const context = { testName: "Monitoring System" }; - - const html = await this.emailService.buildEmail("testEmailTemplate", context); - const messageId = await this.emailService.sendEmail(to, subject, html); - - if (!messageId) { - throw this.errorService.createServerError("Failed to send test email."); - } - - return messageId; + getAllMonitors = async (): Promise => { + const monitors = await this.db.monitorModule.getAllMonitors(); + return monitors; }; - getMonitorsByTeamId = async ({ teamId, limit, type, page, rowsPerPage, filter, field, order }) => { + getUptimeDetailsById = async ({ + teamId, + monitorId, + dateRange, + normalize, + }: { + teamId: string; + monitorId: string; + dateRange: string; + normalize?: boolean; + }): Promise => { + await this.verifyTeamAccess({ teamId, monitorId }); + const data = await this.db.monitorModule.getUptimeDetailsById({ + monitorId, + dateRange, + normalize, + }); + + return data; + }; + + getMonitorStatsById = async ({ + teamId, + monitorId, + limit, + sortOrder, + dateRange, + numToDisplay, + normalize, + }: { + teamId: string; + monitorId: string; + limit?: number; + sortOrder?: 1 | -1; + dateRange?: string; + numToDisplay?: number; + normalize?: boolean; + }): Promise => { + await this.verifyTeamAccess({ teamId, monitorId }); + const monitorStats = await this.db.monitorModule.getMonitorStatsById({ + monitorId, + limit, + sortOrder, + dateRange, + numToDisplay, + normalize, + }); + + return monitorStats; + }; + + getHardwareDetailsById = async ({ teamId, monitorId, dateRange }: { teamId: string; monitorId: string; dateRange: string }): Promise => { + await this.verifyTeamAccess({ teamId, monitorId }); + const monitor = await this.db.monitorModule.getHardwareDetailsById({ monitorId, dateRange }); + + return monitor; + }; + + getMonitorById = async ({ teamId, monitorId }: { teamId: string; monitorId: string }): Promise => { + await this.verifyTeamAccess({ teamId, monitorId }); + const monitor = await this.db.monitorModule.getMonitorById(monitorId); + + return monitor; + }; + + getMonitorsByTeamId = async ({ + teamId, + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + }: { + teamId: string; + limit?: number; + type?: string | string[]; + page?: number; + rowsPerPage?: number; + filter?: string; + field?: string; + order?: "asc" | "desc"; + }): Promise => { const monitors = await this.db.monitorModule.getMonitorsByTeamId({ limit, type, @@ -233,7 +304,15 @@ class MonitorService { return monitors; }; - getMonitorsAndSummaryByTeamId = async ({ teamId, type, explain }) => { + getMonitorsAndSummaryByTeamId = async ({ + teamId, + type, + explain, + }: { + teamId: string; + type?: string | string[]; + explain?: boolean; + }): Promise => { const result = await this.db.monitorModule.getMonitorsAndSummaryByTeamId({ type, explain, @@ -242,7 +321,27 @@ class MonitorService { return result; }; - getMonitorsWithChecksByTeamId = async ({ teamId, limit, type, page, rowsPerPage, filter, field, order, explain }) => { + getMonitorsWithChecksByTeamId = async ({ + teamId, + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + explain, + }: { + teamId: string; + limit?: number; + type?: string | string[]; + page?: number; + rowsPerPage?: number; + filter?: string; + field?: string; + order?: "asc" | "desc"; + explain?: boolean; + }): Promise<{ count: number; monitors: any[] }> => { const count = await this.monitorsRepository.findMonitorCountByTeamIdAndType(teamId, { type, filter }); const monitors = await this.monitorsRepository.findByTeamId(teamId, { limit, @@ -254,9 +353,9 @@ class MonitorService { order, }); - const monitorIds = monitors?.map((m) => m.id) ?? []; + const monitorIds = monitors?.map((m: any) => m.id) ?? []; const checksMap = await this.checksRepository.findLatestChecksByMonitorIds(monitorIds); - const monitorsWithChecks = (monitors ?? []).map((monitor) => { + const monitorsWithChecks = (monitors ?? []).map((monitor: any) => { const checks = NormalizeData(checksMap[monitor.id] ?? [], 10, 100); return { ...monitor, @@ -267,14 +366,80 @@ class MonitorService { return { count, monitors: monitorsWithChecks }; }; - exportMonitorsToCSV = async ({ teamId }) => { + getAllGames = (): any => { + return this.games; + }; + + getGroupsByTeamId = async ({ teamId }: { teamId: string }): Promise => { + const groups = await this.db.monitorModule.getGroupsByTeamId({ teamId }); + return groups; + }; + + editMonitor = async ({ teamId, monitorId, body }: { teamId: string; monitorId: string; body: any }): Promise => { + await this.verifyTeamAccess({ teamId, monitorId }); + const editedMonitor = await this.db.monitorModule.editMonitor({ monitorId, body }); + await this.jobQueue.updateJob(editedMonitor); + }; + + pauseMonitor = async ({ teamId, monitorId }: { teamId: string; monitorId: string }): Promise => { + await this.verifyTeamAccess({ teamId, monitorId }); + const monitor = await this.db.monitorModule.pauseMonitor({ monitorId }); + monitor.isActive === true ? await this.jobQueue.resumeJob(monitor._id, monitor) : await this.jobQueue.pauseJob(monitor); + return monitor; + }; + + deleteMonitor = async ({ teamId, monitorId }: { teamId: string; monitorId: string }): Promise => { + await this.verifyTeamAccess({ teamId, monitorId }); + const monitor = await this.db.monitorModule.deleteMonitor({ teamId, monitorId }); + await this.jobQueue.deleteJob(monitor); + await this.db.statusPageModule.deleteStatusPagesByMonitorId(monitor._id); + return monitor; + }; + + deleteAllMonitors = async ({ teamId }: { teamId: string }): Promise => { + const { monitors, deletedCount } = await this.db.monitorModule.deleteAllMonitors(teamId); + await Promise.all( + monitors.map(async (monitor: any) => { + try { + await this.jobQueue.deleteJob(monitor); + await this.db.checkModule.deleteChecks(monitor._id); + await this.db.pageSpeedCheckModule.deletePageSpeedChecksByMonitorId(monitor._id); + await this.db.notificationsModule.deleteNotificationsByMonitorId(monitor._id); + } catch (error: any) { + this.logger.warn({ + message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`, + service: SERVICE_NAME, + method: "deleteAllMonitors", + stack: error.stack, + }); + } + }) + ); + return deletedCount; + }; + + sendTestEmail = async ({ to }: { to: string }): Promise => { + const subject = this.stringService.testEmailSubject; + const context = { testName: "Monitoring System" }; + + const html = await this.emailService.buildEmail("testEmailTemplate", context); + const messageId = await this.emailService.sendEmail(to, subject, html); + + if (!messageId) { + throw this.errorService.createServerError("Failed to send test email."); + } + + return messageId; + }; + + exportMonitorsToCSV = async ({ teamId }: { teamId: string }): Promise => { const monitors = await this.db.monitorModule.getMonitorsByTeamId({ teamId }); if (!monitors || monitors.length === 0) { throw this.errorService.createNotFoundError("No monitors to export"); } - const csvData = monitors?.filteredMonitors?.map((monitor) => ({ + const csvData = monitors?.filteredMonitors?.map((monitor: any) => ({ name: monitor.name, description: monitor.description, type: monitor.type, @@ -288,7 +453,7 @@ class MonitorService { const csv = this.papaparse.unparse(csvData); return csv; }; - exportMonitorsToJSON = async ({ teamId }) => { + exportMonitorsToJSON = async ({ teamId }: { teamId: string }): Promise => { const monitors = await this.db.monitorModule.getMonitorsByTeamId({ teamId }); if (!monitors || monitors.length === 0) { @@ -296,7 +461,7 @@ class MonitorService { } const json = monitors?.filteredMonitors - ?.map((monitor) => { + ?.map((monitor: any) => { const initialType = monitor.type; let parsedType; @@ -328,15 +493,4 @@ class MonitorService { return json; }; - - getAllGames = () => { - return this.games; - }; - - getGroupsByTeamId = async ({ teamId }) => { - const groups = await this.db.monitorModule.getGroupsByTeamId({ teamId }); - return groups; - }; } - -export default MonitorService; diff --git a/server/src/service/index.ts b/server/src/service/index.ts new file mode 100644 index 000000000..4793f055b --- /dev/null +++ b/server/src/service/index.ts @@ -0,0 +1 @@ +export * from "@/service/business/monitorService.js"; diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js index a3cdea8e7..a6637d4cc 100644 --- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js +++ b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueue.js @@ -4,19 +4,20 @@ const SERVICE_NAME = "JobQueue"; class SuperSimpleQueue { static SERVICE_NAME = SERVICE_NAME; - constructor({ envSettings, db, logger, helper }) { + constructor({ envSettings, db, logger, helper, monitorsRepository }) { this.envSettings = envSettings; this.db = db; this.logger = logger; this.helper = helper; + this.monitorsRepository = monitorsRepository; } get serviceName() { return SuperSimpleQueue.SERVICE_NAME; } - static async create({ envSettings, db, logger, helper }) { - const instance = new SuperSimpleQueue({ envSettings, db, logger, helper }); + static async create({ envSettings, db, logger, helper, monitorsRepository }) { + const instance = new SuperSimpleQueue({ envSettings, db, logger, helper, monitorsRepository }); await instance.init(); return instance; } @@ -33,11 +34,11 @@ class SuperSimpleQueue { this.scheduler.start(); this.scheduler.addTemplate("monitor-job", this.helper.getMonitorJob()); - const monitors = await this.db.monitorModule.getAllMonitors(); + const monitors = await this.monitorsRepository.findAll(); for (const monitor of monitors) { const randomOffset = Math.floor(Math.random() * 100); setTimeout(() => { - this.addJob(monitor._id, monitor); + this.addJob(monitor.id, monitor); }, randomOffset); } @@ -55,44 +56,44 @@ class SuperSimpleQueue { addJob = async (monitorId, monitor) => { this.scheduler.addJob({ - id: monitorId.toString(), + id: monitorId, template: "monitor-job", repeat: monitor.interval, active: monitor.isActive, - data: monitor.toObject(), + data: monitor, }); }; deleteJob = async (monitor) => { - this.scheduler.removeJob(monitor._id.toString()); + this.scheduler.removeJob(monitor.id); }; pauseJob = async (monitor) => { - const result = this.scheduler.pauseJob(monitor._id.toString()); + const result = this.scheduler.pauseJob(monitor.id); if (result === false) { throw new Error("Failed to resume monitor"); } this.logger.debug({ - message: `Paused monitor ${monitor._id}`, + message: `Paused monitor ${monitor.id}`, service: SERVICE_NAME, method: "pauseJob", }); }; resumeJob = async (monitor) => { - const result = this.scheduler.resumeJob(monitor._id.toString()); + const result = this.scheduler.resumeJob(monitor.id); if (result === false) { throw new Error("Failed to resume monitor"); } this.logger.debug({ - message: `Resumed monitor ${monitor._id}`, + message: `Resumed monitor ${monitor.id}`, service: SERVICE_NAME, method: "resumeJob", }); }; updateJob = async (monitor) => { - this.scheduler.updateJob(monitor._id.toString(), { repeat: monitor.interval, data: monitor.toObject() }); + this.scheduler.updateJob(monitor.id, { repeat: monitor.interval, data: monitor }); }; shutdown = async () => { diff --git a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js index 2833a59f5..3cd6b37a8 100644 --- a/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js +++ b/server/src/service/infrastructure/SuperSimpleQueue/SuperSimpleQueueHelper.js @@ -27,7 +27,7 @@ class SuperSimpleQueueHelper { getMonitorJob = () => { return async (monitor) => { try { - const monitorId = monitor._id; + const monitorId = monitor.id; const teamId = monitor.teamId; if (!monitorId) { throw new Error("No monitor id"); @@ -62,7 +62,7 @@ class SuperSimpleQueueHelper { message: error.message, service: SERVICE_NAME, method: "getMonitorJob", - details: `Error sending notifications for job ${monitor._id}: ${error.message}`, + details: `Error sending notifications for job ${monitor.id}: ${error.message}`, stack: error.stack, }); }); diff --git a/server/src/service/infrastructure/networkService.js b/server/src/service/infrastructure/networkService.js index d364f373e..2375e7233 100644 --- a/server/src/service/infrastructure/networkService.js +++ b/server/src/service/infrastructure/networkService.js @@ -95,7 +95,7 @@ class NetworkService { } const pingResponse = { - monitorId: monitor._id, + monitorId: monitor.id, type: "ping", status: response.alive, code: 200, @@ -120,9 +120,9 @@ class NetworkService { } async requestHttp(monitor) { - const { url, secret, _id, teamId, type, ignoreTlsErrors, jsonPath, matchMethod, expectedValue } = monitor; + const { url, secret, id, teamId, type, ignoreTlsErrors, jsonPath, matchMethod, expectedValue } = monitor; const httpResponse = { - monitorId: _id, + monitorId: id, teamId: teamId, type, }; @@ -298,7 +298,7 @@ class NetworkService { }); const dockerResponse = { - monitorId: monitor._id, + monitorId: monitor.id, type: monitor.type, }; @@ -416,7 +416,7 @@ class NetworkService { code: 200, status: response.success, message: this.stringService.portSuccess, - monitorId: monitor._id, + monitorId: monitor.id, type: monitor.type, responseTime: responseTime, }; @@ -444,7 +444,7 @@ class NetworkService { code: 200, status: true, message: "Success", - monitorId: monitor._id, + monitorId: monitor.id, type: "game", }; diff --git a/server/src/service/infrastructure/statusService.js b/server/src/service/infrastructure/statusService.js index 5b64c7419..30a9fc12c 100755 --- a/server/src/service/infrastructure/statusService.js +++ b/server/src/service/infrastructure/statusService.js @@ -10,11 +10,12 @@ class StatusService { * buffer: import("./bufferService.js").BufferService * incidentService: import("../business/incidentService.js").IncidentService * }} - */ constructor({ db, logger, buffer, incidentService }) { + */ constructor({ db, logger, buffer, incidentService, monitorsRepository }) { this.db = db; this.logger = logger; this.buffer = buffer; this.incidentService = incidentService; + this.monitorsRepository = monitorsRepository; } get serviceName() { @@ -23,7 +24,7 @@ class StatusService { async updateRunningStats({ monitor, networkResponse }) { try { - const monitorId = monitor._id; + const monitorId = monitor.id; const { responseTime, status } = networkResponse; // Get stats let stats = await MonitorStats.findOne({ monitorId }); @@ -124,7 +125,7 @@ class StatusService { service: this.SERVICE_NAME, method: "handleIncidentForCheck", message: `Failed to save check immediately for ${errorContext}: ${checkError.message}`, - monitorId: monitor._id, + monitorId: monitor.id, stack: checkError.stack, }); savedCheck = null; @@ -139,7 +140,7 @@ class StatusService { service: this.SERVICE_NAME, method: "handleIncidentForCheck", message: `Failed to add incident to buffer for ${errorContext}: ${incidentError.message}`, - monitorId: monitor._id, + monitorId: monitor.id, action, stack: incidentError.stack, }); @@ -150,7 +151,7 @@ class StatusService { service: this.SERVICE_NAME, method: "handleIncidentForCheck", message: `Error in ${errorContext}: ${error.message}`, - monitorId: monitor?._id, + monitorId: monitor?.id, stack: error.stack, }); } @@ -172,7 +173,8 @@ class StatusService { await this.insertCheck(check); try { const { monitorId, status, code } = networkResponse; - const monitor = await this.db.monitorModule.getMonitorById(monitorId); + + const monitor = await this.monitorsRepository.findById(monitorId); // Update running stats this.updateRunningStats({ monitor, networkResponse }); @@ -198,9 +200,9 @@ class StatusService { // Return early if not enough data points if (monitor.statusWindow.length < monitor.statusWindowSize) { - await monitor.save(); + const updated = await this.monitorsRepository.update(monitor.id, monitor); return { - monitor, + monitor: updated, statusChanged: false, prevStatus, code, @@ -240,14 +242,14 @@ class StatusService { if (monitor.status === false && !statusChanged) { try { - const lastManuallyResolvedIncident = await this.db.incidentModule.getLastManuallyResolvedIncident(monitor._id); + const lastManuallyResolvedIncident = await this.db.incidentModule.getLastManuallyResolvedIncident(monitor.id); let calculatedFailureRate = failureRate; if (lastManuallyResolvedIncident && lastManuallyResolvedIncident.endTime) { try { const checksAfterResolution = await Check.find({ - monitorId: monitor._id, + monitorId: monitor.id, createdAt: { $gt: lastManuallyResolvedIncident.endTime }, }) .sort({ createdAt: 1 }) @@ -267,7 +269,7 @@ class StatusService { service: this.SERVICE_NAME, method: "updateStatus", message: `Failed to query checks after manual resolution: ${checkQueryError.message}`, - monitorId: monitor._id, + monitorId: monitor.id, stack: checkQueryError.stack, }); } @@ -281,17 +283,17 @@ class StatusService { service: this.SERVICE_NAME, method: "updateStatus", message: `Error handling threshold check without status change: ${error.message}`, - monitorId: monitor._id, + monitorId: monitor.id, stack: error.stack, }); } } monitor.status = newStatus; - await monitor.save(); + const updated = await this.monitorsRepository.update(monitor.id, monitor); return { - monitor, + monitor: updated, statusChanged, prevStatus, code, @@ -374,7 +376,7 @@ class StatusService { return { id: audit.id, title: audit.title, - score: typeof audit.score === "number" ? audit.score : audit.score ?? null, + score: typeof audit.score === "number" ? audit.score : (audit.score ?? null), displayValue: audit.displayValue, numericValue: typeof audit.numericValue === "number" ? audit.numericValue : undefined, numericUnit: audit.numericUnit,