diff --git a/server/src/config/controllers.ts b/server/src/config/controllers.ts index 9fe834348..44b051580 100644 --- a/server/src/config/controllers.ts +++ b/server/src/config/controllers.ts @@ -10,8 +10,8 @@ import StatusPageController from "../controllers/statusPageController.js"; import NotificationController from "../controllers/notificationController.js"; import DiagnosticController from "../controllers/diagnosticController.js"; import IncidentController from "../controllers/incidentController.js"; - -export const initializeControllers = (services: any) => { +import type { InitializedSerivces } from "@/config/services.js"; +export const initializeControllers = (services: InitializedSerivces) => { const controllers: Record = {}; controllers.authController = new AuthController(services.userService); @@ -26,7 +26,7 @@ export const initializeControllers = (services: any) => { controllers.queueController = new QueueController(services.jobQueue); controllers.logController = new LogController(services.logger); controllers.statusPageController = new StatusPageController(services.db); - controllers.notificationController = new NotificationController(services.notificationService, services.db); + controllers.notificationController = new NotificationController(services.notificationService, services.db, services.monitorsRepository); controllers.diagnosticController = new DiagnosticController(services.diagnosticService); controllers.incidentController = new IncidentController(services.incidentService); diff --git a/server/src/config/services.ts b/server/src/config/services.ts index 990dc63b3..8ebda0e16 100644 --- a/server/src/config/services.ts +++ b/server/src/config/services.ts @@ -63,16 +63,61 @@ import CheckModule from "../db/modules/checkModule.js"; import StatusPageModule from "../db/modules/statusPageModule.js"; import UserModule from "../db/modules/userModule.js"; import MaintenanceWindowModule from "../db/modules/maintenanceWindowModule.js"; -import MonitorModule from "../db/modules/monitorModule.js"; import NotificationModule from "../db/modules/notificationModule.js"; import RecoveryModule from "../db/modules/recoveryModule.js"; import SettingsModule from "../db/modules/settingsModule.js"; import IncidentModule from "../db/modules/incidentModule.js"; // repositories -import { MongoMonitorsRepository, MongoChecksRepository, MongoMonitorStatsRepository, MongoStatusPagesRepository } from "@/repositories/index.js"; +import { + MongoMonitorsRepository, + MongoChecksRepository, + MongoMonitorStatsRepository, + MongoStatusPagesRepository, + IMonitorsRepository, + IChecksRepository, + IMonitorStatsRepository, + IStatusPagesRepository, +} from "@/repositories/index.js"; -export const initializeServices = async ({ logger, envSettings, settingsService }: { logger: any; envSettings: any; settingsService: any }) => { +export type InitializedSerivces = { + //v1 + settingsService: any; + translationService: any; + stringService: any; + db: any; + networkService: any; + emailService: any; + bufferService: any; + statusService: any; + notificationService: any; + jobQueue: any; + userService: any; + checkService: any; + diagnosticService: any; + inviteService: any; + maintenanceWindowService: any; + monitorService: any; + incidentService: any; + errorService: any; + logger: any; + + // Repositories + monitorsRepository: IMonitorsRepository; + checksRepository: IChecksRepository; + monitorStatsRepository: IMonitorStatsRepository; + statusPagesRepository: IStatusPagesRepository; +}; + +export const initializeServices = async ({ + logger, + envSettings, + settingsService, +}: { + logger: any; + envSettings: any; + settingsService: any; +}): Promise => { const serviceRegistry = new ServiceRegistry({ logger }); (ServiceRegistry as any).instance = serviceRegistry; @@ -87,17 +132,6 @@ export const initializeServices = async ({ logger, envSettings, settingsService const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, stringService }); const userModule = new UserModule({ User, Team, GenerateAvatarImage, ParseBoolean, stringService }); const maintenanceWindowModule = new MaintenanceWindowModule({ MaintenanceWindow }); - const monitorModule = new MonitorModule({ - Monitor, - MonitorStats, - stringService, - fs, - path, - fileURLToPath, - ObjectId, - NormalizeData, - NormalizeDataUptimeDetails, - }); const notificationModule = new NotificationModule({ Notification, Monitor }); const recoveryModule = new RecoveryModule({ User, RecoveryToken, crypto, stringService }); const settingsModule = new SettingsModule({ AppSettings }); @@ -111,7 +145,6 @@ export const initializeServices = async ({ logger, envSettings, settingsService statusPageModule, userModule, maintenanceWindowModule, - monitorModule, notificationModule, recoveryModule, settingsModule, @@ -202,6 +235,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService settingsService, stringService, errorService, + monitorsRepository, }); const diagnosticService = new DiagnosticService(); const inviteService = new InviteService({ @@ -216,6 +250,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService settingsService, stringService, errorService, + monitorsRepository, }); const monitorService = new MonitorService({ jobQueue: superSimpleQueue, @@ -252,6 +287,12 @@ export const initializeServices = async ({ logger, envSettings, settingsService incidentService, errorService, logger, + + // Repositories + monitorsRepository, + checksRepository, + monitorStatsRepository, + statusPagesRepository, }; Object.values(services).forEach((service) => { diff --git a/server/src/controllers/notificationController.ts b/server/src/controllers/notificationController.ts index 74849c6aa..defae2fd3 100644 --- a/server/src/controllers/notificationController.ts +++ b/server/src/controllers/notificationController.ts @@ -2,6 +2,7 @@ import { Request, Response, NextFunction } from "express"; import { createNotificationBodyValidation } from "@/validation/joi.js"; import { AppError } from "@/utils/AppError.js"; +import { IMonitorsRepository } from "@/repositories/index.js"; const SERVICE_NAME = "NotificationController"; @@ -9,9 +10,11 @@ class NotificationController { static SERVICE_NAME = SERVICE_NAME; private db: any; private notificationService: any; - constructor(notificationService: any, db: any) { + private monitorsRepository: IMonitorsRepository; + constructor(notificationService: any, db: any, monitorsRepository: IMonitorsRepository) { this.notificationService = notificationService; this.db = db; + this.monitorsRepository = monitorsRepository; } get serviceName() { @@ -167,11 +170,7 @@ class NotificationController { throw new AppError({ message: "Team ID is required", status: 400 }); } - const monitor = await this.db.monitorModule.getMonitorById(monitorId); - - if (!monitor.teamId.equals(teamId)) { - throw new AppError({ message: "Unauthorized", status: 403 }); - } + const monitor = await this.monitorsRepository.findById(monitorId, teamId); const notifications = monitor.notifications; if (notifications.length === 0) { diff --git a/server/src/db/MongoDB.js b/server/src/db/MongoDB.js index 3db17c78f..f7e58f34e 100755 --- a/server/src/db/MongoDB.js +++ b/server/src/db/MongoDB.js @@ -12,7 +12,6 @@ class MongoDB { statusPageModule, userModule, maintenanceWindowModule, - monitorModule, notificationModule, recoveryModule, settingsModule, @@ -25,7 +24,6 @@ class MongoDB { this.recoveryModule = recoveryModule; this.checkModule = checkModule; this.maintenanceWindowModule = maintenanceWindowModule; - this.monitorModule = monitorModule; this.notificationModule = notificationModule; this.settingsModule = settingsModule; this.statusPageModule = statusPageModule; diff --git a/server/src/db/modules/monitorModule.js b/server/src/db/modules/monitorModule.js deleted file mode 100755 index e13da4b59..000000000 --- a/server/src/db/modules/monitorModule.js +++ /dev/null @@ -1,539 +0,0 @@ -import { - buildUptimeDetailsPipeline, - buildMonitorSummaryByTeamIdPipeline, - buildMonitorsByTeamIdPipeline, - buildMonitorsAndSummaryByTeamIdPipeline, - buildMonitorsWithChecksByTeamIdPipeline, - buildFilteredMonitorsByTeamIdPipeline, - getHardwareStats, - getUpChecks, - getAggregateData, -} from "./monitorModuleQueries.js"; - -import { CheckModel } from "@/db/models/index.js"; - -const SERVICE_NAME = "monitorModule"; - -class MonitorModule { - constructor({ Monitor, MonitorStats, stringService, fs, path, fileURLToPath, ObjectId, NormalizeData, NormalizeDataUptimeDetails }) { - this.Monitor = Monitor; - this.MonitorStats = MonitorStats; - 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 objectId = new this.ObjectId(monitorId); - const indexSpec = { - "metadata.monitorId": 1, - updatedAt: sortOrder, - }; - - const matchBase = { "metadata.monitorId": objectId }; - - const [checksAll, checksForDateRange] = await Promise.all([ - CheckModel.find(matchBase).sort({ createdAt: sortOrder }).hint(indexSpec).lean(), - CheckModel.find({ - ...matchBase, - 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, - }; - }; - - 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 CheckModel.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; - } - }; - - 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 [aggregateData, upChecksCount, metrics] = await Promise.all([ - getAggregateData(monitorId, dates), - getUpChecks(monitorId, dates), - getHardwareStats(monitorId, dates, dateString), - ]); - - const stats = { - aggregateData: aggregateData, - upChecks: upChecksCount, - checks: metrics, - }; - - return { - ...monitor.toObject(), - stats, - }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getHardwareDetailsById"; - throw error; - } - }; - - getMonitorsByTeamId = async ({ teamId, type, filter }) => { - try { - const matchStage = { teamId: new this.ObjectId(teamId) }; - if (type !== undefined) { - matchStage.type = Array.isArray(type) ? { $in: type } : type; - } - if (filter !== undefined && filter !== null && filter !== "") { - matchStage.$or = [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }]; - } - const monitors = await this.Monitor.find(matchStage) - .sort({ name: 1 }) - .select({ - _id: 1, - name: 1, - type: 1, - url: 1, - status: 1, - isActive: 1, - teamId: 1, - }) - .lean(); - return monitors; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getMonitorsByTeamId"; - throw error; - } - }; - - findMonitorsSummaryByTeamId = async ({ type, teamId, explain }) => { - try { - const matchStage = { teamId: new this.ObjectId(teamId) }; - if (type !== undefined) { - matchStage.type = Array.isArray(type) ? { $in: type } : type; - } - - const pipeline = buildMonitorsSummaryByTeamIdPipeline({ matchStage }); - if (explain === true) { - return this.Monitor.aggregate(pipeline).explain("executionStats"); - } - const [summary] = await this.Monitor.aggregate(pipeline); - return summary ?? { totalMonitors: 0, upMonitors: 0, downMonitors: 0, pausedMonitors: 0 }; - } catch (error) { - error.service = SERVICE_NAME; - error.method = "findMonitorsSummaryByTeamId"; - 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; - } - }; - - getGroupsByTeamId = async ({ teamId }) => { - try { - const groups = await this.Monitor.distinct("group", { - teamId: new this.ObjectId(teamId), - group: { $ne: null, $ne: "" }, - }); - - return groups.filter(Boolean).sort(); - } catch (error) { - error.service = SERVICE_NAME; - error.method = "getGroupsByTeamId"; - throw error; - } - }; -} - -export default MonitorModule; diff --git a/server/src/db/modules/monitorModuleQueries.js b/server/src/db/modules/monitorModuleQueries.js deleted file mode 100755 index 596f11791..000000000 --- a/server/src/db/modules/monitorModuleQueries.js +++ /dev/null @@ -1,740 +0,0 @@ -import { ObjectId } from "mongodb"; -import { CheckModel } from "@/db/models/index.js"; - -const buildUptimeDetailsPipeline = (monitorId, dates, dateString) => { - return [ - { - $match: { - "metadata.monitorId": new ObjectId(monitorId), - updatedAt: { $gte: dates.start, $lte: dates.end }, - }, - }, - { - $sort: { - updatedAt: 1, - }, - }, - { - $facet: { - // For the response time chart, should return checks for date window - // Grouped by: {day: hour}, {week: day}, {month: day} - 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, - }, - }, - ], - // Up checks grouped by: {day: hour}, {week: day}, {month: day} - groupedUpChecks: [ - { - $match: { - status: true, - }, - }, - { - $group: { - _id: { - $dateToString: { - format: dateString, - date: "$createdAt", - }, - }, - totalChecks: { - $sum: 1, - }, - avgResponseTime: { - $avg: "$responseTime", - }, - }, - }, - { - $sort: { _id: 1 }, - }, - ], - // Down checks grouped by: {day: hour}, {week: day}, {month: day} for the date window - groupedDownChecks: [ - { - $match: { - status: false, - }, - }, - { - $group: { - _id: { - $dateToString: { - format: dateString, - date: "$createdAt", - }, - }, - totalChecks: { - $sum: 1, - }, - avgResponseTime: { - $avg: "$responseTime", - }, - }, - }, - { - $sort: { _id: 1 }, - }, - ], - }, - }, - { - $lookup: { - from: "monitors", - let: { monitor_id: { $toObjectId: monitorId } }, - pipeline: [ - { - $match: { - $expr: { $eq: ["$_id", "$$monitor_id"] }, - }, - }, - { - $project: { - _id: 1, - teamId: 1, - name: 1, - status: 1, - interval: 1, - type: 1, - url: 1, - isActive: 1, - notifications: 1, - }, - }, - ], - as: "monitor", - }, - }, - { - $project: { - groupedAvgResponseTime: { - $arrayElemAt: ["$groupedAvgResponseTime.avgResponseTime", 0], - }, - - groupedChecks: "$groupedChecks", - groupedUpChecks: "$groupedUpChecks", - groupedDownChecks: "$groupedDownChecks", - groupedUptimePercentage: { $arrayElemAt: ["$uptimePercentage.percentage", 0] }, - monitor: { $arrayElemAt: ["$monitor", 0] }, - }, - }, - ]; -}; - -const buildMonitorStatsPipeline = (monitor) => { - return [ - { - $match: { - monitorId: monitor._id, - }, - }, - { - $project: { - avgResponseTime: 1, - uptimePercentage: 1, - totalChecks: 1, - timeSinceLastCheck: { - $subtract: [Date.now(), "$lastCheckTimestamp"], - }, - lastCheckTimestamp: 1, - uptBurnt: { $toString: "$uptBurnt" }, - }, - }, - ]; -}; - -const buildMonitorSummaryByTeamIdPipeline = ({ matchStage }) => { - return [ - { $match: matchStage }, - { - $group: { - _id: null, - totalMonitors: { $sum: 1 }, - upMonitors: { - $sum: { - $cond: [{ $eq: ["$status", true] }, 1, 0], - }, - }, - downMonitors: { - $sum: { - $cond: [{ $eq: ["$status", false] }, 1, 0], - }, - }, - pausedMonitors: { - $sum: { - $cond: [{ $eq: ["$isActive", false] }, 1, 0], - }, - }, - }, - }, - { - $project: { - _id: 0, - }, - }, - ]; -}; - -const buildMonitorsByTeamIdPipeline = ({ matchStage, field, order }) => { - const sort = { [field]: order === "asc" ? 1 : -1 }; - - return [ - { $match: matchStage }, - { $sort: sort }, - { - $project: { - _id: 1, - name: 1, - type: 1, - port: 1, - }, - }, - ]; -}; - -const buildMonitorsAndSummaryByTeamIdPipeline = ({ matchStage }) => { - return [ - { $match: matchStage }, - { - $facet: { - summary: [ - { - $group: { - _id: null, - totalMonitors: { $sum: 1 }, - upMonitors: { - $sum: { - $cond: [{ $eq: ["$status", true] }, 1, 0], - }, - }, - downMonitors: { - $sum: { - $cond: [{ $eq: ["$status", false] }, 1, 0], - }, - }, - pausedMonitors: { - $sum: { - $cond: [{ $eq: ["$isActive", false] }, 1, 0], - }, - }, - }, - }, - { - $project: { - _id: 0, - }, - }, - ], - monitors: [ - { $sort: { name: 1 } }, - { - $project: { - _id: 1, - name: 1, - type: 1, - }, - }, - ], - }, - }, - { - $project: { - summary: { $arrayElemAt: ["$summary", 0] }, - monitors: 1, - }, - }, - ]; -}; - -const buildMonitorsWithChecksByTeamIdPipeline = ({ matchStage, filter, page, rowsPerPage, field, order, limit, type }) => { - const skip = page && rowsPerPage ? page * rowsPerPage : 0; - const sort = { [field]: order === "asc" ? 1 : -1 }; - const limitStage = rowsPerPage ? [{ $limit: rowsPerPage }] : []; - - // Match name - if (typeof filter !== "undefined" && field === "name") { - matchStage.$or = [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }]; - } - - // Match isActive - if (typeof filter !== "undefined" && field === "isActive") { - matchStage.isActive = filter === "true" ? true : false; - } - - if (typeof filter !== "undefined" && field === "status") { - matchStage.status = filter === "true" ? true : false; - } - - // Match type - if (typeof filter !== "undefined" && field === "type") { - matchStage.type = filter; - } - - const monitorsPipeline = [ - { $sort: sort }, - { $skip: skip }, - ...limitStage, - { - $project: { - _id: 1, - name: 1, - description: 1, - type: 1, - url: 1, - isActive: 1, - createdAt: 1, - updatedAt: 1, - uptimePercentage: 1, - status: 1, - }, - }, - ]; - - // Add checks - if (limit) { - const checksCollection = "checks"; - monitorsPipeline.push({ - $lookup: { - from: checksCollection, - let: { monitorId: "$_id" }, - pipeline: [ - { - $match: { - $expr: { $eq: ["$monitorId", "$$monitorId"] }, - }, - }, - { $sort: { updatedAt: -1 } }, - { $limit: limit }, - { - $project: { - _id: 1, - status: 1, - responseTime: 1, - statusCode: 1, - createdAt: 1, - updatedAt: 1, - originalResponseTime: 1, - }, - }, - ], - as: "checks", - }, - }); - } - - const pipeline = [ - { $match: matchStage }, - { - $facet: { - count: [{ $count: "monitorsCount" }], - monitors: monitorsPipeline, - }, - }, - { - $project: { - count: { $arrayElemAt: ["$count", 0] }, - monitors: 1, - }, - }, - ]; - return pipeline; -}; - -const buildFilteredMonitorsByTeamIdPipeline = ({ matchStage, filter, page, rowsPerPage, field, order, limit, type }) => { - const skip = page && rowsPerPage ? page * rowsPerPage : 0; - const sort = { [field]: order === "asc" ? 1 : -1 }; - const limitStage = rowsPerPage ? [{ $limit: rowsPerPage }] : []; - - if (typeof filter !== "undefined" && field === "name") { - matchStage.$or = [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }]; - } - - if (typeof filter !== "undefined" && field === "status") { - matchStage.status = filter === "true"; - } - - const pipeline = [{ $match: matchStage }, { $sort: sort }, { $skip: skip }, ...limitStage]; - - // Add checks - if (limit) { - const checksCollection = "checks"; - - pipeline.push({ - $lookup: { - from: checksCollection, - let: { monitorId: "$_id" }, - pipeline: [ - { - $match: { - $expr: { $eq: ["$monitorId", "$$monitorId"] }, - }, - }, - { $sort: { createdAt: -1 } }, - { $limit: limit }, - ], - as: "checks", - }, - }); - } - - return pipeline; -}; - -const buildGetMonitorsByTeamIdPipeline = (req) => { - let { limit, type, page, rowsPerPage, filter, field, order } = req.query; - - limit = parseInt(limit); - page = parseInt(page); - rowsPerPage = parseInt(rowsPerPage); - if (field === undefined) { - field = "name"; - order = "asc"; - } - // Build the match stage - const matchStage = { teamId: new ObjectId(req.params.teamId) }; - if (type !== undefined) { - matchStage.type = Array.isArray(type) ? { $in: type } : type; - } - - const skip = page && rowsPerPage ? page * rowsPerPage : 0; - const sort = { [field]: order === "asc" ? 1 : -1 }; - return [ - { $match: matchStage }, - { - $facet: { - summary: [ - { - $group: { - _id: null, - totalMonitors: { $sum: 1 }, - upMonitors: { - $sum: { - $cond: [{ $eq: ["$status", true] }, 1, 0], - }, - }, - downMonitors: { - $sum: { - $cond: [{ $eq: ["$status", false] }, 1, 0], - }, - }, - pausedMonitors: { - $sum: { - $cond: [{ $eq: ["$isActive", false] }, 1, 0], - }, - }, - }, - }, - { - $project: { - _id: 0, - }, - }, - ], - monitors: [ - { $sort: sort }, - { - $project: { - _id: 1, - name: 1, - }, - }, - ], - filteredMonitors: [ - ...(filter !== undefined - ? [ - { - $match: { - $or: [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }], - }, - }, - ] - : []), - { $sort: sort }, - { $skip: skip }, - ...(rowsPerPage ? [{ $limit: rowsPerPage }] : []), - ...(limit - ? [ - { - $lookup: { - from: "checks", - let: { monitorId: "$_id" }, - pipeline: [ - { - $match: { - $expr: { $eq: ["$monitorId", "$$monitorId"] }, - }, - }, - { $sort: { createdAt: -1 } }, - ...(limit ? [{ $limit: limit }] : []), - ], - as: "standardchecks", - }, - }, - ] - : []), - - { - $addFields: { - checks: { - $switch: { - branches: [ - { - case: { $in: ["$type", ["http", "ping", "docker", "port", "game"]] }, - then: "$standardchecks", - }, - ], - default: [], - }, - }, - }, - }, - { - $project: { - standardchecks: 0, - }, - }, - ], - }, - }, - { - $project: { - summary: { $arrayElemAt: ["$summary", 0] }, - filteredMonitors: 1, - monitors: 1, - }, - }, - ]; -}; - -export { - buildUptimeDetailsPipeline, - buildMonitorStatsPipeline, - buildGetMonitorsByTeamIdPipeline, - buildMonitorSummaryByTeamIdPipeline, - buildMonitorsByTeamIdPipeline, - buildMonitorsAndSummaryByTeamIdPipeline, - buildMonitorsWithChecksByTeamIdPipeline, - buildFilteredMonitorsByTeamIdPipeline, -}; - -export const getAggregateData = async (monitorId, dates) => { - const result = await CheckModel.aggregate([ - { - $match: { - "metadata.monitorId": new ObjectId(monitorId), - "metadata.type": "hardware", - createdAt: { $gte: dates.start, $lte: dates.end }, - }, - }, - { $sort: { createdAt: -1 } }, - { - $group: { - _id: null, - latestCheck: { $first: "$$ROOT" }, - totalChecks: { $sum: 1 }, - }, - }, - ]); - return result[0] || { totalChecks: 0, latestCheck: null }; -}; - -export const getUpChecks = async (monitorId, dates) => { - const count = await CheckModel.countDocuments({ - "metadata.monitorId": new ObjectId(monitorId), - "metadata.type": "hardware", - createdAt: { $gte: dates.start, $lte: dates.end }, - status: true, - }); - return { totalChecks: count }; -}; - -export const getHardwareStats = async (monitorId, dates, dateString) => { - return await CheckModel.aggregate([ - { - $match: { - "metadata.monitorId": new ObjectId(monitorId), - "metadata.type": "hardware", - createdAt: { $gte: dates.start, $lte: dates.end }, - }, - }, - { $sort: { createdAt: 1 } }, - { - $group: { - _id: { $dateToString: { format: dateString, date: "$createdAt" } }, - avgCpuUsage: { $avg: "$cpu.usage_percent" }, - avgMemoryUsage: { $avg: "$memory.usage_percent" }, - avgTemperatures: { $push: { $ifNull: ["$cpu.temperature", [0]] } }, - disks: { $push: "$disk" }, - net: { $push: "$net" }, - updatedAts: { $push: "$updatedAt" }, - sampleDoc: { $first: "$$ROOT" }, - }, - }, - { - $project: { - _id: 1, - avgCpuUsage: 1, - avgMemoryUsage: 1, - avgTemperature: { - $map: { - input: { $range: [0, { $size: { $ifNull: [{ $arrayElemAt: ["$avgTemperatures", 0] }, [0]] } }] }, - as: "idx", - in: { $avg: { $map: { input: "$avgTemperatures", as: "t", in: { $arrayElemAt: ["$$t", "$$idx"] } } } }, - }, - }, - disks: { - $map: { - input: { $range: [0, { $size: { $ifNull: ["$sampleDoc.disk", []] } }] }, - as: "dIdx", - in: { - name: { $concat: ["disk", { $toString: "$$dIdx" }] }, - readSpeed: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.read_speed_bytes", "$$dIdx"] } } } }, - writeSpeed: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.write_speed_bytes", "$$dIdx"] } } } }, - totalBytes: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.total_bytes", "$$dIdx"] } } } }, - freeBytes: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.free_bytes", "$$dIdx"] } } } }, - usagePercent: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.usage_percent", "$$dIdx"] } } } }, - }, - }, - }, - net: { - $map: { - input: { $range: [0, { $size: { $ifNull: ["$sampleDoc.net", []] } }] }, - as: "nIdx", - in: { - name: { $arrayElemAt: ["$sampleDoc.net.name", "$$nIdx"] }, - bytesSentPerSecond: { - $let: { - vars: { - tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, - f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.bytes_sent" } }, "$$nIdx"] }, - l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.bytes_sent" } }, "$$nIdx"] }, - }, - in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, - }, - }, - deltaBytesRecv: { - $let: { - vars: { - tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, - f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.bytes_recv" } }, "$$nIdx"] }, - l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.bytes_recv" } }, "$$nIdx"] }, - }, - in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, - }, - }, - deltaPacketsSent: { - $let: { - vars: { - tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, - f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.packets_sent" } }, "$$nIdx"] }, - l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.packets_sent" } }, "$$nIdx"] }, - }, - in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, - }, - }, - deltaPacketsRecv: { - $let: { - vars: { - tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, - f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.packets_recv" } }, "$$nIdx"] }, - l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.packets_recv" } }, "$$nIdx"] }, - }, - in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, - }, - }, - deltaErrIn: { - $let: { - vars: { - tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, - f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.err_in" } }, "$$nIdx"] }, - l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.err_in" } }, "$$nIdx"] }, - }, - in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, - }, - }, - deltaErrOut: { - $let: { - vars: { - tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, - f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.err_out" } }, "$$nIdx"] }, - l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.err_out" } }, "$$nIdx"] }, - }, - in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, - }, - }, - deltaDropIn: { - $let: { - vars: { - tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, - f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.drop_in" } }, "$$nIdx"] }, - l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.drop_in" } }, "$$nIdx"] }, - }, - in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, - }, - }, - deltaDropOut: { - $let: { - vars: { - tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, - f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.drop_out" } }, "$$nIdx"] }, - l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.drop_out" } }, "$$nIdx"] }, - }, - in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, - }, - }, - }, - }, - }, - }, - }, - { $sort: { _id: 1 } }, - ]); -}; diff --git a/server/src/repositories/checks/MongoChecksRepistory.ts b/server/src/repositories/checks/MongoChecksRepistory.ts index 615754933..3816d8cc5 100644 --- a/server/src/repositories/checks/MongoChecksRepistory.ts +++ b/server/src/repositories/checks/MongoChecksRepistory.ts @@ -15,13 +15,11 @@ import type { } from "@/types/index.js"; import { CheckModel, type CheckDocument } from "@/db/models/index.js"; import mongoose from "mongoose"; -import { - getAggregateData as getHardwareAggregateData, - getHardwareStats, - getUpChecks as getHardwareUpChecks, -} from "@/db/modules/monitorModuleQueries.js"; export type LatestChecksMap = Record; +type DateRange = { start: Date; end: Date }; +type HardwareAggregateData = { latestCheck: CheckDocument | null; totalChecks: number }; +type HardwareUpChecks = { totalChecks: number }; class MongoChecksRepository implements IChecksRepository { private toEntity = (doc: CheckDocument): Check => { @@ -323,9 +321,9 @@ class MongoChecksRepository implements IChecksRepository { const monitorId = monitorObjectId.toHexString(); const dates = { start: startDate, end: endDate }; const [aggregateDataDoc, upChecksDoc, hardwareMetrics] = await Promise.all([ - getHardwareAggregateData(monitorId, dates), - getHardwareUpChecks(monitorId, dates), - getHardwareStats(monitorId, dates, dateString), + this.getHardwareAggregateData(monitorId, dates), + this.getHardwareUpChecks(monitorId, dates), + this.getHardwareStats(monitorId, dates, dateString), ]); const aggregateData = { @@ -387,9 +385,183 @@ class MongoChecksRepository implements IChecksRepository { }; deleteByMonitorId = async (monitorId: string): Promise => { - const result = await CheckModel.deleteMany({ "metadata.monitorId": monitorId }); + const result = await CheckModel.deleteMany({ "metadata.monitorId": new mongoose.Types.ObjectId(monitorId) }); return result.deletedCount; }; + + private getHardwareAggregateData = async (monitorId: string, dates: DateRange): Promise => { + const result = await CheckModel.aggregate([ + { + $match: { + "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), + "metadata.type": "hardware", + createdAt: { $gte: dates.start, $lte: dates.end }, + }, + }, + { $sort: { createdAt: -1 } }, + { + $group: { + _id: null, + latestCheck: { $first: "$$ROOT" }, + totalChecks: { $sum: 1 }, + }, + }, + ]); + return result[0] || { totalChecks: 0, latestCheck: null }; + }; + + private getHardwareUpChecks = async (monitorId: string, dates: DateRange): Promise => { + const count = await CheckModel.countDocuments({ + "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), + "metadata.type": "hardware", + createdAt: { $gte: dates.start, $lte: dates.end }, + status: true, + }); + return { totalChecks: count }; + }; + + private getHardwareStats = async (monitorId: string, dates: DateRange, dateString: string) => { + return await CheckModel.aggregate([ + { + $match: { + "metadata.monitorId": new mongoose.Types.ObjectId(monitorId), + "metadata.type": "hardware", + createdAt: { $gte: dates.start, $lte: dates.end }, + }, + }, + { $sort: { createdAt: 1 } }, + { + $group: { + _id: { $dateToString: { format: dateString, date: "$createdAt" } }, + avgCpuUsage: { $avg: "$cpu.usage_percent" }, + avgMemoryUsage: { $avg: "$memory.usage_percent" }, + avgTemperatures: { $push: { $ifNull: ["$cpu.temperature", [0]] } }, + disks: { $push: "$disk" }, + net: { $push: "$net" }, + updatedAts: { $push: "$updatedAt" }, + sampleDoc: { $first: "$$ROOT" }, + }, + }, + { + $project: { + _id: 1, + avgCpuUsage: 1, + avgMemoryUsage: 1, + avgTemperature: { + $map: { + input: { $range: [0, { $size: { $ifNull: [{ $arrayElemAt: ["$avgTemperatures", 0] }, [0]] } }] }, + as: "idx", + in: { $avg: { $map: { input: "$avgTemperatures", as: "t", in: { $arrayElemAt: ["$$t", "$$idx"] } } } }, + }, + }, + disks: { + $map: { + input: { $range: [0, { $size: { $ifNull: ["$sampleDoc.disk", []] } }] }, + as: "dIdx", + in: { + name: { $concat: ["disk", { $toString: "$$dIdx" }] }, + readSpeed: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.read_speed_bytes", "$$dIdx"] } } } }, + writeSpeed: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.write_speed_bytes", "$$dIdx"] } } } }, + totalBytes: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.total_bytes", "$$dIdx"] } } } }, + freeBytes: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.free_bytes", "$$dIdx"] } } } }, + usagePercent: { $avg: { $map: { input: "$disks", as: "dA", in: { $arrayElemAt: ["$$dA.usage_percent", "$$dIdx"] } } } }, + }, + }, + }, + net: { + $map: { + input: { $range: [0, { $size: { $ifNull: ["$sampleDoc.net", []] } }] }, + as: "nIdx", + in: { + name: { $arrayElemAt: ["$sampleDoc.net.name", "$$nIdx"] }, + bytesSentPerSecond: { + $let: { + vars: { + tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, + f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.bytes_sent" } }, "$$nIdx"] }, + l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.bytes_sent" } }, "$$nIdx"] }, + }, + in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, + }, + }, + deltaBytesRecv: { + $let: { + vars: { + tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, + f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.bytes_recv" } }, "$$nIdx"] }, + l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.bytes_recv" } }, "$$nIdx"] }, + }, + in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, + }, + }, + deltaPacketsSent: { + $let: { + vars: { + tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, + f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.packets_sent" } }, "$$nIdx"] }, + l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.packets_sent" } }, "$$nIdx"] }, + }, + in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, + }, + }, + deltaPacketsRecv: { + $let: { + vars: { + tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, + f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.packets_recv" } }, "$$nIdx"] }, + l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.packets_recv" } }, "$$nIdx"] }, + }, + in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, + }, + }, + deltaErrIn: { + $let: { + vars: { + tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, + f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.err_in" } }, "$$nIdx"] }, + l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.err_in" } }, "$$nIdx"] }, + }, + in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, + }, + }, + deltaErrOut: { + $let: { + vars: { + tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, + f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.err_out" } }, "$$nIdx"] }, + l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.err_out" } }, "$$nIdx"] }, + }, + in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, + }, + }, + deltaDropIn: { + $let: { + vars: { + tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, + f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.drop_in" } }, "$$nIdx"] }, + l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.drop_in" } }, "$$nIdx"] }, + }, + in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, + }, + }, + deltaDropOut: { + $let: { + vars: { + tDiff: { $divide: [{ $subtract: [{ $last: "$updatedAts" }, { $first: "$updatedAts" }] }, 1000] }, + f: { $arrayElemAt: [{ $map: { input: { $first: "$net" }, as: "i", in: "$$i.drop_out" } }, "$$nIdx"] }, + l: { $arrayElemAt: [{ $map: { input: { $last: "$net" }, as: "i", in: "$$i.drop_out" } }, "$$nIdx"] }, + }, + in: { $cond: [{ $gt: ["$$tDiff", 0] }, { $divide: [{ $subtract: ["$$l", "$$f"] }, "$$tDiff"] }, 0] }, + }, + }, + }, + }, + }, + }, + }, + { $sort: { _id: 1 } }, + ]); + }; } export default MongoChecksRepository; diff --git a/server/src/repositories/monitors/IMonitorsRepository.ts b/server/src/repositories/monitors/IMonitorsRepository.ts index 06c57ddb9..29ebe37be 100644 --- a/server/src/repositories/monitors/IMonitorsRepository.ts +++ b/server/src/repositories/monitors/IMonitorsRepository.ts @@ -19,11 +19,12 @@ export interface IMonitorsRepository { create(monitor: Monitor, teamId: string, userId: string): Promise; createBulkMonitors(monitors: Monitor[]): Promise; // single fetch - findById(monitorId: string, teamId?: string): Promise; + findById(monitorId: string, teamId: string): Promise; // collection fetch findAll(): Promise; findByTeamId(teamId: string, config: TeamQueryConfig): Promise; + findByIds(monitorIds: string[]): Promise; // update updateById(monitorId: string, teamId: string, updates: Partial): Promise; diff --git a/server/src/repositories/monitors/MongoMonitorsRepository.ts b/server/src/repositories/monitors/MongoMonitorsRepository.ts index 52f423154..878d6f7cf 100644 --- a/server/src/repositories/monitors/MongoMonitorsRepository.ts +++ b/server/src/repositories/monitors/MongoMonitorsRepository.ts @@ -21,19 +21,11 @@ class MongoMonitorsRepository implements IMonitorsRepository { return this.mapDocuments(inserted); }; - findById = async (monitorId: string, teamId?: string): Promise => { - const match: { _id: string; teamId?: string } = { _id: monitorId }; - if (teamId) { - match.teamId = teamId; - } + findById = async (monitorId: string, teamId: string): Promise => { + const match: { _id: string; teamId: string } = { _id: monitorId, teamId }; const monitor = await MonitorModel.findOne(match); if (!monitor) { - if (monitor === null || monitor === undefined) { - throw new AppError({ - message: `Monitor with ID ${monitorId} not found.`, - status: 404, - }); - } + throw new AppError({ message: `Monitor with ID ${monitorId} not found`, status: 404 }); } return this.toEntity(monitor); }; @@ -81,6 +73,12 @@ class MongoMonitorsRepository implements IMonitorsRepository { return this.mapDocuments(documents); }; + findByIds = async (monitorIds: string[]): Promise => { + const objectIds = monitorIds.map((id) => new mongoose.Types.ObjectId(id)); + const monitors = await MonitorModel.find({ _id: { $in: objectIds } }); + return this.mapDocuments(monitors); + }; + findMonitorCountByTeamIdAndType = async (teamId: string, config?: TeamQueryConfig): Promise => { const { type } = config ?? {}; diff --git a/server/src/repositories/status-pages/MongoStatusPagesRepository.ts b/server/src/repositories/status-pages/MongoStatusPagesRepository.ts index 6beb23f0e..e1eb4715b 100644 --- a/server/src/repositories/status-pages/MongoStatusPagesRepository.ts +++ b/server/src/repositories/status-pages/MongoStatusPagesRepository.ts @@ -1,47 +1,60 @@ import { IStatusPagesRepository } from "@/repositories/index.js"; import { type StatusPageDocument, StatusPageModel } from "@/db/models/StatusPage.js"; -import type { StatusPage } from "@/types/statusPage.js"; +import type { StatusPage, StatusPageLogo } from "@/types/statusPage.js"; import mongoose from "mongoose"; class MongoStatusPagesRepository implements IStatusPagesRepository { - private toEntity(doc: StatusPageDocument): StatusPage { - const toStringId = (value: unknown): string => { - if (value instanceof mongoose.Types.ObjectId) { - return value.toString(); - } - return value?.toString() ?? ""; - }; + private toStringId = (value?: mongoose.Types.ObjectId | string | null): string => { + if (!value) { + return ""; + } + return value instanceof mongoose.Types.ObjectId ? value.toString() : String(value); + }; - const toDateString = (value: Date | string): string => { - return value instanceof Date ? value.toISOString() : value; - }; + private toDateString = (value?: Date | string | null): string => { + if (!value) { + return new Date(0).toISOString(); + } + return value instanceof Date ? value.toISOString() : new Date(value).toISOString(); + }; - const mapIdArray = (values?: mongoose.Types.ObjectId[]): string[] => { - return values?.map((value) => toStringId(value)) ?? []; - }; + private mapIdArray = (values?: Array): string[] => { + return values?.map((value) => this.toStringId(value)) ?? []; + }; + private mapLogo = (logo?: StatusPageLogo | null): StatusPageLogo | undefined => { + if (!logo) { + return undefined; + } return { - id: toStringId(doc._id), - userId: toStringId(doc.userId), - teamId: toStringId(doc.teamId), + data: logo.data, + contentType: logo.contentType, + }; + }; + + private toEntity = (doc: StatusPageDocument): StatusPage => { + return { + id: this.toStringId(doc._id), + userId: this.toStringId(doc.userId), + teamId: this.toStringId(doc.teamId), type: doc.type, companyName: doc.companyName, url: doc.url, timezone: doc.timezone ?? undefined, color: doc.color, - monitors: mapIdArray(doc.monitors), - subMonitors: mapIdArray(doc.subMonitors), - originalMonitors: doc.originalMonitors?.map((value) => toStringId(value)), - logo: doc.logo ?? undefined, + monitors: this.mapIdArray(doc.monitors), + subMonitors: this.mapIdArray(doc.subMonitors), + originalMonitors: this.mapIdArray(doc.originalMonitors), + logo: this.mapLogo(doc.logo), isPublished: doc.isPublished, showCharts: doc.showCharts, showUptimePercentage: doc.showUptimePercentage, showAdminLoginLink: doc.showAdminLoginLink, customCSS: doc.customCSS, - createdAt: toDateString(doc.createdAt), - updatedAt: toDateString(doc.updatedAt), + createdAt: this.toDateString(doc.createdAt), + updatedAt: this.toDateString(doc.updatedAt), }; - } + }; removeMonitorFromStatusPages = async (monitorId: string): Promise => { const res = await StatusPageModel.updateMany({ monitors: monitorId }, { $pull: { monitors: monitorId } }); diff --git a/server/src/service/business/checkService.js b/server/src/service/business/checkService.ts similarity index 61% rename from server/src/service/business/checkService.js rename to server/src/service/business/checkService.ts index 9beb43326..61e2fb6ad 100644 --- a/server/src/service/business/checkService.js +++ b/server/src/service/business/checkService.ts @@ -1,20 +1,41 @@ +import { IMonitorsRepository } from "@/repositories/index.js"; + const SERVICE_NAME = "checkService"; class CheckService { static SERVICE_NAME = SERVICE_NAME; - constructor({ db, settingsService, stringService, errorService }) { + private db: any; + private settingsService: any; + private stringService: any; + private errorService: any; + private monitorsRepository: IMonitorsRepository; + + constructor({ + db, + settingsService, + stringService, + errorService, + monitorsRepository, + }: { + db: any; + settingsService: any; + stringService: any; + errorService: any; + monitorsRepository: IMonitorsRepository; + }) { this.db = db; this.settingsService = settingsService; this.stringService = stringService; this.errorService = errorService; + this.monitorsRepository = monitorsRepository; } get serviceName() { return CheckService.SERVICE_NAME; } - getChecksByMonitor = async ({ monitorId, query, teamId }) => { + getChecksByMonitor = async ({ monitorId, query, teamId }: { monitorId: string; query: any; teamId: string }) => { if (!monitorId) { throw this.errorService.createBadRequestError("No monitor ID in request"); } @@ -23,15 +44,8 @@ class CheckService { throw this.errorService.createBadRequestError("No team ID in request"); } - const monitor = await this.db.monitorModule.getMonitorById(monitorId); - - if (!monitor) { - throw this.errorService.createNotFoundError("Monitor not found"); - } - - if (!monitor.teamId.equals(teamId)) { - throw this.errorService.createAuthorizationError(); - } + // For verificaiton, throws an error if monitor doesn't belong to team + await this.monitorsRepository.findById(monitorId, teamId); let { sortOrder, dateRange, filter, ack, page, rowsPerPage, status } = query; const result = await this.db.checkModule.getChecksByMonitor({ @@ -47,7 +61,7 @@ class CheckService { return result; }; - getChecksByTeam = async ({ teamId, query }) => { + getChecksByTeam = async ({ teamId, query }: { teamId: string; query: any }) => { let { sortOrder, dateRange, filter, ack, page, rowsPerPage } = query; if (!teamId) { @@ -66,7 +80,7 @@ class CheckService { return checkData; }; - getChecksSummaryByTeamId = async ({ teamId }) => { + getChecksSummaryByTeamId = async ({ teamId }: { teamId: string }) => { if (!teamId) { throw this.errorService.createBadRequestError("No team ID in request"); } @@ -75,7 +89,7 @@ class CheckService { return summary; }; - ackCheck = async ({ checkId, teamId, ack }) => { + ackCheck = async ({ checkId, teamId, ack }: { checkId: string; teamId: string; ack: any }) => { if (!checkId) { throw this.errorService.createBadRequestError("No check ID in request"); } @@ -88,27 +102,21 @@ class CheckService { return updatedCheck; }; - ackAllChecks = async ({ monitorId, path, teamId, ack }) => { + ackAllChecks = async ({ monitorId, path, teamId, ack }: { monitorId: string; path: string; teamId: string; ack: any }) => { if (path === "monitor") { if (!monitorId) { throw this.errorService.createBadRequestError("No monitor ID in request"); } - const monitor = await this.db.monitorModule.getMonitorById(monitorId); - if (!monitor) { - throw this.errorService.createNotFoundError("Monitor not found"); - } - - if (!monitor.teamId.equals(teamId)) { - throw this.errorService.createAuthorizationError(); - } + // For verificaiton, throws an error if monitor doesn't belong to team + await this.monitorsRepository.findById(monitorId, teamId); } const updatedChecks = await this.db.checkModule.ackAllChecks(monitorId, teamId, ack, path); return updatedChecks; }; - deleteChecks = async ({ monitorId, teamId }) => { + deleteChecks = async ({ monitorId, teamId }: { monitorId: string; teamId: string }) => { if (!monitorId) { throw this.errorService.createBadRequestError("No monitor ID in request"); } @@ -117,20 +125,13 @@ class CheckService { throw this.errorService.createBadRequestError("No team ID in request"); } - const monitor = await this.db.monitorModule.getMonitorById(monitorId); - - if (!monitor) { - throw this.errorService.createNotFoundError("Monitor not found"); - } - - if (!monitor.teamId.equals(teamId)) { - throw this.errorService.createAuthorizationError(); - } + // For verificaiton, throws an error if monitor doesn't belong to team + await this.monitorsRepository.findById(monitorId, teamId); const deletedCount = await this.db.checkModule.deleteChecks(monitorId); return deletedCount; }; - deleteChecksByTeamId = async ({ teamId }) => { + deleteChecksByTeamId = async ({ teamId }: { teamId: string }) => { if (!teamId) { throw this.errorService.createBadRequestError("No team ID in request"); } @@ -139,7 +140,7 @@ class CheckService { return deletedCount; }; - updateChecksTTL = async ({ teamId, ttl }) => { + updateChecksTTL = async ({ teamId, ttl }: { teamId: string; ttl: string }) => { const SECONDS_PER_DAY = 86400; const newTTL = parseInt(ttl, 10) * SECONDS_PER_DAY; await this.db.checkModule.updateChecksTTL(teamId, newTTL); diff --git a/server/src/service/business/maintenanceWindowService.js b/server/src/service/business/maintenanceWindowService.ts similarity index 55% rename from server/src/service/business/maintenanceWindowService.js rename to server/src/service/business/maintenanceWindowService.ts index 9d52702e9..bb82821cf 100644 --- a/server/src/service/business/maintenanceWindowService.js +++ b/server/src/service/business/maintenanceWindowService.ts @@ -1,30 +1,50 @@ +import { IMonitorsRepository } from "@/repositories/index.js"; + const SERVICE_NAME = "maintenanceWindowService"; class MaintenanceWindowService { static SERVICE_NAME = SERVICE_NAME; + private db: any; + private settingsService: any; + private stringService: any; + private errorService: any; + private monitorsRepository: IMonitorsRepository; - constructor({ db, settingsService, stringService, errorService }) { + constructor({ + db, + settingsService, + stringService, + errorService, + monitorsRepository, + }: { + db: any; + settingsService: any; + stringService: any; + errorService: any; + monitorsRepository: IMonitorsRepository; + }) { this.db = db; this.settingsService = settingsService; this.stringService = stringService; this.errorService = errorService; + this.monitorsRepository = monitorsRepository; } get serviceName() { return MaintenanceWindowService.SERVICE_NAME; } - createMaintenanceWindow = async ({ teamId, body }) => { + createMaintenanceWindow = async ({ teamId, body }: { teamId: string; body: any }) => { const monitorIds = body.monitors; - const monitors = await this.db.monitorModule.getMonitorsByIds(monitorIds); + const monitors = await this.monitorsRepository.findByIds(monitorIds); - const unauthorizedMonitors = monitors.filter((monitor) => !monitor.teamId.equals(teamId)); + const unauthorizedMonitors = monitors.filter((monitor) => monitor.teamId !== teamId); if (unauthorizedMonitors.length > 0) { throw this.errorService.createAuthorizationError(); } - const dbTransactions = monitorIds.map((monitorId) => { + const dbTransactions = monitorIds.map((monitorId: string) => { return this.db.maintenanceWindowModule.createMaintenanceWindow({ teamId, monitorId, @@ -38,26 +58,26 @@ class MaintenanceWindowService { await Promise.all(dbTransactions); }; - getMaintenanceWindowById = async ({ id, teamId }) => { + getMaintenanceWindowById = async ({ id, teamId }: { id: string; teamId: string }) => { const maintenanceWindow = await this.db.maintenanceWindowModule.getMaintenanceWindowById({ id, teamId }); return maintenanceWindow; }; - getMaintenanceWindowsByTeamId = async ({ teamId, query }) => { + getMaintenanceWindowsByTeamId = async ({ teamId, query }: { teamId: string; query: any }) => { const maintenanceWindows = await this.db.maintenanceWindowModule.getMaintenanceWindowsByTeamId(teamId, query); return maintenanceWindows; }; - getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => { + getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }: { monitorId: string; teamId: string }) => { const maintenanceWindows = await this.db.maintenanceWindowModule.getMaintenanceWindowsByMonitorId({ monitorId, teamId }); return maintenanceWindows; }; - deleteMaintenanceWindow = async ({ id, teamId }) => { + deleteMaintenanceWindow = async ({ id, teamId }: { id: string; teamId: string }) => { await this.db.maintenanceWindowModule.deleteMaintenanceWindowById({ id, teamId }); }; - editMaintenanceWindow = async ({ id, teamId, body }) => { + editMaintenanceWindow = async ({ id, teamId, body }: { id: string; teamId: string; body: any }) => { const editedMaintenanceWindow = await this.db.maintenanceWindowModule.editMaintenanceWindowById({ id, body, teamId }); return editedMaintenanceWindow; }; diff --git a/server/src/service/business/userService.js b/server/src/service/business/userService.ts similarity index 79% rename from server/src/service/business/userService.js rename to server/src/service/business/userService.ts index 7d7f7b014..129ed1e59 100644 --- a/server/src/service/business/userService.js +++ b/server/src/service/business/userService.ts @@ -1,9 +1,44 @@ +import { IMonitorsRepository } from "@/repositories/index.js"; + const SERVICE_NAME = "userService"; class UserService { static SERVICE_NAME = SERVICE_NAME; - constructor({ crypto, db, emailService, settingsService, logger, stringService, jwt, errorService, jobQueue }) { + private db: any; + private emailService: any; + private settingsService: any; + private logger: any; + private stringService: any; + private jwt: any; + private errorService: any; + private jobQueue: any; + private crypto: any; + private monitorsRepository: IMonitorsRepository; + + constructor({ + crypto, + db, + emailService, + settingsService, + logger, + stringService, + jwt, + errorService, + jobQueue, + monitorsRepository, + }: { + crypto: any; + db: any; + emailService: any; + settingsService: any; + logger: any; + stringService: any; + jwt: any; + errorService: any; + jobQueue: any; + monitorsRepository: IMonitorsRepository; + }) { this.db = db; this.emailService = emailService; this.settingsService = settingsService; @@ -13,20 +48,21 @@ class UserService { this.errorService = errorService; this.jobQueue = jobQueue; this.crypto = crypto; + this.monitorsRepository = monitorsRepository; } get serviceName() { return UserService.SERVICE_NAME; } - issueToken = (payload, appSettings) => { + issueToken = (payload: any, appSettings: any) => { const tokenTTL = appSettings?.jwtTTL ?? "2h"; const tokenSecret = appSettings?.jwtSecret; const payloadData = payload; return this.jwt.sign(payloadData, tokenSecret, { expiresIn: tokenTTL }); }; - registerUser = async (user, file) => { + registerUser = async (user: any, file: any) => { // Create a new user // If superAdmin exists, a token should be attached to all further register requests const superAdminExists = await this.db.userModule.checkSuperadmin(); @@ -61,7 +97,7 @@ class UserService { const html = await this.emailService.buildEmail("welcomeEmailTemplate", { name: newUser.firstName, }); - this.emailService.sendEmail(newUser.email, "Welcome to Uptime Monitor", html).catch((error) => { + this.emailService.sendEmail(newUser.email, "Welcome to Uptime Monitor", html).catch((error: any) => { this.logger.warn({ message: error.message, service: SERVICE_NAME, @@ -69,7 +105,7 @@ class UserService { stack: error.stack, }); }); - } catch (error) { + } catch (error: any) { this.logger.warn({ message: error.message, service: SERVICE_NAME, @@ -81,7 +117,7 @@ class UserService { return { user: newUser, token }; }; - loginUser = async (email, password) => { + loginUser = async (email: string, password: string) => { // Check if user exists const user = await this.db.userModule.getUserByEmail(email); // Compare password @@ -103,7 +139,7 @@ class UserService { return { user: userWithoutPassword, token }; }; - editUser = async (updates, file, currentUser) => { + editUser = async (updates: any, file: any, currentUser: any) => { // Change Password check if (updates?.password && updates?.newPassword) { // Get user's email @@ -131,7 +167,7 @@ class UserService { return superAdminExists; }; - requestRecovery = async (email) => { + requestRecovery = async (email: string) => { const user = await this.db.userModule.getUserByEmail(email); const recoveryToken = await this.db.recoveryModule.requestRecoveryToken(email); const name = user.firstName; @@ -147,18 +183,18 @@ class UserService { return msgId; }; - validateRecovery = async (recoveryToken) => { + validateRecovery = async (recoveryToken: string) => { await this.db.recoveryModule.validateRecoveryToken(recoveryToken); }; - resetPassword = async (password, recoveryToken) => { + resetPassword = async (password: string, recoveryToken: string) => { const user = await this.db.recoveryModule.resetPassword(password, recoveryToken); const appSettings = await this.settingsService.getSettings(); const token = this.issueToken(user._doc, appSettings); return { user, token }; }; - deleteUser = async (user) => { + deleteUser = async (user: any) => { const email = user?.email; if (!email) { throw this.errorService.createBadRequestError("No email in request"); @@ -181,15 +217,14 @@ class UserService { } // 1. Find all the monitors associated with the team ID if superadmin - const result = await this.db.monitorModule.getMonitorsByTeamId({ - teamId: teamId, - }); + const res = await this.monitorsRepository.findByTeamId(teamId, {}); if (roles.includes("superadmin")) { // 2. Remove all jobs, delete checks and alerts - result?.monitors.length > 0 && + res && + res?.length > 0 && (await Promise.all( - result.monitors.map(async (monitor) => { + res.map(async (monitor) => { await this.jobQueue.deleteJob(monitor); }) )); @@ -203,15 +238,15 @@ class UserService { return users; }; - getUserById = async (roles, userId) => { + getUserById = async (roles: any, userId: any) => { const user = await this.db.userModule.getUserById(roles, userId); return user; }; - editUserById = async (userId, user) => { + editUserById = async (userId: any, user: any) => { await this.db.userModule.editUserById(userId, user); }; - setPasswordByUserId = async (userId, password) => { + setPasswordByUserId = async (userId: any, password: string) => { const updatedUser = await this.db.userModule.updateUser({ userId: userId, user: { password: password }, file: null }); return updatedUser; }; diff --git a/server/src/service/infrastructure/statusService.js b/server/src/service/infrastructure/statusService.js index 0b2e9f44c..763b6d1fa 100755 --- a/server/src/service/infrastructure/statusService.js +++ b/server/src/service/infrastructure/statusService.js @@ -167,9 +167,8 @@ class StatusService { const check = this.buildCheck(networkResponse); await this.insertCheck(check); try { - const { monitorId, status, code } = networkResponse; - - const monitor = await this.monitorsRepository.findById(monitorId); + const { monitorId, teamId, status, code } = networkResponse; + const monitor = await this.monitorsRepository.findById(monitorId, teamId); // Update running stats this.updateRunningStats({ monitor, networkResponse });