Merge upstream/develop into fix/3120-data-leak
This commit is contained in:
commit
f0a6fc4fdf
309 files changed
+8413
-4468
No files matched your search
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -0,0 +1,566 @@
|
||||
import { createMonitorsBodyValidation } from "@/validation/joi.js";
|
||||
import { NormalizeData, NormalizeDataUptimeDetails } from "@/utils/dataUtils.js";
|
||||
import { type Monitor } from "@/types/index.js";
|
||||
import type { MonitorType } from "@/types/monitor.js";
|
||||
import type { IChecksRepository, IMonitorsRepository, IMonitorStatsRepository } from "@/repositories/index.js";
|
||||
import fs from "fs";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
|
||||
import { AppError } from "../infrastructure/errorService.js";
|
||||
|
||||
const SERVICE_NAME = "MonitorService";
|
||||
type DateRangeKey = "recent" | "day" | "week" | "month" | "all";
|
||||
|
||||
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(fileData: string, userId: string, teamId: string): Promise<any>;
|
||||
addDemoMonitors(args: { userId: string; teamId: string }): Promise<any[]>;
|
||||
|
||||
// read
|
||||
getUptimeDetailsById(args: { teamId: string; monitorId: string; dateRange: string; normalize?: boolean }): Promise<any>;
|
||||
getHardwareDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<any>;
|
||||
getPageSpeedDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<any>;
|
||||
getMonitorById(args: { teamId: string; monitorId: string }): Promise<Monitor>;
|
||||
getMonitorsByTeamId(args: {
|
||||
teamId: string;
|
||||
limit?: number;
|
||||
type?: MonitorType | MonitorType[];
|
||||
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?: MonitorType | MonitorType[];
|
||||
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: IChecksRepository;
|
||||
private monitorStatsRepository: IMonitorStatsRepository;
|
||||
|
||||
constructor({
|
||||
db,
|
||||
jobQueue,
|
||||
stringService,
|
||||
emailService,
|
||||
papaparse,
|
||||
logger,
|
||||
errorService,
|
||||
games,
|
||||
monitorsRepository,
|
||||
checksRepository,
|
||||
monitorStatsRepository,
|
||||
}: {
|
||||
db: any;
|
||||
jobQueue: any;
|
||||
stringService: any;
|
||||
emailService: any;
|
||||
papaparse: any;
|
||||
logger: any;
|
||||
errorService: any;
|
||||
games: any;
|
||||
monitorsRepository: IMonitorsRepository;
|
||||
checksRepository: IChecksRepository;
|
||||
monitorStatsRepository: IMonitorStatsRepository;
|
||||
}) {
|
||||
this.db = db;
|
||||
this.jobQueue = jobQueue;
|
||||
this.stringService = stringService;
|
||||
this.emailService = emailService;
|
||||
this.papaparse = papaparse;
|
||||
this.logger = logger;
|
||||
this.errorService = errorService;
|
||||
this.games = games;
|
||||
this.monitorsRepository = monitorsRepository;
|
||||
this.checksRepository = checksRepository;
|
||||
this.monitorStatsRepository = monitorStatsRepository;
|
||||
}
|
||||
|
||||
get serviceName(): string {
|
||||
return MonitorService.SERVICE_NAME;
|
||||
}
|
||||
|
||||
private getDateRange = (dateRange: DateRangeKey) => {
|
||||
const startDates = {
|
||||
recent: new Date(new Date().setHours(new Date().getHours() - 2)),
|
||||
day: new Date(new Date().setDate(new Date().getDate() - 1)),
|
||||
week: new Date(new Date().setDate(new Date().getDate() - 7)),
|
||||
month: new Date(new Date().setMonth(new Date().getMonth() - 1)),
|
||||
all: new Date(0),
|
||||
};
|
||||
return {
|
||||
start: startDates[dateRange],
|
||||
end: new Date(),
|
||||
};
|
||||
};
|
||||
|
||||
private getDateFormat = (dateRange: DateRangeKey): string => {
|
||||
const formatLookup = {
|
||||
recent: "%Y-%m-%dT%H:%M:00Z",
|
||||
day: "%Y-%m-%dT%H:00:00Z",
|
||||
week: "%Y-%m-%dT00:00:00Z",
|
||||
month: "%Y-%m-%dT00:00:00Z",
|
||||
all: "%Y-%m-%dT00:00:00Z",
|
||||
};
|
||||
return formatLookup[dateRange];
|
||||
};
|
||||
|
||||
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();
|
||||
}
|
||||
};
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
createBulkMonitors = async (fileData: string, userId: string, teamId: string): Promise<any> => {
|
||||
const { parse } = this.papaparse;
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
parse(fileData, {
|
||||
header: true,
|
||||
skipEmptyLines: true,
|
||||
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
|
||||
if (["port", "interval"].includes(header)) {
|
||||
const num = parseInt(value, 10);
|
||||
if (isNaN(num)) {
|
||||
throw this.errorService.createBadRequestError(`${header} should be a valid number, got: ${value}`);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
complete: async ({ data, errors }: { data: any[]; errors: Error[] }): Promise<void> => {
|
||||
try {
|
||||
if (errors.length > 0) {
|
||||
throw this.errorService.createServerError("Error parsing CSV");
|
||||
}
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
throw this.errorService.createServerError("CSV file contains no data rows");
|
||||
}
|
||||
|
||||
const enrichedData = data.map((monitor: Monitor) => ({
|
||||
...monitor,
|
||||
userId,
|
||||
teamId,
|
||||
description: monitor.description || monitor.name || monitor.url,
|
||||
name: monitor.name || monitor.url,
|
||||
type: monitor.type || "http",
|
||||
}));
|
||||
|
||||
await createMonitorsBodyValidation.validateAsync(enrichedData);
|
||||
|
||||
const monitors = await this.monitorsRepository.createBulkMonitors(enrichedData);
|
||||
|
||||
await Promise.all(
|
||||
monitors.map(async (monitor: Monitor) => {
|
||||
this.jobQueue.addJob(monitor.id, monitor);
|
||||
})
|
||||
);
|
||||
|
||||
resolve(monitors);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
addDemoMonitors = async ({ userId, teamId }: { userId: string; teamId: string }): Promise<any[]> => {
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = path.dirname(__filename);
|
||||
const demoMonitorsPath = path.resolve(__dirname, "../../utils/demoMonitors.json");
|
||||
|
||||
const demoData = JSON.parse(fs.readFileSync(demoMonitorsPath, "utf8"));
|
||||
const monitors: Monitor[] = demoData.map((monitor: Monitor) => {
|
||||
return {
|
||||
userId,
|
||||
teamId,
|
||||
name: monitor.name,
|
||||
description: monitor.name,
|
||||
type: "http",
|
||||
url: monitor.url,
|
||||
interval: 60000,
|
||||
};
|
||||
});
|
||||
const demoMonitors = await this.monitorsRepository.createBulkMonitors(monitors);
|
||||
|
||||
await Promise.all(demoMonitors.map((monitor) => this.jobQueue.addJob(monitor.id, monitor)));
|
||||
return demoMonitors;
|
||||
};
|
||||
|
||||
getUptimeDetailsById = async ({
|
||||
teamId,
|
||||
monitorId,
|
||||
dateRange,
|
||||
normalize,
|
||||
}: {
|
||||
teamId: string;
|
||||
monitorId: string;
|
||||
dateRange: string;
|
||||
normalize?: boolean;
|
||||
}): Promise<any> => {
|
||||
await this.verifyTeamAccess({ teamId, monitorId });
|
||||
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
if (!monitor) {
|
||||
throw new AppError({ message: `Monitor with ID ${monitorId} not found.`, status: 404 });
|
||||
}
|
||||
const rangeKey = (dateRange as DateRangeKey) ?? "recent";
|
||||
const { start, end } = this.getDateRange(rangeKey);
|
||||
const checksData = await this.checksRepository.findDateRangeChecksByMonitor(monitor.id, start, end, this.getDateFormat(rangeKey), {
|
||||
type: monitor.type,
|
||||
});
|
||||
const monitorStats = await this.monitorStatsRepository.findByMonitorId(monitor.id);
|
||||
|
||||
if (
|
||||
checksData.monitorType !== "http" &&
|
||||
checksData.monitorType !== "ping" &&
|
||||
checksData.monitorType !== "docker" &&
|
||||
checksData.monitorType !== "port" &&
|
||||
checksData.monitorType !== "game"
|
||||
) {
|
||||
throw new AppError({ message: `${monitor.type} monitors are not supported for uptime details`, status: 400 });
|
||||
}
|
||||
|
||||
return {
|
||||
monitorData: {
|
||||
monitor,
|
||||
groupedChecks: NormalizeDataUptimeDetails(checksData.groupedChecks, 10, 100),
|
||||
groupedUpChecks: NormalizeDataUptimeDetails(checksData.groupedUpChecks, 10, 100),
|
||||
groupedDownChecks: NormalizeDataUptimeDetails(checksData.groupedDownChecks, 10, 100),
|
||||
groupedAvgResponseTime: checksData.avgResponseTime,
|
||||
groupedUptimePercentage: checksData.uptimePercentage,
|
||||
},
|
||||
monitorStats,
|
||||
};
|
||||
};
|
||||
|
||||
getHardwareDetailsById = async ({ teamId, monitorId, dateRange }: { teamId: string; monitorId: string; dateRange: string }): Promise<any> => {
|
||||
await this.verifyTeamAccess({ teamId, monitorId });
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
if (!monitor) {
|
||||
throw new AppError({ message: `Monitor with ID ${monitorId} not found.`, status: 404 });
|
||||
}
|
||||
if (monitor.type !== "hardware") {
|
||||
throw new AppError({ message: `${monitor.type} monitors are not supported for hardware details`, status: 400 });
|
||||
}
|
||||
|
||||
const rangeKey = (dateRange as DateRangeKey) ?? "recent";
|
||||
const { start, end } = this.getDateRange(rangeKey);
|
||||
const checksData = await this.checksRepository.findDateRangeChecksByMonitor(monitor.id, start, end, this.getDateFormat(rangeKey), {
|
||||
type: monitor.type,
|
||||
});
|
||||
|
||||
if (checksData.monitorType !== "hardware") {
|
||||
throw new AppError({ message: "Unable to load hardware stats for this monitor", status: 500 });
|
||||
}
|
||||
|
||||
const stats = {
|
||||
aggregateData: checksData.aggregateData,
|
||||
upChecks: checksData.upChecks,
|
||||
checks: checksData.checks,
|
||||
};
|
||||
|
||||
return {
|
||||
...monitor,
|
||||
stats,
|
||||
};
|
||||
};
|
||||
|
||||
getPageSpeedDetailsById = async ({ teamId, monitorId, dateRange }: { teamId: string; monitorId: string; dateRange: string }): Promise<any> => {
|
||||
await this.verifyTeamAccess({ teamId, monitorId });
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
if (!monitor) {
|
||||
throw new AppError({ message: `Monitor with ID ${monitorId} not found.`, status: 404 });
|
||||
}
|
||||
if (monitor.type !== "pagespeed") {
|
||||
throw new AppError({ message: `${monitor.type} monitors are not supported for pagespeed details`, status: 400 });
|
||||
}
|
||||
|
||||
const rangeKey = (dateRange as DateRangeKey) ?? "recent";
|
||||
const { start, end } = this.getDateRange(rangeKey);
|
||||
const checksData = await this.checksRepository.findDateRangeChecksByMonitor(monitor.id, start, end, this.getDateFormat(rangeKey), {
|
||||
type: monitor.type,
|
||||
});
|
||||
|
||||
if (checksData.monitorType !== "pagespeed") {
|
||||
throw new AppError({ message: "Unable to load pagespeed stats for this monitor", status: 500 });
|
||||
}
|
||||
return {
|
||||
...monitor,
|
||||
checks: checksData.checks,
|
||||
};
|
||||
};
|
||||
getMonitorById = async ({ teamId, monitorId }: { teamId: string; monitorId: string }): Promise<any> => {
|
||||
await this.verifyTeamAccess({ teamId, monitorId });
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
return monitor;
|
||||
};
|
||||
|
||||
getMonitorsByTeamId = async ({ teamId, type, filter }: { teamId: string; type?: MonitorType | MonitorType[]; filter?: string }): Promise<any> => {
|
||||
const monitors = await this.monitorsRepository.findByTeamId(teamId, {
|
||||
type,
|
||||
filter,
|
||||
});
|
||||
return monitors;
|
||||
};
|
||||
|
||||
getMonitorsAndSummaryByTeamId = async ({
|
||||
teamId,
|
||||
type,
|
||||
explain,
|
||||
}: {
|
||||
teamId: string;
|
||||
type?: string | string[];
|
||||
explain?: boolean;
|
||||
}): Promise<any> => {
|
||||
const result = await this.db.monitorModule.getMonitorsAndSummaryByTeamId({
|
||||
type,
|
||||
explain,
|
||||
teamId,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
getMonitorsWithChecksByTeamId = async ({
|
||||
teamId,
|
||||
limit,
|
||||
type,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
explain,
|
||||
}: {
|
||||
teamId: string;
|
||||
limit?: number;
|
||||
type?: MonitorType | MonitorType[];
|
||||
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,
|
||||
type,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
});
|
||||
|
||||
const monitorsList = (monitors ?? []) as Monitor[];
|
||||
const snapshotTypes: MonitorType[] = ["hardware", "pagespeed"];
|
||||
const requestedTypes = Array.isArray(type) ? type : type ? [type] : [];
|
||||
const snapshotOnlyRequest =
|
||||
requestedTypes.length > 0 && requestedTypes.every((requestedType) => snapshotTypes.includes(requestedType as MonitorType));
|
||||
|
||||
const limitPerMonitor = snapshotOnlyRequest ? 1 : 25;
|
||||
const checksMap = await this.checksRepository.findLatestChecksByMonitorIds(
|
||||
monitorsList.map((monitor) => monitor.id),
|
||||
{ limitPerMonitor }
|
||||
);
|
||||
|
||||
const monitorsWithChecks = monitorsList.map((monitor: Monitor) => {
|
||||
const rawChecks = checksMap[monitor.id] ?? [];
|
||||
const isSnapshotType = snapshotOnlyRequest || snapshotTypes.includes(monitor.type);
|
||||
const checks = isSnapshotType ? rawChecks.slice(0, 1) : NormalizeData(rawChecks, 10, 100);
|
||||
return {
|
||||
...monitor,
|
||||
checks,
|
||||
};
|
||||
});
|
||||
|
||||
return { count, monitors: monitorsWithChecks };
|
||||
};
|
||||
|
||||
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.monitorsRepository.deleteByTeamId(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: 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: any) => ({
|
||||
name: monitor.name,
|
||||
description: monitor.description,
|
||||
type: monitor.type,
|
||||
url: monitor.url,
|
||||
interval: monitor.interval,
|
||||
port: monitor.port,
|
||||
ignoreTlsErrors: monitor.ignoreTlsErrors,
|
||||
isActive: monitor.isActive,
|
||||
}));
|
||||
|
||||
const csv = this.papaparse.unparse(csvData);
|
||||
return csv;
|
||||
};
|
||||
exportMonitorsToJSON = async ({ teamId }: { teamId: string }): Promise<any[]> => {
|
||||
const monitors = await this.db.monitorModule.getMonitorsByTeamId({ teamId });
|
||||
|
||||
if (!monitors || monitors.length === 0) {
|
||||
throw this.errorService.createNotFoundError("No monitors to export");
|
||||
}
|
||||
|
||||
const json = monitors?.filteredMonitors
|
||||
?.map((monitor: any) => {
|
||||
const initialType = monitor.type;
|
||||
let parsedType;
|
||||
|
||||
if (initialType === "hardware") {
|
||||
parsedType = "infrastructure";
|
||||
} else if (initialType === "http") {
|
||||
if (monitor.url.startsWith("https://")) {
|
||||
parsedType = "https";
|
||||
} else {
|
||||
parsedType = "http";
|
||||
}
|
||||
} else if (initialType === "pagespeed") {
|
||||
parsedType = initialType;
|
||||
} else {
|
||||
// Skip unsupported types
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
name: monitor.name,
|
||||
url: monitor.url,
|
||||
type: parsedType,
|
||||
interval: monitor.interval,
|
||||
n: monitor.statusWindowSize,
|
||||
secret: monitor.secret,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return json;
|
||||
};
|
||||
}
|
||||
File renamed without changes.
@@ -0,0 +1 @@
|
||||
export * from "@/service/business/monitorService.js";
|
||||
+14
-13
@@ -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 () => {
|
||||
+2
-2
@@ -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,
|
||||
});
|
||||
});
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+6
-6
@@ -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",
|
||||
};
|
||||
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
+48
-28
@@ -1,5 +1,5 @@
|
||||
import MonitorStats from "../../../db/v1/models/MonitorStats.js";
|
||||
import Check from "../../../db/v1/models/Check.js";
|
||||
import MonitorStats from "../../db/models/MonitorStats.js";
|
||||
import { CheckModel } from "@/db/models/index.js";
|
||||
const SERVICE_NAME = "StatusService";
|
||||
|
||||
class StatusService {
|
||||
@@ -7,14 +7,19 @@ class StatusService {
|
||||
|
||||
/**
|
||||
* @param {{
|
||||
* db: any
|
||||
* logger: any
|
||||
* buffer: import("./bufferService.js").BufferService
|
||||
* incidentService: import("../business/incidentService.js").IncidentService
|
||||
* monitorsRepository: any
|
||||
* }}
|
||||
*/ 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 +28,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 });
|
||||
@@ -115,7 +120,7 @@ class StatusService {
|
||||
|
||||
if (!check._id) {
|
||||
try {
|
||||
const checkModel = new Check(check);
|
||||
const checkModel = new CheckModel(check);
|
||||
savedCheck = await checkModel.save();
|
||||
|
||||
this.buffer.removeCheckFromBuffer(check);
|
||||
@@ -124,7 +129,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 +144,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 +155,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 +177,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 +204,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 +246,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 +273,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 +287,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,
|
||||
@@ -337,9 +343,11 @@ class StatusService {
|
||||
} = networkResponse;
|
||||
|
||||
const check = {
|
||||
monitorId,
|
||||
teamId,
|
||||
type,
|
||||
metadata: {
|
||||
monitorId,
|
||||
teamId,
|
||||
type,
|
||||
},
|
||||
status,
|
||||
statusCode: code,
|
||||
responseTime,
|
||||
@@ -365,18 +373,30 @@ class StatusService {
|
||||
}
|
||||
const categories = payload?.lighthouseResult?.categories ?? {};
|
||||
const audits = payload?.lighthouseResult?.audits ?? {};
|
||||
const {
|
||||
"cumulative-layout-shift": cls = 0,
|
||||
"speed-index": si = 0,
|
||||
"first-contentful-paint": fcp = 0,
|
||||
"largest-contentful-paint": lcp = 0,
|
||||
"total-blocking-time": tbt = 0,
|
||||
} = audits;
|
||||
const mapAudit = (audit) => {
|
||||
if (!audit || typeof audit !== "object") {
|
||||
return undefined;
|
||||
}
|
||||
return {
|
||||
id: audit.id,
|
||||
title: audit.title,
|
||||
score: typeof audit.score === "number" ? audit.score : (audit.score ?? null),
|
||||
displayValue: audit.displayValue,
|
||||
numericValue: typeof audit.numericValue === "number" ? audit.numericValue : undefined,
|
||||
numericUnit: audit.numericUnit,
|
||||
};
|
||||
};
|
||||
check.accessibility = (categories?.accessibility?.score || 0) * 100;
|
||||
check.bestPractices = (categories?.["best-practices"]?.score || 0) * 100;
|
||||
check.seo = (categories?.seo?.score || 0) * 100;
|
||||
check.performance = (categories?.performance?.score || 0) * 100;
|
||||
check.audits = { cls, si, fcp, lcp, tbt };
|
||||
check.audits = {
|
||||
cls: mapAudit(audits?.["cumulative-layout-shift"]),
|
||||
si: mapAudit(audits?.["speed-index"]),
|
||||
fcp: mapAudit(audits?.["first-contentful-paint"]),
|
||||
lcp: mapAudit(audits?.["largest-contentful-paint"]),
|
||||
tbt: mapAudit(audits?.["total-blocking-time"]),
|
||||
};
|
||||
}
|
||||
|
||||
if (type === "hardware") {
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -1,317 +0,0 @@
|
||||
import { createMonitorsBodyValidation } from "../../../validation/joi.js";
|
||||
|
||||
const SERVICE_NAME = "MonitorService";
|
||||
class MonitorService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
constructor({ db, settingsService, jobQueue, stringService, emailService, papaparse, logger, errorService, games }) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.jobQueue = jobQueue;
|
||||
this.stringService = stringService;
|
||||
this.emailService = emailService;
|
||||
this.papaparse = papaparse;
|
||||
this.logger = logger;
|
||||
this.errorService = errorService;
|
||||
this.games = games;
|
||||
}
|
||||
|
||||
get serviceName() {
|
||||
return MonitorService.SERVICE_NAME;
|
||||
}
|
||||
|
||||
verifyTeamAccess = async ({ teamId, monitorId }) => {
|
||||
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;
|
||||
};
|
||||
|
||||
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 }) => {
|
||||
const { parse } = this.papaparse;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
parse(fileData, {
|
||||
header: true,
|
||||
skipEmptyLines: true,
|
||||
transform: (value, header) => {
|
||||
if (value === "") return undefined; // Empty fields become undefined
|
||||
|
||||
// Handle 'port' and 'interval' fields, check if they're valid numbers
|
||||
if (["port", "interval"].includes(header)) {
|
||||
const num = parseInt(value, 10);
|
||||
if (isNaN(num)) {
|
||||
throw this.errorService.createBadRequestError(`${header} should be a valid number, got: ${value}`);
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
complete: async ({ data, errors }) => {
|
||||
try {
|
||||
if (errors.length > 0) {
|
||||
throw this.errorService.createServerError("Error parsing CSV");
|
||||
}
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
throw this.errorService.createServerError("CSV file contains no data rows");
|
||||
}
|
||||
|
||||
const enrichedData = data.map((monitor) => ({
|
||||
userId,
|
||||
teamId,
|
||||
...monitor,
|
||||
description: monitor.description || monitor.name || monitor.url,
|
||||
name: monitor.name || monitor.url,
|
||||
type: monitor.type || "http",
|
||||
}));
|
||||
|
||||
await createMonitorsBodyValidation.validateAsync(enrichedData);
|
||||
|
||||
const monitors = await this.db.monitorModule.createBulkMonitors(enrichedData);
|
||||
|
||||
await Promise.all(
|
||||
monitors.map(async (monitor) => {
|
||||
this.jobQueue.addJob(monitor._id, monitor);
|
||||
})
|
||||
);
|
||||
|
||||
resolve(monitors);
|
||||
} catch (error) {
|
||||
reject(error);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
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 }) => {
|
||||
const demoMonitors = await this.db.monitorModule.addDemoMonitors(userId, teamId);
|
||||
|
||||
await Promise.all(demoMonitors.map((monitor) => 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;
|
||||
};
|
||||
|
||||
getMonitorsByTeamId = async ({ teamId, limit, type, page, rowsPerPage, filter, field, order }) => {
|
||||
const monitors = await this.db.monitorModule.getMonitorsByTeamId({
|
||||
limit,
|
||||
type,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
teamId,
|
||||
});
|
||||
return monitors;
|
||||
};
|
||||
|
||||
getMonitorsAndSummaryByTeamId = async ({ teamId, type, explain }) => {
|
||||
const result = await this.db.monitorModule.getMonitorsAndSummaryByTeamId({
|
||||
type,
|
||||
explain,
|
||||
teamId,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
getMonitorsWithChecksByTeamId = async ({ teamId, limit, type, page, rowsPerPage, filter, field, order, explain }) => {
|
||||
const result = await this.db.monitorModule.getMonitorsWithChecksByTeamId({
|
||||
limit,
|
||||
type,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
teamId,
|
||||
explain,
|
||||
});
|
||||
return result;
|
||||
};
|
||||
|
||||
exportMonitorsToCSV = async ({ teamId }) => {
|
||||
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) => ({
|
||||
name: monitor.name,
|
||||
description: monitor.description,
|
||||
type: monitor.type,
|
||||
url: monitor.url,
|
||||
interval: monitor.interval,
|
||||
port: monitor.port,
|
||||
ignoreTlsErrors: monitor.ignoreTlsErrors,
|
||||
isActive: monitor.isActive,
|
||||
}));
|
||||
|
||||
const csv = this.papaparse.unparse(csvData);
|
||||
return csv;
|
||||
};
|
||||
exportMonitorsToJSON = async ({ teamId }) => {
|
||||
const monitors = await this.db.monitorModule.getMonitorsByTeamId({ teamId });
|
||||
|
||||
if (!monitors || monitors.length === 0) {
|
||||
throw this.errorService.createNotFoundError("No monitors to export");
|
||||
}
|
||||
|
||||
const json = monitors?.filteredMonitors
|
||||
?.map((monitor) => {
|
||||
const initialType = monitor.type;
|
||||
let parsedType;
|
||||
|
||||
if (initialType === "hardware") {
|
||||
parsedType = "infrastructure";
|
||||
} else if (initialType === "http") {
|
||||
if (monitor.url.startsWith("https://")) {
|
||||
parsedType = "https";
|
||||
} else {
|
||||
parsedType = "http";
|
||||
}
|
||||
} else if (initialType === "pagespeed") {
|
||||
parsedType = initialType;
|
||||
} else {
|
||||
// Skip unsupported types
|
||||
return;
|
||||
}
|
||||
|
||||
return {
|
||||
name: monitor.name,
|
||||
url: monitor.url,
|
||||
type: parsedType,
|
||||
interval: monitor.interval,
|
||||
n: monitor.statusWindowSize,
|
||||
secret: monitor.secret,
|
||||
};
|
||||
})
|
||||
.filter(Boolean);
|
||||
|
||||
return json;
|
||||
};
|
||||
|
||||
getAllGames = () => {
|
||||
return this.games;
|
||||
};
|
||||
|
||||
getGroupsByTeamId = async ({ teamId }) => {
|
||||
const groups = await this.db.monitorModule.getGroupsByTeamId({ teamId });
|
||||
return groups;
|
||||
};
|
||||
}
|
||||
|
||||
export default MonitorService;
|
||||
Reference in new issue
Block a user