diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index af4ea7e9f..c3d1a38be 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -2,6 +2,7 @@ import Monitor from "../../models/Monitor.js"; import Check from "../../models/Check.js"; import PageSpeedCheck from "../../models/PageSpeedCheck.js"; import HardwareCheck from "../../models/HardwareCheck.js"; +import DistributedUptimeCheck from "../../models/DistributedUptimeCheck.js"; import { errorMessages } from "../../../utils/messages.js"; import Notification from "../../models/Notification.js"; import { NormalizeData, NormalizeDataUptimeDetails } from "../../../utils/dataUtils.js"; @@ -11,6 +12,7 @@ import { fileURLToPath } from "url"; import { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline, + buildDistributedUptimeDetailsPipeline, } from "./monitorModuleQueries.js"; import { ObjectId } from "mongodb"; const __filename = fileURLToPath(import.meta.url); @@ -366,6 +368,48 @@ const getUptimeDetailsById = async (req) => { } }; +const getDistributedUptimeDetailsById = async (req) => { + try { + const { monitorId } = req.params; + const monitor = await Monitor.findById(monitorId); + if (monitor === null || monitor === undefined) { + throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + } + + const { dateRange, normalize } = req.query; + const dates = getDateRange(dateRange); + const formatLookup = { + day: "%Y-%m-%dT%H:%M:00Z", + week: "%Y-%m-%dT%H:00:00Z", + month: "%Y-%m-%dT00:00:00Z", + }; + + const dateString = formatLookup[dateRange]; + const results = await DistributedUptimeCheck.aggregate( + buildDistributedUptimeDetailsPipeline(monitor, dates, dateString) + ); + + const monitorData = results[0]; + const normalizedGroupChecks = NormalizeDataUptimeDetails( + monitorData.groupedChecks, + 10, + 100 + ); + + const monitorStats = { + ...monitor.toObject(), + ...monitorData, + groupedChecks: normalizedGroupChecks, + }; + + return monitorStats; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getUptimeDetailsById"; + throw error; + } +}; + /** * Get stats by monitor ID * @async @@ -815,6 +859,7 @@ export { getMonitorById, getMonitorsByTeamId, getUptimeDetailsById, + getDistributedUptimeDetailsById, createMonitor, deleteMonitor, deleteAllMonitors, diff --git a/Server/db/mongo/modules/monitorModuleQueries.js b/Server/db/mongo/modules/monitorModuleQueries.js index 094e1f932..489017f51 100644 --- a/Server/db/mongo/modules/monitorModuleQueries.js +++ b/Server/db/mongo/modules/monitorModuleQueries.js @@ -534,4 +534,188 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => { ]; }; -export { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline }; +const buildDistributedUptimeDetailsPipeline = (monitor, dates, dateString) => { + return [ + { + $match: { + monitorId: monitor._id, + }, + }, + { + $sort: { + createdAt: 1, + }, + }, + { + $facet: { + aggregateData: [ + { + $group: { + _id: null, + avgResponseTime: { + $avg: "$responseTime", + }, + lastCheck: { + $last: "$$ROOT", + }, + totalChecks: { + $sum: 1, + }, + uptBurnt: { + $sum: "$uptBurnt", + }, + }, + }, + ], + // For the response time chart, should return checks for date window + // Grouped by: {day: hour}, {week: day}, {month: day} + groupedMapChecks: [ + { + $match: { + createdAt: { $gte: dates.start, $lte: dates.end }, + }, + }, + { + $group: { + _id: { + date: { + $dateToString: { + format: dateString, + date: "$createdAt", + }, + }, + city: "$city", + lat: "$location.lat", + lng: "$location.lng", + }, + city: { $first: "$city" }, // Add this line to include city in output + lat: { $first: "$location.lat" }, + lng: { $first: "$location.lng" }, + avgResponseTime: { + $avg: "$responseTime", + }, + totalChecks: { + $sum: 1, + }, + }, + }, + { + $sort: { + "_id.date": 1, + }, + }, + ], + groupedChecks: [ + { + $match: { + createdAt: { $gte: dates.start, $lte: dates.end }, + }, + }, + { + $group: { + _id: { + $dateToString: { + format: dateString, + date: "$createdAt", + }, + }, + avgResponseTime: { + $avg: "$responseTime", + }, + totalChecks: { + $sum: 1, + }, + }, + }, + { + $sort: { + _id: 1, + }, + }, + ], + // Average response time for the date window + groupAvgResponseTime: [ + { + $match: { + createdAt: { $gte: dates.start, $lte: dates.end }, + }, + }, + { + $group: { + _id: null, + avgResponseTime: { + $avg: "$responseTime", + }, + }, + }, + ], + latestChecks: [ + { + $sort: { createdAt: -1 }, // Sort by newest first + }, + { + $limit: 5, // Get only the first 5 documents + }, + { + $project: { + responseTime: 1, + city: 1, + countryCode: 1, + uptBurnt: { $toString: "$uptBurnt" }, + }, + }, + ], + }, + }, + { + $project: { + totalUptBurnt: { + $toString: { + $arrayElemAt: ["$aggregateData.uptBurnt", 0], + }, + }, + avgResponseTime: { + $arrayElemAt: ["$aggregateData.avgResponseTime", 0], + }, + totalChecks: { + $arrayElemAt: ["$aggregateData.totalChecks", 0], + }, + latestResponseTime: { + $arrayElemAt: ["$aggregateData.lastCheck.responseTime", 0], + }, + timeSinceLastCheck: { + $let: { + vars: { + lastCheck: { + $arrayElemAt: ["$aggregateData.lastCheck", 0], + }, + }, + in: { + $cond: [ + { + $ifNull: ["$$lastCheck", false], + }, + { + $subtract: [new Date(), "$$lastCheck.createdAt"], + }, + 0, + ], + }, + }, + }, + groupedMapChecks: "$groupedMapChecks", + groupedChecks: "$groupedChecks", + groupedAvgResponseTime: { + $arrayElemAt: ["$groupAvgResponseTime", 0], + }, + latestChecks: "$latestChecks", + }, + }, + ]; +}; + +export { + buildUptimeDetailsPipeline, + buildHardwareDetailsPipeline, + buildDistributedUptimeDetailsPipeline, +};