2025-04-20 11:29:53 -07:00
|
|
|
import { ObjectId } from "mongodb";
|
2025-07-01 18:23:11 +05:30
|
|
|
import { buildChecksSummaryByTeamIdPipeline } from "./checkModuleQueries.js";
|
2025-04-20 11:29:53 -07:00
|
|
|
|
|
|
|
|
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,
|
|
|
|
|
};
|
|
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
class CheckModule {
|
|
|
|
|
constructor({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User }) {
|
|
|
|
|
this.logger = logger;
|
|
|
|
|
this.Check = Check;
|
|
|
|
|
this.HardwareCheck = HardwareCheck;
|
|
|
|
|
this.PageSpeedCheck = PageSpeedCheck;
|
|
|
|
|
this.Monitor = Monitor;
|
|
|
|
|
this.User = User;
|
2025-04-20 11:29:53 -07:00
|
|
|
}
|
|
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
createChecks = async (checks) => {
|
|
|
|
|
try {
|
|
|
|
|
await this.Check.insertMany(checks, { ordered: false });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
error.service = SERVICE_NAME;
|
|
|
|
|
error.method = "createCheck";
|
|
|
|
|
throw error;
|
2025-04-20 11:29:53 -07:00
|
|
|
}
|
2025-07-29 11:51:06 -07:00
|
|
|
};
|
|
|
|
|
|
2025-08-18 10:55:12 -07:00
|
|
|
getChecksByMonitor = async ({ monitorId, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => {
|
2025-07-29 11:51:06 -07:00
|
|
|
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 = {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
//Sort
|
|
|
|
|
sortOrder = sortOrder === "asc" ? 1 : -1;
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
// Pagination
|
|
|
|
|
let skip = 0;
|
|
|
|
|
if (page && rowsPerPage) {
|
|
|
|
|
skip = page * rowsPerPage;
|
|
|
|
|
}
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-08-18 10:55:12 -07:00
|
|
|
const checks = await this.Check.aggregate([
|
2025-07-29 11:51:06 -07:00
|
|
|
{ $match: matchStage },
|
|
|
|
|
{ $sort: { createdAt: sortOrder } },
|
|
|
|
|
{
|
|
|
|
|
$facet: {
|
|
|
|
|
summary: [{ $count: "checksCount" }],
|
|
|
|
|
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
2025-04-20 11:29:53 -07:00
|
|
|
},
|
|
|
|
|
},
|
2025-07-29 11:51:06 -07:00
|
|
|
{
|
|
|
|
|
$project: {
|
|
|
|
|
checksCount: {
|
|
|
|
|
$ifNull: [{ $arrayElemAt: ["$summary.checksCount", 0] }, 0],
|
|
|
|
|
},
|
|
|
|
|
checks: {
|
|
|
|
|
$ifNull: ["$checks", []],
|
|
|
|
|
},
|
|
|
|
|
},
|
2025-04-20 11:29:53 -07:00
|
|
|
},
|
2025-07-29 11:51:06 -07:00
|
|
|
]);
|
|
|
|
|
return checks[0];
|
|
|
|
|
} catch (error) {
|
|
|
|
|
error.service = SERVICE_NAME;
|
|
|
|
|
error.method = "getChecks";
|
|
|
|
|
throw error;
|
2025-04-20 11:29:53 -07:00
|
|
|
}
|
2025-07-29 11:51:06 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
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 = {
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
sortOrder = sortOrder === "asc" ? 1 : -1;
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
// pagination
|
|
|
|
|
let skip = 0;
|
|
|
|
|
if (page && rowsPerPage) {
|
|
|
|
|
skip = page * rowsPerPage;
|
|
|
|
|
}
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
const aggregatePipeline = [
|
|
|
|
|
{ $match: matchStage },
|
2025-06-09 10:17:09 +08:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
{ $sort: { createdAt: sortOrder } },
|
|
|
|
|
{
|
|
|
|
|
$facet: {
|
|
|
|
|
summary: [{ $count: "checksCount" }],
|
|
|
|
|
checks: [{ $skip: skip }, { $limit: rowsPerPage }],
|
|
|
|
|
},
|
2025-04-20 11:29:53 -07:00
|
|
|
},
|
2025-07-29 11:51:06 -07:00
|
|
|
{
|
|
|
|
|
$project: {
|
|
|
|
|
checksCount: { $arrayElemAt: ["$summary.checksCount", 0] },
|
|
|
|
|
checks: "$checks",
|
|
|
|
|
},
|
2025-04-20 11:29:53 -07:00
|
|
|
},
|
2025-07-29 11:51:06 -07:00
|
|
|
];
|
|
|
|
|
|
|
|
|
|
const checks = await this.Check.aggregate(aggregatePipeline);
|
|
|
|
|
return checks[0];
|
|
|
|
|
} catch (error) {
|
|
|
|
|
error.service = SERVICE_NAME;
|
|
|
|
|
error.method = "getChecksByTeam";
|
|
|
|
|
throw error;
|
2025-06-23 22:07:03 +05:30
|
|
|
}
|
2025-07-29 11:51:06 -07:00
|
|
|
};
|
2025-06-23 22:07:03 +05:30
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
ackCheck = async (checkId, teamId, ack) => {
|
|
|
|
|
try {
|
|
|
|
|
const updatedCheck = await this.Check.findOneAndUpdate({ _id: checkId, teamId: teamId }, { $set: { ack, ackAt: new Date() } }, { new: true });
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
if (!updatedCheck) {
|
|
|
|
|
throw new Error("Check not found");
|
|
|
|
|
}
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
return updatedCheck;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
error.service = SERVICE_NAME;
|
|
|
|
|
error.method = "ackCheck";
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ackAllChecks = async (monitorId, teamId, ack, path) => {
|
|
|
|
|
try {
|
|
|
|
|
const updatedChecks = await this.Check.updateMany(path === "monitor" ? { monitorId } : { teamId }, { $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 = {
|
|
|
|
|
teamId: new ObjectId(teamId),
|
|
|
|
|
};
|
|
|
|
|
const checks = await this.Check.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 this.Check.deleteMany({ 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 this.Check.deleteMany({ monitorId: { $in: monitorIds } });
|
|
|
|
|
|
|
|
|
|
return deleteResult.deletedCount;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
error.service = SERVICE_NAME;
|
|
|
|
|
error.method = "deleteChecksByTeamId";
|
|
|
|
|
throw error;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
updateChecksTTL = async (teamId, ttl) => {
|
|
|
|
|
try {
|
|
|
|
|
await this.Check.collection.dropIndex("expiry_1");
|
|
|
|
|
} catch (error) {
|
|
|
|
|
this.logger.error({
|
|
|
|
|
message: error.message,
|
|
|
|
|
service: SERVICE_NAME,
|
|
|
|
|
method: "updateChecksTTL",
|
|
|
|
|
stack: error.stack,
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
try {
|
|
|
|
|
await this.Check.collection.createIndex(
|
|
|
|
|
{ expiry: 1 },
|
|
|
|
|
{ expireAfterSeconds: ttl } // TTL in seconds, adjust as necessary
|
|
|
|
|
);
|
|
|
|
|
} 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;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-04-20 11:29:53 -07:00
|
|
|
|
2025-07-29 11:51:06 -07:00
|
|
|
export default CheckModule;
|