From a1c04ba35eacf1e57e230390c7783b1e6fcc7666 Mon Sep 17 00:00:00 2001 From: a-y-a-n-das Date: Thu, 2 Oct 2025 20:43:00 +0530 Subject: [PATCH] fixed the format --- server/src/db/v1/modules/monitorModule.js | 1138 ++++++++++----------- 1 file changed, 569 insertions(+), 569 deletions(-) diff --git a/server/src/db/v1/modules/monitorModule.js b/server/src/db/v1/modules/monitorModule.js index 55cf37799..f795a7f2e 100755 --- a/server/src/db/v1/modules/monitorModule.js +++ b/server/src/db/v1/modules/monitorModule.js @@ -1,578 +1,578 @@ import { - buildUptimeDetailsPipeline, - buildHardwareDetailsPipeline, - buildMonitorSummaryByTeamIdPipeline, - buildMonitorsByTeamIdPipeline, - buildMonitorsAndSummaryByTeamIdPipeline, - buildMonitorsWithChecksByTeamIdPipeline, - buildFilteredMonitorsByTeamIdPipeline, + buildUptimeDetailsPipeline, + buildHardwareDetailsPipeline, + buildMonitorSummaryByTeamIdPipeline, + buildMonitorsByTeamIdPipeline, + buildMonitorsAndSummaryByTeamIdPipeline, + buildMonitorsWithChecksByTeamIdPipeline, + buildFilteredMonitorsByTeamIdPipeline, } from "./monitorModuleQueries.js"; const SERVICE_NAME = "monitorModule"; class MonitorModule { - constructor({ Monitor, MonitorStats, Check, stringService, fs, path, fileURLToPath, ObjectId, NormalizeData, NormalizeDataUptimeDetails }) { - this.Monitor = Monitor; - this.MonitorStats = MonitorStats; - this.Check = Check; - this.stringService = stringService; - this.fs = fs; - this.path = path; - this.fileURLToPath = fileURLToPath; - this.ObjectId = ObjectId; - this.NormalizeData = NormalizeData; - this.NormalizeDataUptimeDetails = NormalizeDataUptimeDetails; - - const __filename = fileURLToPath(import.meta.url); - const __dirname = path.dirname(__filename); - - this.demoMonitorsPath = path.resolve(__dirname, "../../../utils/demoMonitors.json"); - } - - // Helper - calculateUptimeDuration = (checks) => { - if (!checks || checks.length === 0) { - return 0; - } - const latestCheck = new Date(checks[0].createdAt); - let latestDownCheck = 0; - - for (let i = checks.length - 1; i >= 0; i--) { - if (checks[i].status === false) { - latestDownCheck = new Date(checks[i].createdAt); - break; - } - } - - // If no down check is found, uptime is from the last check to now - if (latestDownCheck === 0) { - return Date.now() - new Date(checks[checks.length - 1].createdAt); - } - - // Otherwise the uptime is from the last check to the last down check - return latestCheck - latestDownCheck; - }; - - // Helper - getLastChecked = (checks) => { - if (!checks || checks.length === 0) { - return 0; // Handle case when no checks are available - } - // Data is sorted newest->oldest, so last check is the most recent - return new Date() - new Date(checks[0].createdAt); - }; - getLatestResponseTime = (checks) => { - if (!checks || checks.length === 0) { - return 0; - } - - return checks[0]?.responseTime ?? 0; - }; - - // Helper - getAverageResponseTime = (checks) => { - if (!checks || checks.length === 0) { - return 0; - } - - const validChecks = checks.filter((check) => typeof check.responseTime === "number"); - if (validChecks.length === 0) { - return 0; - } - const aggResponseTime = validChecks.reduce((sum, check) => { - return sum + check.responseTime; - }, 0); - return aggResponseTime / validChecks.length; - }; - - // Helper - getUptimePercentage = (checks) => { - if (!checks || checks.length === 0) { - return 0; - } - const upCount = checks.reduce((count, check) => { - return check.status === true ? count + 1 : count; - }, 0); - return (upCount / checks.length) * 100; - }; - - // Helper - getIncidents = (checks) => { - if (!checks || checks.length === 0) { - return 0; // Handle case when no checks are available - } - return checks.reduce((acc, check) => { - return check.status === false ? (acc += 1) : acc; - }, 0); - }; - - // Helper - getDateRange = (dateRange) => { - 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(), - }; - }; - - //Helper - getMonitorChecks = async (monitorId, dateRange, sortOrder) => { - const indexSpec = { - monitorId: 1, - updatedAt: sortOrder, // This will be 1 or -1 - }; - - const [checksAll, checksForDateRange] = await Promise.all([ - this.Check.find({ monitorId }).sort({ createdAt: sortOrder }).hint(indexSpec).lean(), - this.Check.find({ - monitorId, - createdAt: { $gte: dateRange.start, $lte: dateRange.end }, - }) - .hint(indexSpec) - .lean(), - ]); - - return { checksAll, checksForDateRange }; - }; - - // Helper - processChecksForDisplay = (normalizeData, checks, numToDisplay, normalize) => { - let processedChecks = checks; - if (numToDisplay && checks.length > numToDisplay) { - const n = Math.ceil(checks.length / numToDisplay); - processedChecks = checks.filter((_, index) => index % n === 0); - } - return normalize ? normalizeData(processedChecks, 1, 100) : processedChecks; - }; - - // Helper - groupChecksByTime = (checks, dateRange) => { - return checks.reduce((acc, check) => { - // Validate the date - const checkDate = new Date(check.createdAt); - if (Number.isNaN(checkDate.getTime()) || checkDate.getTime() === 0) { - return acc; - } - - const time = dateRange === "day" ? checkDate.setMinutes(0, 0, 0) : checkDate.toISOString().split("T")[0]; - - if (!acc[time]) { - acc[time] = { time, checks: [] }; - } - acc[time].checks.push(check); - return acc; - }, {}); - }; - - // Helper - calculateGroupStats = (group) => { - const totalChecks = group.checks.length; - - const checksWithResponseTime = group.checks.filter((check) => typeof check.responseTime === "number" && !Number.isNaN(check.responseTime)); - - return { - time: group.time, - uptimePercentage: this.getUptimePercentage(group.checks), - totalChecks, - totalIncidents: group.checks.filter((check) => !check.status).length, - avgResponseTime: - checksWithResponseTime.length > 0 - ? checksWithResponseTime.reduce((sum, check) => sum + check.responseTime, 0) / checksWithResponseTime.length - : 0, - }; - }; - - 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); - if (monitor === null || monitor === undefined) { - const error = new Error(this.stringService.getDbFindMonitorById(monitorId)); - error.status = 404; - throw error; - } - - return monitor; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorById"; - throw error; - } - }; - - getMonitorsByIds = async (monitorIds) => { - try { - const objectIds = monitorIds.map((id) => new this.ObjectId(id)); - return await this.Monitor.find({ _id: { $in: objectIds } }, { _id: 1, teamId: 1 }).lean(); - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorsByIds"; - throw error; - } - }; - getUptimeDetailsById = async ({ monitorId, dateRange }) => { - try { - const dates = this.getDateRange(dateRange); - 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", - }; - - const dateString = formatLookup[dateRange]; - - const results = await this.Check.aggregate(buildUptimeDetailsPipeline(monitorId, dates, dateString)); - - const monitorData = results[0]; - - monitorData.groupedUpChecks = this.NormalizeDataUptimeDetails(monitorData.groupedUpChecks, 10, 100); - - monitorData.groupedDownChecks = this.NormalizeDataUptimeDetails(monitorData.groupedDownChecks, 10, 100); - - const normalizedGroupChecks = this.NormalizeDataUptimeDetails(monitorData.groupedChecks, 10, 100); - - monitorData.groupedChecks = normalizedGroupChecks; - const monitorStats = await this.MonitorStats.findOne({ monitorId }); - return { monitorData, monitorStats }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getUptimeDetailsById"; - throw error; - } - }; - - getMonitorStatsById = async ({ monitorId, sortOrder, dateRange, numToDisplay, normalize }) => { - try { - // Get monitor, if we can't find it, abort with error - const monitor = await this.Monitor.findById(monitorId); - if (monitor === null || monitor === undefined) { - throw new Error(this.stringService.getDbFindMonitorById(monitorId)); - } - - // Get query params - const sort = sortOrder === "asc" ? 1 : -1; - - // Get Checks for monitor in date range requested - const dates = this.getDateRange(dateRange); - const { checksAll, checksForDateRange } = await this.getMonitorChecks(monitorId, dates, sort); - - // Build monitor stats - const monitorStats = { - ...monitor.toObject(), - uptimeDuration: this.calculateUptimeDuration(checksAll), - lastChecked: this.getLastChecked(checksAll), - latestResponseTime: this.getLatestResponseTime(checksAll), - periodIncidents: this.getIncidents(checksForDateRange), - periodTotalChecks: checksForDateRange.length, - checks: this.processChecksForDisplay(this.NormalizeData, checksForDateRange, numToDisplay, normalize), - }; - - if (monitor.type === "http" || monitor.type === "ping" || monitor.type === "docker" || monitor.type === "port" || monitor.type === "game") { - // HTTP/PING Specific stats - monitorStats.periodAvgResponseTime = this.getAverageResponseTime(checksForDateRange); - monitorStats.periodUptime = this.getUptimePercentage(checksForDateRange); - const groupedChecks = this.groupChecksByTime(checksForDateRange, dateRange); - monitorStats.aggregateData = Object.values(groupedChecks).map(this.calculateGroupStats); - } - - return monitorStats; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorStatsById"; - throw error; - } - }; - - getHardwareDetailsById = async ({ monitorId, dateRange }) => { - try { - const monitor = await this.Monitor.findById(monitorId); - const dates = this.getDateRange(dateRange); - - 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", - }; - const dateString = formatLookup[dateRange]; - - const hardwareStats = await this.Check.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString)); - - const stats = hardwareStats[0]; - - return { - ...monitor.toObject(), - stats, - }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getHardwareDetailsById"; - throw error; - } - }; - - getMonitorsByTeamId = async ({ limit, type, page, rowsPerPage, filter, field, order, teamId }) => { - limit = parseInt(limit); - page = parseInt(page); - rowsPerPage = parseInt(rowsPerPage); - if (field === undefined) { - field = "name"; - order = "asc"; - } - // Build match stage - const matchStage = { teamId: new this.ObjectId(teamId) }; - if (type !== undefined) { - matchStage.type = Array.isArray(type) ? { $in: type } : type; - } - - const summaryResult = await this.Monitor.aggregate(buildMonitorSummaryByTeamIdPipeline({ matchStage })); - const summary = summaryResult[0]; - - const monitors = await this.Monitor.aggregate(buildMonitorsByTeamIdPipeline({ matchStage, field, order })); - - const filteredMonitors = await this.Monitor.aggregate( - buildFilteredMonitorsByTeamIdPipeline({ - matchStage, - filter, - page, - rowsPerPage, - field, - order, - limit, - type, - }) - ); - - const normalizedFilteredMonitors = filteredMonitors.map((monitor) => { - if (!monitor.checks) { - return monitor; - } - monitor.checks = this.NormalizeData(monitor.checks, 10, 100); - return monitor; - }); - - return { summary, monitors, filteredMonitors: normalizedFilteredMonitors }; - }; - - getMonitorsAndSummaryByTeamId = async ({ type, explain, teamId }) => { - try { - const matchStage = { teamId: new this.ObjectId(teamId) }; - if (type !== undefined) { - matchStage.type = Array.isArray(type) ? { $in: type } : type; - } - - if (explain === true) { - return this.Monitor.aggregate(buildMonitorsAndSummaryByTeamIdPipeline({ matchStage })).explain("executionStats"); - } - - const queryResult = await this.Monitor.aggregate(buildMonitorsAndSummaryByTeamIdPipeline({ matchStage })); - const { monitors, summary } = queryResult?.[0] ?? {}; - return { monitors, summary }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorsAndSummaryByTeamId"; - throw error; - } - }; - - getMonitorsWithChecksByTeamId = async ({ limit, type, page, rowsPerPage, filter, field, order, teamId, explain }) => { - try { - limit = parseInt(limit); - page = parseInt(page); - rowsPerPage = parseInt(rowsPerPage); - if (field === undefined) { - field = "name"; - order = "asc"; - } - // Build match stage - const matchStage = { teamId: new this.ObjectId(teamId) }; - if (type !== undefined) { - matchStage.type = Array.isArray(type) ? { $in: type } : type; - } - - if (explain === true) { - return this.Monitor.aggregate( - buildMonitorsWithChecksByTeamIdPipeline({ - matchStage, - filter, - page, - rowsPerPage, - field, - order, - limit, - type, - }) - ).explain("executionStats"); - } - - const queryResult = await this.Monitor.aggregate( - buildMonitorsWithChecksByTeamIdPipeline({ - matchStage, - filter, - page, - rowsPerPage, - field, - order, - limit, - type, - }) - ); - const monitors = queryResult[0]?.monitors; - const count = queryResult[0]?.count; - const normalizedFilteredMonitors = monitors.map((monitor) => { - if (!monitor.checks) { - return monitor; - } - monitor.checks = this.NormalizeData(monitor.checks, 10, 100); - return monitor; - }); - return { count, monitors: normalizedFilteredMonitors }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorsWithChecksByTeamId"; - throw error; - } - }; - createMonitor = async ({ body, teamId, userId }) => { - try { - const monitor = new this.Monitor({ ...body, teamId, userId }); - const saved = await monitor.save(); - return saved; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createMonitor"; - throw error; - } - }; - - createBulkMonitors = async (req) => { - try { - const monitors = req.map((item) => new this.Monitor({ ...item, notifications: undefined })); - await this.Monitor.bulkSave(monitors); - return monitors; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "createBulkMonitors"; - throw error; - } - }; - - deleteMonitor = async ({ teamId, monitorId }) => { - try { - const deletedMonitor = await this.Monitor.findOneAndDelete({ _id: monitorId, teamId }); - - if (!deletedMonitor) { - throw new Error(this.stringService.getDbFindMonitorById(monitorId)); - } - - return deletedMonitor; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteMonitor"; - throw error; - } - }; - - deleteAllMonitors = async (teamId) => { - try { - const monitors = await this.Monitor.find({ teamId }); - const { deletedCount } = await this.Monitor.deleteMany({ teamId }); - - return { monitors, deletedCount }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteAllMonitors"; - throw error; - } - }; - - deleteMonitorsByUserId = async (userId) => { - try { - const result = await this.Monitor.deleteMany({ userId: userId }); - return result; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "deleteMonitorsByUserId"; - throw error; - } - }; - - editMonitor = async ({ monitorId, body }) => { - try { - const editedMonitor = await this.Monitor.findByIdAndUpdate(monitorId, body, { - new: true, - }); - return editedMonitor; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "editMonitor"; - throw error; - } - }; - - addDemoMonitors = async (userId, teamId) => { - try { - const demoMonitors = JSON.parse(this.fs.readFileSync(this.demoMonitorsPath, "utf8")); - - const demoMonitorsToInsert = demoMonitors.map((monitor) => { - return { - userId, - teamId, - name: monitor.name, - description: monitor.name, - type: "http", - url: monitor.url, - interval: 60000, - }; - }); - const insertedMonitors = await this.Monitor.insertMany(demoMonitorsToInsert); - return insertedMonitors; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "addDemoMonitors"; - throw error; - } - }; - - pauseMonitor = async ({ monitorId }) => { - try { - const monitor = await this.Monitor.findOneAndUpdate( - { _id: monitorId }, - [ - { - $set: { - isActive: { $not: "$isActive" }, - status: "$$REMOVE", - }, - }, - ], - { new: true } - ); - - return monitor; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "pauseMonitor"; - throw error; - } - }; + constructor({ Monitor, MonitorStats, Check, stringService, fs, path, fileURLToPath, ObjectId, NormalizeData, NormalizeDataUptimeDetails }) { + this.Monitor = Monitor; + this.MonitorStats = MonitorStats; + this.Check = Check; + this.stringService = stringService; + this.fs = fs; + this.path = path; + this.fileURLToPath = fileURLToPath; + this.ObjectId = ObjectId; + this.NormalizeData = NormalizeData; + this.NormalizeDataUptimeDetails = NormalizeDataUptimeDetails; + + const __filename = fileURLToPath(import.meta.url); + const __dirname = path.dirname(__filename); + + this.demoMonitorsPath = path.resolve(__dirname, "../../../utils/demoMonitors.json"); + } + + // Helper + calculateUptimeDuration = (checks) => { + if (!checks || checks.length === 0) { + return 0; + } + const latestCheck = new Date(checks[0].createdAt); + let latestDownCheck = 0; + + for (let i = checks.length - 1; i >= 0; i--) { + if (checks[i].status === false) { + latestDownCheck = new Date(checks[i].createdAt); + break; + } + } + + // If no down check is found, uptime is from the last check to now + if (latestDownCheck === 0) { + return Date.now() - new Date(checks[checks.length - 1].createdAt); + } + + // Otherwise the uptime is from the last check to the last down check + return latestCheck - latestDownCheck; + }; + + // Helper + getLastChecked = (checks) => { + if (!checks || checks.length === 0) { + return 0; // Handle case when no checks are available + } + // Data is sorted newest->oldest, so last check is the most recent + return new Date() - new Date(checks[0].createdAt); + }; + getLatestResponseTime = (checks) => { + if (!checks || checks.length === 0) { + return 0; + } + + return checks[0]?.responseTime ?? 0; + }; + + // Helper + getAverageResponseTime = (checks) => { + if (!checks || checks.length === 0) { + return 0; + } + + const validChecks = checks.filter((check) => typeof check.responseTime === "number"); + if (validChecks.length === 0) { + return 0; + } + const aggResponseTime = validChecks.reduce((sum, check) => { + return sum + check.responseTime; + }, 0); + return aggResponseTime / validChecks.length; + }; + + // Helper + getUptimePercentage = (checks) => { + if (!checks || checks.length === 0) { + return 0; + } + const upCount = checks.reduce((count, check) => { + return check.status === true ? count + 1 : count; + }, 0); + return (upCount / checks.length) * 100; + }; + + // Helper + getIncidents = (checks) => { + if (!checks || checks.length === 0) { + return 0; // Handle case when no checks are available + } + return checks.reduce((acc, check) => { + return check.status === false ? (acc += 1) : acc; + }, 0); + }; + + // Helper + getDateRange = (dateRange) => { + 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(), + }; + }; + + //Helper + getMonitorChecks = async (monitorId, dateRange, sortOrder) => { + const indexSpec = { + monitorId: 1, + updatedAt: sortOrder, // This will be 1 or -1 + }; + + const [checksAll, checksForDateRange] = await Promise.all([ + this.Check.find({ monitorId }).sort({ createdAt: sortOrder }).hint(indexSpec).lean(), + this.Check.find({ + monitorId, + createdAt: { $gte: dateRange.start, $lte: dateRange.end }, + }) + .hint(indexSpec) + .lean(), + ]); + + return { checksAll, checksForDateRange }; + }; + + // Helper + processChecksForDisplay = (normalizeData, checks, numToDisplay, normalize) => { + let processedChecks = checks; + if (numToDisplay && checks.length > numToDisplay) { + const n = Math.ceil(checks.length / numToDisplay); + processedChecks = checks.filter((_, index) => index % n === 0); + } + return normalize ? normalizeData(processedChecks, 1, 100) : processedChecks; + }; + + // Helper + groupChecksByTime = (checks, dateRange) => { + return checks.reduce((acc, check) => { + // Validate the date + const checkDate = new Date(check.createdAt); + if (Number.isNaN(checkDate.getTime()) || checkDate.getTime() === 0) { + return acc; + } + + const time = dateRange === "day" ? checkDate.setMinutes(0, 0, 0) : checkDate.toISOString().split("T")[0]; + + if (!acc[time]) { + acc[time] = { time, checks: [] }; + } + acc[time].checks.push(check); + return acc; + }, {}); + }; + + // Helper + calculateGroupStats = (group) => { + const totalChecks = group.checks.length; + + const checksWithResponseTime = group.checks.filter((check) => typeof check.responseTime === "number" && !Number.isNaN(check.responseTime)); + + return { + time: group.time, + uptimePercentage: this.getUptimePercentage(group.checks), + totalChecks, + totalIncidents: group.checks.filter((check) => !check.status).length, + avgResponseTime: + checksWithResponseTime.length > 0 + ? checksWithResponseTime.reduce((sum, check) => sum + check.responseTime, 0) / checksWithResponseTime.length + : 0, + }; + }; + + 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); + if (monitor === null || monitor === undefined) { + const error = new Error(this.stringService.getDbFindMonitorById(monitorId)); + error.status = 404; + throw error; + } + + return monitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorById"; + throw error; + } + }; + + getMonitorsByIds = async (monitorIds) => { + try { + const objectIds = monitorIds.map((id) => new this.ObjectId(id)); + return await this.Monitor.find({ _id: { $in: objectIds } }, { _id: 1, teamId: 1 }).lean(); + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorsByIds"; + throw error; + } + }; + getUptimeDetailsById = async ({ monitorId, dateRange }) => { + try { + const dates = this.getDateRange(dateRange); + 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", + }; + + const dateString = formatLookup[dateRange]; + + const results = await this.Check.aggregate(buildUptimeDetailsPipeline(monitorId, dates, dateString)); + + const monitorData = results[0]; + + monitorData.groupedUpChecks = this.NormalizeDataUptimeDetails(monitorData.groupedUpChecks, 10, 100); + + monitorData.groupedDownChecks = this.NormalizeDataUptimeDetails(monitorData.groupedDownChecks, 10, 100); + + const normalizedGroupChecks = this.NormalizeDataUptimeDetails(monitorData.groupedChecks, 10, 100); + + monitorData.groupedChecks = normalizedGroupChecks; + const monitorStats = await this.MonitorStats.findOne({ monitorId }); + return { monitorData, monitorStats }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getUptimeDetailsById"; + throw error; + } + }; + + getMonitorStatsById = async ({ monitorId, sortOrder, dateRange, numToDisplay, normalize }) => { + try { + // Get monitor, if we can't find it, abort with error + const monitor = await this.Monitor.findById(monitorId); + if (monitor === null || monitor === undefined) { + throw new Error(this.stringService.getDbFindMonitorById(monitorId)); + } + + // Get query params + const sort = sortOrder === "asc" ? 1 : -1; + + // Get Checks for monitor in date range requested + const dates = this.getDateRange(dateRange); + const { checksAll, checksForDateRange } = await this.getMonitorChecks(monitorId, dates, sort); + + // Build monitor stats + const monitorStats = { + ...monitor.toObject(), + uptimeDuration: this.calculateUptimeDuration(checksAll), + lastChecked: this.getLastChecked(checksAll), + latestResponseTime: this.getLatestResponseTime(checksAll), + periodIncidents: this.getIncidents(checksForDateRange), + periodTotalChecks: checksForDateRange.length, + checks: this.processChecksForDisplay(this.NormalizeData, checksForDateRange, numToDisplay, normalize), + }; + + if (monitor.type === "http" || monitor.type === "ping" || monitor.type === "docker" || monitor.type === "port" || monitor.type === "game") { + // HTTP/PING Specific stats + monitorStats.periodAvgResponseTime = this.getAverageResponseTime(checksForDateRange); + monitorStats.periodUptime = this.getUptimePercentage(checksForDateRange); + const groupedChecks = this.groupChecksByTime(checksForDateRange, dateRange); + monitorStats.aggregateData = Object.values(groupedChecks).map(this.calculateGroupStats); + } + + return monitorStats; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorStatsById"; + throw error; + } + }; + + getHardwareDetailsById = async ({ monitorId, dateRange }) => { + try { + const monitor = await this.Monitor.findById(monitorId); + const dates = this.getDateRange(dateRange); + + 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", + }; + const dateString = formatLookup[dateRange]; + + const hardwareStats = await this.Check.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString)); + + const stats = hardwareStats[0]; + + return { + ...monitor.toObject(), + stats, + }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getHardwareDetailsById"; + throw error; + } + }; + + getMonitorsByTeamId = async ({ limit, type, page, rowsPerPage, filter, field, order, teamId }) => { + limit = parseInt(limit); + page = parseInt(page); + rowsPerPage = parseInt(rowsPerPage); + if (field === undefined) { + field = "name"; + order = "asc"; + } + // Build match stage + const matchStage = { teamId: new this.ObjectId(teamId) }; + if (type !== undefined) { + matchStage.type = Array.isArray(type) ? { $in: type } : type; + } + + const summaryResult = await this.Monitor.aggregate(buildMonitorSummaryByTeamIdPipeline({ matchStage })); + const summary = summaryResult[0]; + + const monitors = await this.Monitor.aggregate(buildMonitorsByTeamIdPipeline({ matchStage, field, order })); + + const filteredMonitors = await this.Monitor.aggregate( + buildFilteredMonitorsByTeamIdPipeline({ + matchStage, + filter, + page, + rowsPerPage, + field, + order, + limit, + type, + }) + ); + + const normalizedFilteredMonitors = filteredMonitors.map((monitor) => { + if (!monitor.checks) { + return monitor; + } + monitor.checks = this.NormalizeData(monitor.checks, 10, 100); + return monitor; + }); + + return { summary, monitors, filteredMonitors: normalizedFilteredMonitors }; + }; + + getMonitorsAndSummaryByTeamId = async ({ type, explain, teamId }) => { + try { + const matchStage = { teamId: new this.ObjectId(teamId) }; + if (type !== undefined) { + matchStage.type = Array.isArray(type) ? { $in: type } : type; + } + + if (explain === true) { + return this.Monitor.aggregate(buildMonitorsAndSummaryByTeamIdPipeline({ matchStage })).explain("executionStats"); + } + + const queryResult = await this.Monitor.aggregate(buildMonitorsAndSummaryByTeamIdPipeline({ matchStage })); + const { monitors, summary } = queryResult?.[0] ?? {}; + return { monitors, summary }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorsAndSummaryByTeamId"; + throw error; + } + }; + + getMonitorsWithChecksByTeamId = async ({ limit, type, page, rowsPerPage, filter, field, order, teamId, explain }) => { + try { + limit = parseInt(limit); + page = parseInt(page); + rowsPerPage = parseInt(rowsPerPage); + if (field === undefined) { + field = "name"; + order = "asc"; + } + // Build match stage + const matchStage = { teamId: new this.ObjectId(teamId) }; + if (type !== undefined) { + matchStage.type = Array.isArray(type) ? { $in: type } : type; + } + + if (explain === true) { + return this.Monitor.aggregate( + buildMonitorsWithChecksByTeamIdPipeline({ + matchStage, + filter, + page, + rowsPerPage, + field, + order, + limit, + type, + }) + ).explain("executionStats"); + } + + const queryResult = await this.Monitor.aggregate( + buildMonitorsWithChecksByTeamIdPipeline({ + matchStage, + filter, + page, + rowsPerPage, + field, + order, + limit, + type, + }) + ); + const monitors = queryResult[0]?.monitors; + const count = queryResult[0]?.count; + const normalizedFilteredMonitors = monitors.map((monitor) => { + if (!monitor.checks) { + return monitor; + } + monitor.checks = this.NormalizeData(monitor.checks, 10, 100); + return monitor; + }); + return { count, monitors: normalizedFilteredMonitors }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorsWithChecksByTeamId"; + throw error; + } + }; + createMonitor = async ({ body, teamId, userId }) => { + try { + const monitor = new this.Monitor({ ...body, teamId, userId }); + const saved = await monitor.save(); + return saved; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "createMonitor"; + throw error; + } + }; + + createBulkMonitors = async (req) => { + try { + const monitors = req.map((item) => new this.Monitor({ ...item, notifications: undefined })); + await this.Monitor.bulkSave(monitors); + return monitors; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "createBulkMonitors"; + throw error; + } + }; + + deleteMonitor = async ({ teamId, monitorId }) => { + try { + const deletedMonitor = await this.Monitor.findOneAndDelete({ _id: monitorId, teamId }); + + if (!deletedMonitor) { + throw new Error(this.stringService.getDbFindMonitorById(monitorId)); + } + + return deletedMonitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteMonitor"; + throw error; + } + }; + + deleteAllMonitors = async (teamId) => { + try { + const monitors = await this.Monitor.find({ teamId }); + const { deletedCount } = await this.Monitor.deleteMany({ teamId }); + + return { monitors, deletedCount }; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteAllMonitors"; + throw error; + } + }; + + deleteMonitorsByUserId = async (userId) => { + try { + const result = await this.Monitor.deleteMany({ userId: userId }); + return result; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "deleteMonitorsByUserId"; + throw error; + } + }; + + editMonitor = async ({ monitorId, body }) => { + try { + const editedMonitor = await this.Monitor.findByIdAndUpdate(monitorId, body, { + new: true, + }); + return editedMonitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "editMonitor"; + throw error; + } + }; + + addDemoMonitors = async (userId, teamId) => { + try { + const demoMonitors = JSON.parse(this.fs.readFileSync(this.demoMonitorsPath, "utf8")); + + const demoMonitorsToInsert = demoMonitors.map((monitor) => { + return { + userId, + teamId, + name: monitor.name, + description: monitor.name, + type: "http", + url: monitor.url, + interval: 60000, + }; + }); + const insertedMonitors = await this.Monitor.insertMany(demoMonitorsToInsert); + return insertedMonitors; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "addDemoMonitors"; + throw error; + } + }; + + pauseMonitor = async ({ monitorId }) => { + try { + const monitor = await this.Monitor.findOneAndUpdate( + { _id: monitorId }, + [ + { + $set: { + isActive: { $not: "$isActive" }, + status: "$$REMOVE", + }, + }, + ], + { new: true } + ); + + return monitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "pauseMonitor"; + throw error; + } + }; } export default MonitorModule;