mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-24 11:59:39 -05:00
getUptimeDetails
This commit is contained in:
@@ -39,19 +39,6 @@ class MonitorController {
|
||||
}
|
||||
}
|
||||
|
||||
getAllMonitors = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
const monitors = await this.monitorService.getAllMonitors();
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: "Retrieved all monitors successfully",
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
getMonitorCertificate = async (req: Request, res: Response, next: NextFunction) => {
|
||||
try {
|
||||
await getCertificateParamValidation.validateAsync(req.params);
|
||||
|
||||
@@ -194,17 +194,6 @@ class MonitorModule {
|
||||
};
|
||||
};
|
||||
|
||||
getAllMonitors = async () => {
|
||||
try {
|
||||
const monitors = await this.Monitor.find();
|
||||
return monitors;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getAllMonitors";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
getMonitorById = async (monitorId) => {
|
||||
try {
|
||||
const monitor = await this.Monitor.findById(monitorId);
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
import type { LatestChecksMap } from "@/repositories/checks/MongoChecksRepistory.js";
|
||||
|
||||
export interface IChecksRepository {
|
||||
// create
|
||||
// single fetch
|
||||
// collection fetch
|
||||
findLatestChecksByMonitorIds(monitorIds: string[]): Promise<LatestChecksMap>;
|
||||
// update
|
||||
// delete
|
||||
findDateRangeChecksByMonitor(
|
||||
monitorId: string,
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
dateString: string
|
||||
): Promise<{
|
||||
groupedChecks: Array<{ _id: string; avgResponseTime: number; totalChecks: number }>;
|
||||
groupedUpChecks: Array<{ _id: string; totalChecks: number; avgResponseTime: number }>;
|
||||
groupedDownChecks: Array<{ _id: string; totalChecks: number; avgResponseTime: number }>;
|
||||
uptimePercentage: number;
|
||||
avgResponseTime: number;
|
||||
}>;
|
||||
}
|
||||
|
||||
@@ -209,6 +209,106 @@ class MongoChecksRepistory implements IChecksRepository {
|
||||
return acc;
|
||||
}, {});
|
||||
};
|
||||
|
||||
findDateRangeChecksByMonitor = async (
|
||||
monitorId: string,
|
||||
startDate: Date,
|
||||
endDate: Date,
|
||||
dateString: string
|
||||
): Promise<{
|
||||
groupedChecks: Array<{ _id: string; avgResponseTime: number; totalChecks: number }>;
|
||||
groupedUpChecks: Array<{ _id: string; totalChecks: number; avgResponseTime: number }>;
|
||||
groupedDownChecks: Array<{ _id: string; totalChecks: number; avgResponseTime: number }>;
|
||||
uptimePercentage: number;
|
||||
avgResponseTime: number;
|
||||
}> => {
|
||||
const matchStage = {
|
||||
"metadata.monitorId": new mongoose.Types.ObjectId(monitorId),
|
||||
updatedAt: { $gte: startDate, $lte: endDate },
|
||||
};
|
||||
const [result] = await CheckModel.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $sort: { updatedAt: 1 } },
|
||||
{
|
||||
$facet: {
|
||||
uptimePercentage: [
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
upChecks: { $sum: { $cond: [{ $eq: ["$status", true] }, 1, 0] } },
|
||||
totalChecks: { $sum: 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
percentage: {
|
||||
$cond: [{ $eq: ["$totalChecks", 0] }, 0, { $divide: ["$upChecks", "$totalChecks"] }],
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
groupedAvgResponseTime: [
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
avgResponseTime: { $avg: "$responseTime" },
|
||||
},
|
||||
},
|
||||
],
|
||||
groupedChecks: [
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
$dateToString: { format: dateString, date: "$createdAt" },
|
||||
},
|
||||
avgResponseTime: { $avg: "$responseTime" },
|
||||
totalChecks: { $sum: 1 },
|
||||
},
|
||||
},
|
||||
{ $sort: { _id: 1 } },
|
||||
],
|
||||
groupedUpChecks: [
|
||||
{ $match: { status: true } },
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
$dateToString: { format: dateString, date: "$createdAt" },
|
||||
},
|
||||
totalChecks: { $sum: 1 },
|
||||
avgResponseTime: { $avg: "$responseTime" },
|
||||
},
|
||||
},
|
||||
{ $sort: { _id: 1 } },
|
||||
],
|
||||
groupedDownChecks: [
|
||||
{ $match: { status: false } },
|
||||
{
|
||||
$group: {
|
||||
_id: {
|
||||
$dateToString: { format: dateString, date: "$createdAt" },
|
||||
},
|
||||
totalChecks: { $sum: 1 },
|
||||
avgResponseTime: { $avg: "$responseTime" },
|
||||
},
|
||||
},
|
||||
{ $sort: { _id: 1 } },
|
||||
],
|
||||
},
|
||||
},
|
||||
]).exec();
|
||||
|
||||
const uptimePercentage = result?.uptimePercentage?.[0]?.percentage ?? 0;
|
||||
const avgResponseTime = result?.groupedAvgResponseTime?.[0]?.avgResponseTime ?? 0;
|
||||
|
||||
return {
|
||||
groupedChecks: result?.groupedChecks ?? [],
|
||||
groupedUpChecks: result?.groupedUpChecks ?? [],
|
||||
groupedDownChecks: result?.groupedDownChecks ?? [],
|
||||
uptimePercentage,
|
||||
avgResponseTime,
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
export default MongoChecksRepistory;
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface IMonitorsRepository {
|
||||
create(monitor: Monitor, teamId: string, userId: string): Promise<Monitor | null>;
|
||||
createBulkMonitors(monitors: Monitor[]): Promise<Monitor[]>;
|
||||
// single fetch
|
||||
findById(monitorId: string): Promise<Monitor | null>;
|
||||
findById(monitorId: string, teamId?: string): Promise<Monitor | null>;
|
||||
|
||||
// collection fetch
|
||||
findAll(): Promise<Monitor[] | null>;
|
||||
|
||||
@@ -21,12 +21,16 @@ class MongoMonitorsRepository implements IMonitorsRepository {
|
||||
return this.mapDocuments(inserted);
|
||||
};
|
||||
|
||||
findById = async (MonitorModelId: string): Promise<Monitor> => {
|
||||
const monitor = await MonitorModel.findById(MonitorModelId);
|
||||
findById = async (monitorId: string, teamId?: string): Promise<Monitor> => {
|
||||
const match: { _id: string; teamId?: string } = { _id: monitorId };
|
||||
if (teamId) {
|
||||
match.teamId = teamId;
|
||||
}
|
||||
const monitor = await MonitorModel.findOne(match);
|
||||
if (!monitor) {
|
||||
if (monitor === null || monitor === undefined) {
|
||||
throw new AppError({
|
||||
message: `Monitor with ID ${MonitorModelId} not found.`,
|
||||
message: `Monitor with ID ${monitorId} not found.`,
|
||||
status: 404,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ class MonitorRoutes {
|
||||
});
|
||||
|
||||
// General monitor CRUD routes
|
||||
this.router.get("/", this.monitorController.getAllMonitors);
|
||||
this.router.post("/", isAllowed(["admin", "superadmin"]), this.monitorController.createMonitor);
|
||||
this.router.delete("/", isAllowed(["superadmin"]), this.monitorController.deleteAllMonitors);
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { createMonitorsBodyValidation } from "@/validation/joi.js";
|
||||
import { NormalizeData } from "@/utils/dataUtils.js";
|
||||
import { NormalizeData, NormalizeDataUptimeDetails } from "@/utils/dataUtils.js";
|
||||
import { type Monitor } from "@/types/index.js";
|
||||
import type { MonitorType } from "@/types/monitor.js";
|
||||
import type { IMonitorsRepository } from "@/repositories/index.js";
|
||||
import type { IChecksRepository, IMonitorsRepository } from "@/repositories/index.js";
|
||||
import fs from "fs";
|
||||
import { fileURLToPath } from "url";
|
||||
import path from "path";
|
||||
@@ -10,6 +10,7 @@ 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;
|
||||
@@ -21,7 +22,6 @@ export interface IMonitorService {
|
||||
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;
|
||||
@@ -85,7 +85,7 @@ export class MonitorService implements IMonitorService {
|
||||
private errorService: any;
|
||||
private games: any;
|
||||
private monitorsRepository: IMonitorsRepository;
|
||||
private checksRepository: any;
|
||||
private checksRepository: IChecksRepository;
|
||||
private fs: any;
|
||||
|
||||
constructor({
|
||||
@@ -109,7 +109,7 @@ export class MonitorService implements IMonitorService {
|
||||
errorService: any;
|
||||
games: any;
|
||||
monitorsRepository: IMonitorsRepository;
|
||||
checksRepository: any;
|
||||
checksRepository: IChecksRepository;
|
||||
}) {
|
||||
this.db = db;
|
||||
this.jobQueue = jobQueue;
|
||||
@@ -127,6 +127,31 @@ export class MonitorService implements IMonitorService {
|
||||
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)) {
|
||||
@@ -225,11 +250,6 @@ export class MonitorService implements IMonitorService {
|
||||
return demoMonitors;
|
||||
};
|
||||
|
||||
getAllMonitors = async (): Promise<any[]> => {
|
||||
const monitors = await this.db.monitorModule.getAllMonitors();
|
||||
return monitors;
|
||||
};
|
||||
|
||||
getUptimeDetailsById = async ({
|
||||
teamId,
|
||||
monitorId,
|
||||
@@ -242,13 +262,34 @@ export class MonitorService implements IMonitorService {
|
||||
normalize?: boolean;
|
||||
}): Promise<any> => {
|
||||
await this.verifyTeamAccess({ teamId, monitorId });
|
||||
const data = await this.db.monitorModule.getUptimeDetailsById({
|
||||
|
||||
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(
|
||||
monitorId: monitor.id,
|
||||
startDate: start,
|
||||
endDate: end,
|
||||
dateString: this.getDateFormat(rangeKey),
|
||||
);
|
||||
const monitorStats = await this.db.monitorModule.getMonitorStatsById({
|
||||
monitorId,
|
||||
dateRange,
|
||||
normalize,
|
||||
});
|
||||
|
||||
return data;
|
||||
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,
|
||||
};
|
||||
};
|
||||
|
||||
getMonitorStatsById = async ({
|
||||
|
||||
Reference in New Issue
Block a user