mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-20 08:28:48 -05:00
monitorSerivce return types
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { type MonitorType, type Monitor } from "@/types/index.js";
|
||||
import { type MonitorType, type Monitor, type MonitorsSummary } from "@/types/index.js";
|
||||
|
||||
export interface TeamQueryConfig {
|
||||
limit?: number;
|
||||
@@ -37,6 +37,6 @@ export interface IMonitorsRepository {
|
||||
findMonitorCountByTeamIdAndType(teamId: string, config: TeamQueryConfig): Promise<number>;
|
||||
|
||||
// other
|
||||
findMonitorsSummaryByTeamId(teamId: string, config?: SummaryConfig): Promise<any>;
|
||||
findMonitorsSummaryByTeamId(teamId: string, config?: SummaryConfig): Promise<MonitorsSummary>;
|
||||
findGroupsByTeamId(teamId: string): Promise<string[]>;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { MonitorModel } from "@/db/models/index.js";
|
||||
import type { MonitorDocument } from "@/db/models/index.js";
|
||||
import type { Monitor } from "@/types/index.js";
|
||||
import type { Monitor, MonitorsSummary } from "@/types/index.js";
|
||||
import mongoose, { type FilterQuery } from "mongoose";
|
||||
import type { IMonitorsRepository, TeamQueryConfig, SummaryConfig } from "./IMonitorsRepository.js";
|
||||
import { AppError } from "@/utils/AppError.js";
|
||||
@@ -149,7 +149,7 @@ class MongoMonitorsRepository implements IMonitorsRepository {
|
||||
findMonitorsSummaryByTeamId = async (
|
||||
teamId: string,
|
||||
config?: SummaryConfig
|
||||
): Promise<{ totalMonitors: number; upMonitors: number; downMonitors: number; pausedMonitors: number }> => {
|
||||
): Promise<MonitorsSummary> => {
|
||||
const match: FilterQuery<MonitorDocument> = { teamId: new mongoose.Types.ObjectId(teamId) };
|
||||
if (config?.type !== undefined) {
|
||||
match.type = Array.isArray(config.type) ? { $in: config.type } : config.type;
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
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 { MonitorType, MonitorsWithChecksByTeamIdResult, UptimeDetailsResult, HardwareDetailsResult, PageSpeedDetailsResult } from "@/types/monitor.js";
|
||||
import type { IChecksRepository, IMonitorsRepository, IMonitorStatsRepository, IStatusPagesRepository } from "@/repositories/index.js";
|
||||
import fs from "fs";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
|
||||
import { AppError } from "../infrastructure/errorService.js";
|
||||
import { ISuperSimpleQueue } from "../infrastructure/SuperSimpleQueue/SuperSimpleQueue.js";
|
||||
|
||||
const SERVICE_NAME = "MonitorService";
|
||||
type DateRangeKey = "recent" | "day" | "week" | "month" | "all";
|
||||
@@ -17,13 +18,13 @@ export interface IMonitorService {
|
||||
|
||||
// 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[]>;
|
||||
createBulkMonitors(fileData: string, userId: string, teamId: string): Promise<Monitor[]>;
|
||||
addDemoMonitors(args: { userId: string; teamId: string }): Promise<Monitor[]>;
|
||||
|
||||
// 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>;
|
||||
getUptimeDetailsById(args: { teamId: string; monitorId: string; dateRange: string; normalize?: boolean }): Promise<UptimeDetailsResult>;
|
||||
getHardwareDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<HardwareDetailsResult>;
|
||||
getPageSpeedDetailsById(args: { teamId: string; monitorId: string; dateRange: string }): Promise<PageSpeedDetailsResult>;
|
||||
getMonitorById(args: { teamId: string; monitorId: string }): Promise<Monitor>;
|
||||
getMonitorsByTeamId(args: {
|
||||
teamId: string;
|
||||
@@ -34,7 +35,7 @@ export interface IMonitorService {
|
||||
filter?: string;
|
||||
field?: string;
|
||||
order?: "asc" | "desc";
|
||||
}): Promise<any>;
|
||||
}): Promise<Monitor[] | null>;
|
||||
getMonitorsWithChecksByTeamId(args: {
|
||||
teamId: string;
|
||||
limit?: number;
|
||||
@@ -45,7 +46,7 @@ export interface IMonitorService {
|
||||
field?: string;
|
||||
order?: "asc" | "desc";
|
||||
explain?: boolean;
|
||||
}): Promise<{ summary: any; count: number; monitors: any[] }>;
|
||||
}): Promise<MonitorsWithChecksByTeamIdResult>;
|
||||
getAllGames(): any;
|
||||
getGroupsByTeamId(args: { teamId: string }): Promise<string[]>;
|
||||
|
||||
@@ -59,13 +60,13 @@ export interface IMonitorService {
|
||||
|
||||
// other
|
||||
sendTestEmail(args: { to: string }): Promise<string>;
|
||||
exportMonitorsToJSON(args: { teamId: string }): Promise<any[]>;
|
||||
exportMonitorsToJSON(args: { teamId: string }): Promise<Monitor[]>;
|
||||
}
|
||||
|
||||
export class MonitorService implements IMonitorService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
private jobQueue: any;
|
||||
private jobQueue: ISuperSimpleQueue;
|
||||
private emailService: any;
|
||||
private papaparse: any;
|
||||
private logger: any;
|
||||
@@ -88,7 +89,7 @@ export class MonitorService implements IMonitorService {
|
||||
monitorStatsRepository,
|
||||
statusPagesRepository,
|
||||
}: {
|
||||
jobQueue: any;
|
||||
jobQueue: ISuperSimpleQueue;
|
||||
emailService: any;
|
||||
papaparse: any;
|
||||
logger: any;
|
||||
@@ -149,10 +150,10 @@ export class MonitorService implements IMonitorService {
|
||||
this.jobQueue.addJob(monitor.id, monitor);
|
||||
};
|
||||
|
||||
createBulkMonitors = async (fileData: string, userId: string, teamId: string): Promise<any> => {
|
||||
createBulkMonitors = async (fileData: string, userId: string, teamId: string): Promise<Monitor[]> => {
|
||||
const { parse } = this.papaparse;
|
||||
|
||||
return new Promise<any>((resolve, reject) => {
|
||||
return new Promise<Monitor[]>((resolve, reject) => {
|
||||
parse(fileData, {
|
||||
header: true,
|
||||
skipEmptyLines: true,
|
||||
@@ -241,7 +242,7 @@ export class MonitorService implements IMonitorService {
|
||||
monitorId: string;
|
||||
dateRange: string;
|
||||
normalize?: boolean;
|
||||
}): Promise<any> => {
|
||||
}): Promise<UptimeDetailsResult> => {
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
if (!monitor) {
|
||||
throw new AppError({ message: `Monitor with ID ${monitorId} not found.`, status: 404 });
|
||||
@@ -276,7 +277,7 @@ export class MonitorService implements IMonitorService {
|
||||
};
|
||||
};
|
||||
|
||||
getHardwareDetailsById = async ({ teamId, monitorId, dateRange }: { teamId: string; monitorId: string; dateRange: string }): Promise<any> => {
|
||||
getHardwareDetailsById = async ({ teamId, monitorId, dateRange }: { teamId: string; monitorId: string; dateRange: string }): Promise<HardwareDetailsResult> => {
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
if (!monitor) {
|
||||
throw new AppError({ message: `Monitor with ID ${monitorId} not found.`, status: 404 });
|
||||
@@ -307,7 +308,7 @@ export class MonitorService implements IMonitorService {
|
||||
};
|
||||
};
|
||||
|
||||
getPageSpeedDetailsById = async ({ teamId, monitorId, dateRange }: { teamId: string; monitorId: string; dateRange: string }): Promise<any> => {
|
||||
getPageSpeedDetailsById = async ({ teamId, monitorId, dateRange }: { teamId: string; monitorId: string; dateRange: string }): Promise<PageSpeedDetailsResult> => {
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
if (!monitor) {
|
||||
throw new AppError({ message: `Monitor with ID ${monitorId} not found.`, status: 404 });
|
||||
@@ -336,12 +337,12 @@ export class MonitorService implements IMonitorService {
|
||||
monitorStats,
|
||||
};
|
||||
};
|
||||
getMonitorById = async ({ teamId, monitorId }: { teamId: string; monitorId: string }): Promise<any> => {
|
||||
getMonitorById = async ({ teamId, monitorId }: { teamId: string; monitorId: string }): Promise<Monitor> => {
|
||||
const monitor = await this.monitorsRepository.findById(monitorId, teamId);
|
||||
return monitor;
|
||||
};
|
||||
|
||||
getMonitorsByTeamId = async ({ teamId, type, filter }: { teamId: string; type?: MonitorType | MonitorType[]; filter?: string }): Promise<any> => {
|
||||
getMonitorsByTeamId = async ({ teamId, type, filter }: { teamId: string; type?: MonitorType | MonitorType[]; filter?: string }): Promise<Monitor[] | null> => {
|
||||
const monitors = await this.monitorsRepository.findByTeamId(teamId, {
|
||||
type,
|
||||
filter,
|
||||
@@ -369,7 +370,7 @@ export class MonitorService implements IMonitorService {
|
||||
field?: string;
|
||||
order?: "asc" | "desc";
|
||||
explain?: boolean;
|
||||
}): Promise<{ summary: any; count: number; monitors: any[] }> => {
|
||||
}): Promise<MonitorsWithChecksByTeamIdResult> => {
|
||||
const summary = await this.monitorsRepository.findMonitorsSummaryByTeamId(teamId);
|
||||
const count = await this.monitorsRepository.findMonitorCountByTeamIdAndType(teamId, { type, filter });
|
||||
const monitors = await this.monitorsRepository.findByTeamId(teamId, {
|
||||
|
||||
@@ -156,11 +156,17 @@ export interface HardwareChecksResult {
|
||||
}>;
|
||||
}
|
||||
|
||||
export interface GroupedCheck {
|
||||
_id: string;
|
||||
avgResponseTime: number;
|
||||
totalChecks: number;
|
||||
}
|
||||
|
||||
export interface UptimeChecksResult {
|
||||
monitorType: Exclude<MonitorType, "hardware" | "pagespeed">;
|
||||
groupedChecks: Array<{ _id: string; avgResponseTime: number; totalChecks: number }>;
|
||||
groupedUpChecks: Array<{ _id: string; totalChecks: number; avgResponseTime: number }>;
|
||||
groupedDownChecks: Array<{ _id: string; totalChecks: number; avgResponseTime: number }>;
|
||||
groupedChecks: GroupedCheck[];
|
||||
groupedUpChecks: GroupedCheck[];
|
||||
groupedDownChecks: GroupedCheck[];
|
||||
uptimePercentage: number;
|
||||
avgResponseTime: number;
|
||||
}
|
||||
@@ -171,3 +177,11 @@ export interface ChecksSummary {
|
||||
downChecks: number;
|
||||
cannotResolveChecks: number;
|
||||
}
|
||||
|
||||
export type NormalizedCheck<T extends Check = Check> = T & {
|
||||
originalResponseTime: number;
|
||||
};
|
||||
|
||||
export type NormalizedUptimeCheck<T extends GroupedCheck = GroupedCheck> = T & {
|
||||
originalAvgResponseTime: number;
|
||||
};
|
||||
|
||||
@@ -44,3 +44,78 @@ export interface Monitor {
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
}
|
||||
|
||||
export interface MonitorsSummary {
|
||||
totalMonitors: number;
|
||||
upMonitors: number;
|
||||
downMonitors: number;
|
||||
pausedMonitors: number;
|
||||
}
|
||||
|
||||
export interface MonitorWithChecks extends Monitor {
|
||||
checks: import("./check.js").Check[];
|
||||
}
|
||||
|
||||
export interface MonitorsWithChecksByTeamIdResult {
|
||||
summary: MonitorsSummary | null;
|
||||
count: number;
|
||||
monitors: MonitorWithChecks[];
|
||||
}
|
||||
|
||||
export interface UptimeDetailsResult {
|
||||
monitorData: {
|
||||
monitor: Monitor;
|
||||
groupedChecks: import("./check.js").GroupedCheck[];
|
||||
groupedUpChecks: import("./check.js").GroupedCheck[];
|
||||
groupedDownChecks: import("./check.js").GroupedCheck[];
|
||||
groupedAvgResponseTime: number;
|
||||
groupedUptimePercentage: number;
|
||||
};
|
||||
monitorStats: import("./monitorStats.js").MonitorStats | null;
|
||||
}
|
||||
|
||||
export interface HardwareDetailsResult extends Monitor {
|
||||
stats: {
|
||||
aggregateData: {
|
||||
latestCheck: import("./check.js").Check | null;
|
||||
totalChecks: number;
|
||||
};
|
||||
upChecks: {
|
||||
totalChecks: number;
|
||||
};
|
||||
checks: Array<{
|
||||
_id: string;
|
||||
avgCpuUsage: number;
|
||||
avgMemoryUsage: number;
|
||||
avgTemperature: number[];
|
||||
disks: Array<{
|
||||
name: string;
|
||||
readSpeed: number;
|
||||
writeSpeed: number;
|
||||
totalBytes: number;
|
||||
freeBytes: number;
|
||||
usagePercent: number;
|
||||
}>;
|
||||
net: Array<{
|
||||
name: string;
|
||||
bytesSentPerSecond: number;
|
||||
deltaBytesRecv: number;
|
||||
deltaPacketsSent: number;
|
||||
deltaPacketsRecv: number;
|
||||
deltaErrIn: number;
|
||||
deltaErrOut: number;
|
||||
deltaDropIn: number;
|
||||
deltaDropOut: number;
|
||||
deltaFifoIn: number;
|
||||
deltaFifoOut: number;
|
||||
}>;
|
||||
}>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface PageSpeedDetailsResult {
|
||||
monitor: Monitor & {
|
||||
checks: import("./check.js").Check[];
|
||||
};
|
||||
monitorStats: import("./monitorStats.js").MonitorStats | null;
|
||||
}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
const calculatePercentile = (arr, percentile) => {
|
||||
import type { Check, GroupedCheck, NormalizedCheck, NormalizedUptimeCheck } from "@/types/index.js";
|
||||
|
||||
|
||||
const calculatePercentile = (arr: Check[], percentile: number): number => {
|
||||
const sorted = arr.slice().sort((a, b) => a.responseTime - b.responseTime);
|
||||
const index = (percentile / 100) * (sorted.length - 1);
|
||||
const lower = Math.floor(index);
|
||||
const upper = lower + 1;
|
||||
const weight = index % 1;
|
||||
if (upper >= sorted.length) return sorted[lower].responseTime;
|
||||
return sorted[lower].responseTime * (1 - weight) + sorted[upper].responseTime * weight;
|
||||
if (upper >= sorted.length) return sorted[lower]!.responseTime;
|
||||
return sorted[lower]!.responseTime * (1 - weight) + sorted[upper]!.responseTime * weight;
|
||||
};
|
||||
|
||||
const calculatePercentileUptimeDetails = (arr, percentile) => {
|
||||
const calculatePercentileUptimeDetails = (arr: GroupedCheck[], percentile: number): number => {
|
||||
const sorted = arr.slice().sort((a, b) => a.avgResponseTime - b.avgResponseTime);
|
||||
const index = (percentile / 100) * (sorted.length - 1);
|
||||
const lower = Math.floor(index);
|
||||
const upper = lower + 1;
|
||||
const weight = index % 1;
|
||||
if (upper >= sorted.length) return sorted[lower].avgResponseTime;
|
||||
return sorted[lower].avgResponseTime * (1 - weight) + sorted[upper].avgResponseTime * weight;
|
||||
if (upper >= sorted.length) return sorted[lower]!.avgResponseTime;
|
||||
return sorted[lower]!.avgResponseTime * (1 - weight) + sorted[upper]!.avgResponseTime * weight;
|
||||
};
|
||||
|
||||
const NormalizeData = (checks, rangeMin, rangeMax) => {
|
||||
export const NormalizeData = <T extends Check>(checks: T[], rangeMin: number, rangeMax: number): NormalizedCheck<T>[] => {
|
||||
if (checks.length > 1) {
|
||||
// Get the 5th and 95th percentile
|
||||
const min = calculatePercentile(checks, 0);
|
||||
const max = calculatePercentile(checks, 95);
|
||||
const normalizedChecks = checks.map((check) => {
|
||||
const originalResponseTime = check.responseTime;
|
||||
// Normalize the response time between 1 and 100
|
||||
let normalizedResponseTime = rangeMin + ((check.responseTime - min) * (rangeMax - rangeMin)) / (max - min);
|
||||
|
||||
// Put a floor on the response times so we don't have extreme outliers
|
||||
// Better visuals
|
||||
normalizedResponseTime = Math.max(rangeMin, Math.min(rangeMax, normalizedResponseTime));
|
||||
return {
|
||||
...check,
|
||||
@@ -45,19 +44,16 @@ const NormalizeData = (checks, rangeMin, rangeMax) => {
|
||||
});
|
||||
}
|
||||
};
|
||||
const NormalizeDataUptimeDetails = (checks, rangeMin, rangeMax) => {
|
||||
|
||||
export const NormalizeDataUptimeDetails = <T extends GroupedCheck>(checks: T[], rangeMin: number, rangeMax: number): NormalizedUptimeCheck<T>[] => {
|
||||
if (checks.length > 1) {
|
||||
// Get the 5th and 95th percentile
|
||||
const min = calculatePercentileUptimeDetails(checks, 0);
|
||||
const max = calculatePercentileUptimeDetails(checks, 95);
|
||||
|
||||
const normalizedChecks = checks.map((check) => {
|
||||
const originalResponseTime = check.avgResponseTime;
|
||||
// Normalize the response time between 1 and 100
|
||||
let normalizedResponseTime = rangeMin + ((check.avgResponseTime - min) * (rangeMax - rangeMin)) / (max - min);
|
||||
|
||||
// Put a floor on the response times so we don't have extreme outliers
|
||||
// Better visuals
|
||||
normalizedResponseTime = Math.max(rangeMin, Math.min(rangeMax, normalizedResponseTime));
|
||||
return {
|
||||
...check,
|
||||
@@ -69,32 +65,7 @@ const NormalizeDataUptimeDetails = (checks, rangeMin, rangeMax) => {
|
||||
return normalizedChecks;
|
||||
} else {
|
||||
return checks.map((check) => {
|
||||
return { ...check, originalResponseTime: check.responseTime };
|
||||
return { ...check, originalAvgResponseTime: check.avgResponseTime };
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const safelyParseFloat = (value, defaultValue = 0) => {
|
||||
if (value === null || typeof value === "undefined") {
|
||||
return defaultValue;
|
||||
}
|
||||
const stringValue = String(value).trim();
|
||||
|
||||
if (typeof value === "number" && !isNaN(value)) {
|
||||
return value;
|
||||
}
|
||||
|
||||
if (stringValue === "") {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
const parsedValue = parseFloat(stringValue);
|
||||
|
||||
if (isNaN(parsedValue) || !isFinite(parsedValue)) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return parsedValue;
|
||||
};
|
||||
|
||||
export { safelyParseFloat, calculatePercentile, NormalizeData, calculatePercentileUptimeDetails, NormalizeDataUptimeDetails };
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,13 +0,0 @@
|
||||
export const ROLES = {
|
||||
SUPERADMIN: "superadmin",
|
||||
ADMIN: "admin",
|
||||
USER: "user",
|
||||
DEMO: "demo",
|
||||
};
|
||||
|
||||
export const VALID_ROLES = [ROLES.ADMIN, ROLES.USER, ROLES.DEMO];
|
||||
|
||||
export const EDITABLE_ROLES = [
|
||||
{ role: ROLES.ADMIN, _id: ROLES.ADMIN },
|
||||
{ role: ROLES.USER, _id: ROLES.USER },
|
||||
];
|
||||
@@ -1,5 +1,5 @@
|
||||
import joi from "joi";
|
||||
import { ROLES, VALID_ROLES } from "../utils/roleUtils.js";
|
||||
import { UserRoles } from "@/types/user.js";
|
||||
|
||||
//****************************************
|
||||
// Custom Validators
|
||||
@@ -689,7 +689,7 @@ const editUserByIdBodyValidation = joi.object({
|
||||
email: joi.string().email().required(),
|
||||
role: joi
|
||||
.array()
|
||||
.items(joi.string().valid(...VALID_ROLES))
|
||||
.items(joi.string().valid(...UserRoles))
|
||||
.min(1)
|
||||
.required(),
|
||||
});
|
||||
@@ -700,7 +700,7 @@ const editSuperadminUserByIdBodyValidation = joi.object({
|
||||
email: joi.string().email().required(),
|
||||
role: joi
|
||||
.array()
|
||||
.items(joi.string().valid(...VALID_ROLES, ROLES.SUPERADMIN))
|
||||
.items(joi.string().valid(...UserRoles))
|
||||
.min(1)
|
||||
.required(),
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user