create and update

This commit is contained in:
Alex Holliday
2026-01-13 20:20:00 +00:00
parent 117a2e00c4
commit d0b3bc60df
10 changed files with 384 additions and 186 deletions
+3 -3
View File
@@ -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,
+1 -1
View File
@@ -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,
@@ -12,11 +12,15 @@ export interface TeamQueryConfig {
export interface IMonitorsRepository {
// create
create(monitor: Monitor, teamId: string, userId: string): Promise<Monitor | null>;
// single fetch
findById(monitorId: string): Promise<Monitor | null>;
// collection fetch
findAll(): Promise<Monitor[] | null>;
findByTeamId(teamId: string, config: TeamQueryConfig): Promise<Monitor[] | null>;
// update
update(monitorId: string, updates: Partial<Monitor>): Promise<Monitor>;
// delete
// counts
@@ -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<Monitor[] | null> => {
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<Monitor> => {
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<Monitor[]> => {
const monitors = await MonitorModel.find();
return this.mapDocuments(monitors);
};
findByTeamId = async (teamId: string, config: TeamQueryConfig): Promise<Monitor[] | null> => {
@@ -64,9 +84,25 @@ class MongoMonitorsRepository implements IMonitorsRepository {
return count;
};
private mapDocuments = (documents: MonitorDocument[]): Monitor[] | null => {
update = async (monitorId: string, patch: Partial<Monitor>) => {
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));
};
@@ -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<void>;
// create
createMonitor(teamId: string, userId: string, body: Monitor): Promise<void>;
createBulkMonitors(args: { fileData: string; userId: string; teamId: string }): Promise<any>;
addDemoMonitors(args: { userId: string; teamId: string }): Promise<any[]>;
// read
getAllMonitors(): Promise<any[]>;
getUptimeDetailsById(args: { teamId: string; monitorId: string; dateRange: string; normalize?: boolean }): Promise<any>;
getMonitorStatsById(args: {
teamId: string;
monitorId: string;
limit?: number;
sortOrder?: 1 | -1;
dateRange?: string;
numToDisplay?: number;
normalize?: boolean;
}): Promise<any>;
getHardwareDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<any>;
getMonitorById(args: { teamId: string; monitorId: string }): Promise<any>;
getMonitorsByTeamId(args: {
teamId: string;
limit?: number;
type?: string | string[];
page?: number;
rowsPerPage?: number;
filter?: string;
field?: string;
order?: "asc" | "desc";
}): Promise<any>;
getMonitorsAndSummaryByTeamId(args: { teamId: string; type?: string | string[]; explain?: boolean }): Promise<any>;
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<any[]>;
// update
editMonitor(args: { teamId: string; monitorId: string; body: any }): Promise<void>;
pauseMonitor(args: { teamId: string; monitorId: string }): Promise<any>;
// delete
deleteMonitor(args: { teamId: string; monitorId: string }): Promise<any>;
deleteAllMonitors(args: { teamId: string }): Promise<number>;
// other
sendTestEmail(args: { to: string }): Promise<string>;
exportMonitorsToCSV(args: { teamId: string }): Promise<string>;
exportMonitorsToJSON(args: { teamId: string }): Promise<any[]>;
}
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<void> => {
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<void> => {
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<any> => {
const { parse } = this.papaparse;
return new Promise((resolve, reject) => {
return new Promise<any>((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<void> => {
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<any[]> => {
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<any[]> => {
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<any> => {
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<any> => {
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<any> => {
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<any> => {
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<any> => {
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<any> => {
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<any[]> => {
const groups = await this.db.monitorModule.getGroupsByTeamId({ teamId });
return groups;
};
editMonitor = async ({ teamId, monitorId, body }: { teamId: string; monitorId: string; body: any }): Promise<void> => {
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<any> => {
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<any> => {
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<number> => {
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<string> => {
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<string> => {
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<any[]> => {
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;
+1
View File
@@ -0,0 +1 @@
export * from "@/service/business/monitorService.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 () => {
@@ -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,
});
});
@@ -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",
};
@@ -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,