import { ObjectId } from "mongodb"; import mongoose from "mongoose"; import { CheckModel } from "@/db/models/index.js"; import { buildChecksSummaryByTeamIdPipeline } from "./checkModuleQueries.js"; const SERVICE_NAME = "checkModule"; const dateRangeLookup = { recent: new Date(new Date().setDate(new Date().getDate() - 2)), hour: new Date(new Date().setHours(new Date().getHours() - 1)), 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: undefined, }; class CheckModule { constructor({ logger, Monitor, User }) { this.logger = logger; this.Monitor = Monitor; this.User = User; } createChecks = async (checks) => { try { await CheckModel.insertMany(checks, { ordered: false, runValidators: true }); } catch (error) { error.service = SERVICE_NAME; error.method = "createCheck"; throw error; } }; getChecksByMonitor = async ({ monitorId, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => { try { status = status === "true" ? true : status === "false" ? false : undefined; page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] }; // Match const matchStage = { "metadata.monitorId": new ObjectId(monitorId), ...(typeof status !== "undefined" && { status }), ...(typeof ack !== "undefined" && ackStage), ...(dateRangeLookup[dateRange] && { createdAt: { $gte: dateRangeLookup[dateRange], }, }), }; if (filter !== undefined) { switch (filter) { case "all": break; case "down": break; case "resolve": matchStage.statusCode = 5000; break; default: this.logger.warn({ message: "invalid filter", service: SERVICE_NAME, method: "getChecks", }); break; } } //Sort sortOrder = sortOrder === "asc" ? 1 : -1; // Pagination let skip = 0; if (page && rowsPerPage) { skip = page * rowsPerPage; } const checks = await CheckModel.aggregate([ { $match: matchStage }, { $sort: { createdAt: sortOrder } }, { $facet: { summary: [{ $count: "checksCount" }], checks: [{ $skip: skip }, { $limit: rowsPerPage }], }, }, { $project: { checksCount: { $ifNull: [{ $arrayElemAt: ["$summary.checksCount", 0] }, 0], }, checks: { $ifNull: ["$checks", []], }, }, }, ]); return checks[0]; } catch (error) { error.service = SERVICE_NAME; error.method = "getChecks"; throw error; } }; getChecksByTeam = async ({ sortOrder, dateRange, filter, ack, page, rowsPerPage, teamId }) => { try { page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] }; const matchStage = { "metadata.teamId": new ObjectId(teamId), status: false, ...(typeof ack !== "undefined" && ackStage), ...(dateRangeLookup[dateRange] && { createdAt: { $gte: dateRangeLookup[dateRange], }, }), }; // Add filter to match stage if (filter !== undefined) { switch (filter) { case "all": break; case "down": break; case "resolve": matchStage.statusCode = 5000; break; default: this.logger.warn({ message: "invalid filter", service: SERVICE_NAME, method: "getChecksByTeam", }); break; } } sortOrder = sortOrder === "asc" ? 1 : -1; // pagination let skip = 0; if (page && rowsPerPage) { skip = page * rowsPerPage; } const aggregatePipeline = [ { $match: matchStage }, { $sort: { createdAt: sortOrder } }, { $facet: { summary: [{ $count: "checksCount" }], checks: [{ $skip: skip }, { $limit: rowsPerPage }], }, }, { $project: { checksCount: { $arrayElemAt: ["$summary.checksCount", 0] }, checks: "$checks", }, }, ]; const checks = await CheckModel.aggregate(aggregatePipeline); return checks[0]; } catch (error) { error.service = SERVICE_NAME; error.method = "getChecksByTeam"; throw error; } }; ackCheck = async (checkId, teamId, ack) => { try { const updatedCheck = await CheckModel.findOneAndUpdate( { _id: checkId, "metadata.teamId": teamId }, { $set: { ack, ackAt: new Date() } }, { new: true } ); if (!updatedCheck) { throw new Error("Check not found"); } return updatedCheck; } catch (error) { error.service = SERVICE_NAME; error.method = "ackCheck"; throw error; } }; ackAllChecks = async (monitorId, teamId, ack, path) => { try { const filter = path === "monitor" ? { "metadata.monitorId": new mongoose.Types.ObjectId(monitorId) } : { "metadata.teamId": new mongoose.Types.ObjectId(teamId) }; const updatedChecks = await CheckModel.updateMany(filter, { $set: { ack, ackAt: new Date() } }); return updatedChecks.modifiedCount; } catch (error) { error.service = SERVICE_NAME; error.method = "ackAllChecks"; throw error; } }; getChecksSummaryByTeamId = async ({ teamId }) => { try { const matchStage = { "metadata.teamId": new ObjectId(teamId), }; const checks = await CheckModel.aggregate(buildChecksSummaryByTeamIdPipeline({ matchStage })); return checks[0].summary; } catch (error) { error.service = SERVICE_NAME; error.method = "getChecksSummaryByTeamId"; throw error; } }; deleteChecks = async (monitorId) => { try { const result = await CheckModel.deleteMany({ "metadata.monitorId": monitorId }); return result.deletedCount; } catch (error) { error.service = SERVICE_NAME; error.method = "deleteChecks"; throw error; } }; deleteChecksByTeamId = async (teamId) => { try { // Find all monitor IDs for this team (only get _id field for efficiency) const teamMonitors = await this.Monitor.find({ teamId }, { _id: 1 }); const monitorIds = teamMonitors.map((monitor) => monitor._id); // Delete all checks for these monitors in one operation const deleteResult = await CheckModel.deleteMany({ "metadata.monitorId": { $in: monitorIds } }); return deleteResult.deletedCount; } catch (error) { error.service = SERVICE_NAME; error.method = "deleteChecksByTeamId"; throw error; } }; updateChecksTTL = async (teamId, ttl) => { try { await CheckModel.collection.dropIndex("expiry_1"); } catch (error) { this.logger.error({ message: error.message, service: SERVICE_NAME, method: "updateChecksTTL", stack: error.stack, }); } try { await CheckModel.collection.createIndex( { expiry: 1 }, { expireAfterSeconds: ttl, partialFilterExpression: { "metadata.mode": { $exists: true } } } ); } catch (error) { error.service = SERVICE_NAME; error.method = "updateChecksTTL"; throw error; } // Update user try { await this.User.updateMany({ teamId: teamId }, { checkTTL: ttl }); } catch (error) { error.service = SERVICE_NAME; error.method = "updateChecksTTL"; throw error; } }; } export default CheckModule;