mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2025-12-30 22:29:37 -06:00
313 lines
6.7 KiB
JavaScript
Executable File
313 lines
6.7 KiB
JavaScript
Executable File
import StatusPage from "../../models/StatusPage.js";
|
|
import Monitor from "../../models/Monitor.js";
|
|
import { NormalizeData } from "../../../utils/dataUtils.js";
|
|
import ServiceRegistry from "../../../service/serviceRegistry.js";
|
|
import StringService from "../../../service/stringService.js";
|
|
|
|
const SERVICE_NAME = "statusPageModule";
|
|
|
|
const createStatusPage = async (statusPageData, image) => {
|
|
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
|
|
|
try {
|
|
const statusPage = new StatusPage({ ...statusPageData });
|
|
if (image) {
|
|
statusPage.logo = {
|
|
data: image.buffer,
|
|
contentType: image.mimetype,
|
|
};
|
|
}
|
|
await statusPage.save();
|
|
return statusPage;
|
|
} catch (error) {
|
|
if (error?.code === 11000) {
|
|
// Handle duplicate URL errors
|
|
error.status = 400;
|
|
error.message = stringService.statusPageUrlNotUnique;
|
|
}
|
|
error.service = SERVICE_NAME;
|
|
error.method = "createStatusPage";
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const updateStatusPage = async (statusPageData, image) => {
|
|
try {
|
|
if (image) {
|
|
statusPageData.logo = {
|
|
data: image.buffer,
|
|
contentType: image.mimetype,
|
|
};
|
|
} else {
|
|
statusPageData.logo = null;
|
|
}
|
|
|
|
if (statusPageData.deleteSubmonitors === "true") {
|
|
statusPageData.subMonitors = [];
|
|
}
|
|
const statusPage = await StatusPage.findOneAndUpdate(
|
|
{ url: statusPageData.url },
|
|
statusPageData,
|
|
{
|
|
new: true,
|
|
}
|
|
);
|
|
|
|
return statusPage;
|
|
} catch (error) {
|
|
error.service = SERVICE_NAME;
|
|
error.method = "updateStatusPage";
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const getDistributedStatusPageByUrl = async ({ url, daysToShow = 30 }) => {
|
|
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
|
try {
|
|
const statusPage = await StatusPage.findOne({ url }).lean();
|
|
|
|
if (!statusPage) {
|
|
const error = new Error(stringService.statusPageNotFound);
|
|
error.status = 404;
|
|
throw error;
|
|
}
|
|
|
|
// No sub monitors, return status page
|
|
if (statusPage.subMonitors.length === 0) {
|
|
return statusPage;
|
|
}
|
|
// Sub monitors, return status page with sub monitors
|
|
const daysAgo = new Date();
|
|
daysAgo.setDate(daysAgo.getDate() - daysToShow);
|
|
|
|
const subMonitors = await Monitor.aggregate([
|
|
{ $match: { _id: { $in: statusPage.subMonitors } } },
|
|
{
|
|
$addFields: {
|
|
orderIndex: { $indexOfArray: [statusPage.subMonitors, "$_id"] },
|
|
},
|
|
},
|
|
|
|
// Return 30 days of checks by default
|
|
{
|
|
$lookup: {
|
|
from: "checks",
|
|
let: { monitorId: "$_id" },
|
|
pipeline: [
|
|
{
|
|
$match: {
|
|
$expr: {
|
|
$and: [
|
|
{ $eq: ["$monitorId", "$$monitorId"] },
|
|
{ $gte: ["$updatedAt", daysAgo] },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: {
|
|
$dateToString: { format: "%Y-%m-%d", date: "$updatedAt" },
|
|
},
|
|
responseTime: {
|
|
$avg: "$responseTime",
|
|
},
|
|
trueCount: {
|
|
$sum: {
|
|
$cond: [{ $eq: ["$status", true] }, 1, 0],
|
|
},
|
|
},
|
|
totalCount: {
|
|
$sum: 1,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
_id: 1,
|
|
responseTime: 1,
|
|
upPercentage: {
|
|
$cond: [
|
|
{ $eq: ["$totalCount", 0] },
|
|
0,
|
|
{ $multiply: [{ $divide: ["$trueCount", "$totalCount"] }, 100] },
|
|
],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$sort: { _id: -1 },
|
|
},
|
|
],
|
|
as: "checks",
|
|
},
|
|
},
|
|
{ $sort: { orderIndex: 1 } },
|
|
{ $project: { orderIndex: 0 } },
|
|
]);
|
|
|
|
const normalizedSubMonitors = subMonitors.map((monitor) => {
|
|
return {
|
|
...monitor,
|
|
checks: NormalizeData(monitor.checks, 10, 100),
|
|
};
|
|
});
|
|
return { ...statusPage, subMonitors: normalizedSubMonitors };
|
|
} catch (error) {
|
|
error.service = SERVICE_NAME;
|
|
error.method = "getDistributedStatusPageByUrl";
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const getStatusPageByUrl = async (url, type) => {
|
|
// TODO This is deprecated, can remove and have controller call getStatusPage
|
|
try {
|
|
return getStatusPage(url);
|
|
} catch (error) {
|
|
error.service = SERVICE_NAME;
|
|
error.method = "getStatusPageByUrl";
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const getStatusPagesByTeamId = async (teamId) => {
|
|
try {
|
|
const statusPages = await StatusPage.find({ teamId });
|
|
return statusPages;
|
|
} catch (error) {
|
|
error.service = SERVICE_NAME;
|
|
error.method = "getStatusPagesByTeamId";
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const getStatusPage = async (url) => {
|
|
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
|
|
|
try {
|
|
const statusPageQuery = await StatusPage.aggregate([
|
|
{ $match: { url: url } },
|
|
{
|
|
$set: {
|
|
originalMonitors: "$monitors",
|
|
},
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "monitors",
|
|
localField: "monitors",
|
|
foreignField: "_id",
|
|
as: "monitors",
|
|
},
|
|
},
|
|
{
|
|
$unwind: "$monitors",
|
|
},
|
|
{
|
|
$lookup: {
|
|
from: "checks",
|
|
let: { monitorId: "$monitors._id" },
|
|
pipeline: [
|
|
{
|
|
$match: {
|
|
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
|
},
|
|
},
|
|
{ $sort: { createdAt: -1 } },
|
|
{ $limit: 25 },
|
|
],
|
|
as: "monitors.checks",
|
|
},
|
|
},
|
|
{
|
|
$addFields: {
|
|
"monitors.orderIndex": {
|
|
$indexOfArray: ["$originalMonitors", "$monitors._id"],
|
|
},
|
|
},
|
|
},
|
|
{
|
|
$group: {
|
|
_id: "$_id",
|
|
statusPage: { $first: "$$ROOT" },
|
|
monitors: { $push: "$monitors" },
|
|
},
|
|
},
|
|
{
|
|
$project: {
|
|
statusPage: {
|
|
_id: 1,
|
|
color: 1,
|
|
companyName: 1,
|
|
isPublished: 1,
|
|
logo: 1,
|
|
originalMonitors: 1,
|
|
showCharts: 1,
|
|
showUptimePercentage: 1,
|
|
timezone: 1,
|
|
url: 1,
|
|
},
|
|
monitors: {
|
|
$sortArray: {
|
|
input: "$monitors",
|
|
sortBy: { orderIndex: 1 },
|
|
},
|
|
},
|
|
},
|
|
},
|
|
]);
|
|
if (!statusPageQuery.length) {
|
|
const error = new Error(stringService.statusPageNotFound);
|
|
error.status = 404;
|
|
throw error;
|
|
}
|
|
|
|
const { statusPage, monitors } = statusPageQuery[0];
|
|
|
|
const normalizedMonitors = monitors.map((monitor) => {
|
|
return {
|
|
...monitor,
|
|
checks: NormalizeData(monitor.checks, 10, 100),
|
|
};
|
|
});
|
|
|
|
return { statusPage, monitors: normalizedMonitors };
|
|
} catch (error) {
|
|
error.service = SERVICE_NAME;
|
|
error.method = "getStatusPageByUrl";
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const deleteStatusPage = async (url) => {
|
|
try {
|
|
await StatusPage.deleteOne({ url });
|
|
} catch (error) {
|
|
error.service = SERVICE_NAME;
|
|
error.method = "deleteStatusPage";
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
const deleteStatusPagesByMonitorId = async (monitorId) => {
|
|
try {
|
|
await StatusPage.deleteMany({ monitors: { $in: [monitorId] } });
|
|
} catch (error) {
|
|
error.service = SERVICE_NAME;
|
|
error.method = "deleteStatusPageByMonitorId";
|
|
throw error;
|
|
}
|
|
};
|
|
|
|
export {
|
|
createStatusPage,
|
|
updateStatusPage,
|
|
getStatusPagesByTeamId,
|
|
getStatusPage,
|
|
getStatusPageByUrl,
|
|
getDistributedStatusPageByUrl,
|
|
deleteStatusPage,
|
|
deleteStatusPagesByMonitorId,
|
|
};
|