Merge pull request #1752 from bluewave-labs/feat/fe/config-du-monitor

feat: fe/config du monitor
This commit is contained in:
Alexander Holliday
2025-02-13 10:58:48 -08:00
committed by GitHub
18 changed files with 448 additions and 125 deletions

View File

@@ -92,7 +92,7 @@ export const updateUptimeMonitor = createAsyncThunk(
const res = await networkService.updateMonitor({
authToken: authToken,
monitorId: monitor._id,
updatedFields: updatedFields,
monitor,
});
return res.data;
} catch (error) {

View File

@@ -0,0 +1,33 @@
import { useState } from "react";
import { networkService } from "../../../../main";
import { useSelector } from "react-redux";
import { createToast } from "../../../../Utils/toastUtils";
const useCreateDistributedUptimeMonitor = ({ isCreate, monitorId }) => {
const { authToken, user } = useSelector((state) => state.auth);
const [isLoading, setIsLoading] = useState(false);
const [networkError, setNetworkError] = useState(false);
const createDistributedUptimeMonitor = async ({ form }) => {
setIsLoading(true);
try {
if (isCreate) {
await networkService.createMonitor({ authToken, monitor: form });
} else {
await networkService.updateMonitor({ authToken, monitor: form, monitorId });
}
return true;
} catch (error) {
setNetworkError(true);
createToast({ body: error?.response?.data?.msg ?? error.message });
return false;
} finally {
setIsLoading(false);
}
};
return [createDistributedUptimeMonitor, isLoading, networkError];
};
export { useCreateDistributedUptimeMonitor };

View File

@@ -0,0 +1,32 @@
import { useEffect, useState } from "react";
import { networkService } from "../../../../main";
import { createToast } from "../../../../Utils/toastUtils";
export const useMonitorFetch = ({ authToken, monitorId, isCreate }) => {
const [networkError, setNetworkError] = useState(false);
const [isLoading, setIsLoading] = useState(true);
const [monitor, setMonitor] = useState(undefined);
useEffect(() => {
const fetchMonitors = async () => {
try {
if (isCreate) return;
const res = await networkService.getUptimeDetailsById({
authToken: authToken,
monitorId: monitorId,
normalize: true,
});
setMonitor(res?.data?.data ?? {});
} catch (error) {
setNetworkError(true);
createToast({ body: error.message });
} finally {
setIsLoading(false);
}
};
fetchMonitors();
}, [authToken, monitorId, isCreate]);
return [monitor, isLoading, networkError];
};
export default useMonitorFetch;

View File

@@ -13,18 +13,15 @@ import { createToast } from "../../../Utils/toastUtils";
// Utility
import { useTheme } from "@emotion/react";
import { useState } from "react";
import { useState, useEffect } from "react";
import { useNavigate } from "react-router-dom";
import { useSelector, useDispatch } from "react-redux";
import { useSelector } from "react-redux";
import { monitorValidation } from "../../../Validation/validation";
import { createUptimeMonitor } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
import { useLocation } from "react-router-dom";
import { useParams } from "react-router-dom";
import { useCreateDistributedUptimeMonitor } from "./Hooks/useCreateDistributedUptimeMonitor";
import { useMonitorFetch } from "./Hooks/useMonitorFetch";
// Constants
const BREADCRUMBS = [
{ name: `distributed uptime`, path: "/distributed-uptime" },
{ name: "create", path: `/distributed-uptime/create` },
];
const MS_PER_MINUTE = 60000;
const SELECT_VALUES = [
{ _id: 1, name: "1 minute" },
@@ -34,18 +31,30 @@ const SELECT_VALUES = [
{ _id: 5, name: "5 minutes" },
];
const parseUrl = (url) => {
try {
return new URL(url);
} catch (error) {
return null;
}
};
const CreateDistributedUptime = () => {
const location = useLocation();
const isCreate = location.pathname.startsWith("/distributed-uptime/create");
const { monitorId } = useParams();
const isCreate = typeof monitorId === "undefined";
const BREADCRUMBS = [
{ name: `distributed uptime`, path: "/distributed-uptime" },
{ name: isCreate ? "create" : "configure", path: `` },
];
// Redux state
const { user, authToken } = useSelector((state) => state.auth);
const isLoading = useSelector((state) => state.uptimeMonitors.isLoading);
// Local state
const [https, setHttps] = useState(true);
const [notifications, setNotifications] = useState([]);
const [monitor, setMonitor] = useState({
const [form, setForm] = useState({
type: "distributed_http",
name: "",
url: "",
@@ -55,12 +64,37 @@ const CreateDistributedUptime = () => {
//utils
const theme = useTheme();
const dispatch = useDispatch();
const navigate = useNavigate();
const [createDistributedUptimeMonitor, isLoading, networkError] =
useCreateDistributedUptimeMonitor({ isCreate, monitorId });
const [monitor, monitorIsLoading, monitorNetworkError] = useMonitorFetch({
authToken,
monitorId,
isCreate,
});
// Effect to set monitor to fetched monitor
useEffect(() => {
if (typeof monitor !== "undefined") {
const parsedUrl = parseUrl(monitor?.url);
const protocol = parsedUrl?.protocol?.replace(":", "") || "";
setHttps(protocol === "https");
const newForm = {
name: monitor.name,
interval: monitor.interval / MS_PER_MINUTE,
url: parsedUrl.host,
type: monitor.type,
};
setForm(newForm);
}
}, [monitor]);
// Handlers
const handleCreateMonitor = async (event) => {
const monitorToSubmit = { ...monitor };
const handleCreateMonitor = async () => {
const monitorToSubmit = { ...form };
// Prepend protocol to url
monitorToSubmit.url = `http${https ? "s" : ""}://` + monitorToSubmit.url;
@@ -68,7 +102,6 @@ const CreateDistributedUptime = () => {
const { error } = monitorValidation.validate(monitorToSubmit, {
abortEarly: false,
});
if (error) {
const newErrors = {};
error.details.forEach((err) => {
@@ -80,16 +113,14 @@ const CreateDistributedUptime = () => {
}
// Append needed fields
monitorToSubmit.description = monitor.name;
monitorToSubmit.interval = monitor.interval * MS_PER_MINUTE;
monitorToSubmit.description = form.name;
monitorToSubmit.interval = form.interval * MS_PER_MINUTE;
monitorToSubmit.teamId = user.teamId;
monitorToSubmit.userId = user._id;
monitorToSubmit.notifications = notifications;
const action = await dispatch(
createUptimeMonitor({ authToken, monitor: monitorToSubmit })
);
if (action.meta.requestStatus === "fulfilled") {
const success = await createDistributedUptimeMonitor({ form: monitorToSubmit });
if (success) {
createToast({ body: "Monitor created successfully!" });
navigate("/distributed-uptime");
} else {
@@ -98,9 +129,10 @@ const CreateDistributedUptime = () => {
};
const handleChange = (event) => {
const { name, value } = event.target;
setMonitor({
...monitor,
let { name, value } = event.target;
setForm({
...form,
[name]: value,
});
const { error } = monitorValidation.validate(
@@ -178,7 +210,8 @@ const CreateDistributedUptime = () => {
label="URL to monitor"
https={https}
placeholder={"www.google.com"}
value={monitor.url}
disabled={!isCreate}
value={form.url}
name="url"
onChange={handleChange}
error={errors["url"] ? true : false}
@@ -190,7 +223,7 @@ const CreateDistributedUptime = () => {
label="Display name"
isOptional={true}
placeholder={"Google"}
value={monitor.name}
value={form.name}
name="name"
onChange={handleChange}
error={errors["name"] ? true : false}
@@ -217,8 +250,11 @@ const CreateDistributedUptime = () => {
checked={true}
onChange={handleChange}
/>
{monitor.type === "http" || monitor.type === "distributed_http" ? (
<ButtonGroup sx={{ ml: theme.spacing(16) }}>
{form.type === "http" || form.type === "distributed_http" ? (
<ButtonGroup
disabled={!isCreate}
sx={{ ml: theme.spacing(16) }}
>
<Button
variant="group"
filled={https.toString()}
@@ -282,7 +318,7 @@ const CreateDistributedUptime = () => {
id="monitor-interval"
label="Check frequency"
name="interval"
value={monitor.interval || 1}
value={form.interval}
onChange={handleChange}
items={SELECT_VALUES}
/>
@@ -299,7 +335,7 @@ const CreateDistributedUptime = () => {
disabled={!Object.values(errors).every((value) => value === undefined)}
loading={isLoading}
>
Create monitor
{isCreate ? "Create monitor" : "Configure monitor"}
</LoadingButton>
</Stack>
</Stack>

View File

@@ -0,0 +1,91 @@
// Components
import { Box, Stack, Typography, Button } from "@mui/material";
import Image from "../../../../../Components/Image";
import SettingsIcon from "../../../../../assets/icons/settings-bold.svg?react";
//Utils
import { useTheme } from "@mui/material/styles";
import { useNavigate } from "react-router-dom";
import { useLocation } from "react-router-dom";
import PropTypes from "prop-types";
const Controls = ({ isDeleteOpen, setIsDeleteOpen, isDeleting, monitorId }) => {
const theme = useTheme();
const navigate = useNavigate();
return (
<Stack
direction="row"
gap={theme.spacing(2)}
>
<Box>
<Button
variant="contained"
color="error"
onClick={() => setIsDeleteOpen(!isDeleteOpen)}
loading={isDeleting}
>
Delete
</Button>
</Box>
<Box>
<Button
variant="contained"
color="secondary"
onClick={() => {
navigate(`/distributed-uptime/configure/${monitorId}`);
}}
sx={{
px: theme.spacing(5),
"& svg": {
mr: theme.spacing(3),
"& path": {
stroke: theme.palette.secondary.contrastText,
},
},
}}
>
<SettingsIcon /> Configure
</Button>
</Box>
</Stack>
);
};
Controls.propTypes = {
isDeleting: PropTypes.bool,
monitorId: PropTypes.string,
isDeleteOpen: PropTypes.bool.isRequired,
setIsDeleteOpen: PropTypes.func.isRequired,
};
const ControlsHeader = ({ isDeleting, isDeleteOpen, setIsDeleteOpen, monitorId }) => {
const theme = useTheme();
return (
<Stack
alignSelf="flex-start"
direction="row"
width="100%"
gap={theme.spacing(2)}
justifyContent="flex-end"
alignItems="flex-end"
>
<Controls
isDeleting={isDeleting}
isDeleteOpen={isDeleteOpen}
setIsDeleteOpen={setIsDeleteOpen}
monitorId={monitorId}
/>
</Stack>
);
};
ControlsHeader.propTypes = {
monitorId: PropTypes.string,
isDeleting: PropTypes.bool,
isDeleteOpen: PropTypes.bool.isRequired,
setIsDeleteOpen: PropTypes.func.isRequired,
};
export default ControlsHeader;

View File

@@ -0,0 +1,27 @@
import { useSelector } from "react-redux";
import { useState } from "react";
import { networkService } from "../../../../main";
import { createToast } from "../../../../Utils/toastUtils";
const useDeleteMonitor = ({ monitorId }) => {
const [isLoading, setIsLoading] = useState(false);
const { authToken } = useSelector((state) => state.auth);
const deleteMonitor = async () => {
try {
setIsLoading(true);
await networkService.deleteMonitorById({ authToken, monitorId });
return true;
} catch (error) {
createToast({
body: error.message,
});
return false;
} finally {
setIsLoading(false);
}
};
return [deleteMonitor, isLoading];
};
export { useDeleteMonitor };

View File

@@ -12,23 +12,31 @@ import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader";
import GenericFallback from "../../../Components/GenericFallback";
import MonitorCreateHeader from "../../../Components/MonitorCreateHeader";
import SkeletonLayout from "./Components/Skeleton";
import ControlsHeader from "./Components/ControlsHeader";
import Dialog from "../../../Components/Dialog";
//Utils
import { useTheme } from "@mui/material/styles";
import { useState } from "react";
import { useParams } from "react-router-dom";
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
import { useSubscribeToDetails } from "./Hooks/useSubscribeToDetails";
import { useDeleteMonitor } from "./Hooks/useDeleteMonitor";
import { useNavigate } from "react-router-dom";
const DistributedUptimeDetails = () => {
const { monitorId } = useParams();
// Local State
const [dateRange, setDateRange] = useState("day");
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
// Utils
const theme = useTheme();
const isAdmin = useIsAdmin();
const navigate = useNavigate();
const [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger] =
useSubscribeToDetails({ monitorId, dateRange });
const [deleteMonitor, isDeleting] = useDeleteMonitor({ monitorId });
// Constants
const BREADCRUMBS = [
{ name: "Distributed Uptime", path: "/distributed-uptime" },
@@ -76,6 +84,12 @@ const DistributedUptimeDetails = () => {
isAdmin={isAdmin}
path={`/status/distributed/create/${monitorId}`}
/>
<ControlsHeader
isDeleting={isDeleting}
isDeleteOpen={isDeleteOpen}
setIsDeleteOpen={setIsDeleteOpen}
monitorId={monitorId}
/>
<MonitorHeader monitor={monitor} />
<StatBoxes
monitor={monitor}
@@ -107,6 +121,21 @@ const DistributedUptimeDetails = () => {
/>
</Stack>
<Footer />
<Dialog
title="Do you want to delete this monitor?"
onConfirm={() => {
deleteMonitor();
setIsDeleteOpen(false);
navigate("/distributed-uptime");
}}
onCancel={() => {
setIsDeleteOpen(false);
}}
open={isDeleteOpen}
confirmationButtonLabel="Yes, delete monitor"
description="Once deleted, your monitor cannot be retrieved."
isLoading={isDeleting || isLoading}
/>
</Stack>
);
};

View File

@@ -29,7 +29,7 @@ const MonitorTable = ({ isLoading, monitors }) => {
<Host
key={row._id}
url={row.url}
title={row.title}
title={row.name}
percentageColor={row.percentageColor}
percentage={row.percentage}
/>

View File

@@ -41,7 +41,7 @@ const DistributedUptimeMonitors = () => {
);
}
if (typeof monitorsSummary !== "undefined" && monitorsSummary.totalMonitors === 0) {
if (typeof monitorsSummary === "undefined" || monitorsSummary.totalMonitors === 0) {
return (
<Fallback
vowelStart={false}

View File

@@ -13,7 +13,6 @@ import { useState, useEffect } from "react";
import { useParams } from "react-router-dom";
import { useStatusPageFetch } from "../../StatusPage/Status/Hooks/useStatusPageFetch";
import { useCreateStatusPage } from "../../StatusPage/Create/Hooks/useCreateStatusPage";
import { useLocation } from "react-router-dom";
import { statusPageValidation } from "../../../Validation/validation";
import { buildErrors } from "../../../Validation/error";
import { createToast } from "../../../Utils/toastUtils";
@@ -23,8 +22,7 @@ const CreateStatus = () => {
const theme = useTheme();
const { monitorId, url } = useParams();
const navigate = useNavigate();
const location = useLocation();
const isCreate = location.pathname.startsWith("/status/distributed/create");
const isCreate = typeof url === "undefined";
const [createStatusPage, isLoading, networkError] = useCreateStatusPage(isCreate);
const [statusPage, statusPageMonitors, statusPageIsLoading, statusPageNetworkError] =

View File

@@ -12,7 +12,6 @@ import { useMonitorsFetch } from "./Hooks/useMonitorsFetch";
import { useCreateStatusPage } from "./Hooks/useCreateStatusPage";
import { createToast } from "../../../Utils/toastUtils";
import { useNavigate } from "react-router-dom";
import { useLocation } from "react-router-dom";
import { useStatusPageFetch } from "../Status/Hooks/useStatusPageFetch";
import { useParams } from "react-router-dom";
@@ -47,8 +46,7 @@ const CreateStatusPage = () => {
const intervalRef = useRef(null);
// Setup
const location = useLocation();
const isCreate = location.pathname === "/status/uptime/create";
const isCreate = typeof url === "undefined";
//Utils
const theme = useTheme();

View File

@@ -101,7 +101,7 @@ const UptimeDataTable = ({
<Host
key={row._id}
url={row.url}
title={row.title}
title={row.name}
percentageColor={row.percentageColor}
percentage={row.percentage}
/>

View File

@@ -107,6 +107,14 @@ const Routes = () => {
</ProtectedDistributedUptimeRoute>
}
/>
<Route
path="/distributed-uptime/configure/:monitorId"
element={
<ProtectedDistributedUptimeRoute>
<CreateDistributedUptime />
</ProtectedDistributedUptimeRoute>
}
/>
<Route
path="/distributed-uptime/:monitorId"
element={

View File

@@ -99,9 +99,11 @@ class NetworkService {
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
*/
async createMonitor(config) {
return this.axiosInstance.post(`/monitors`, config.monitor, {
const { authToken, monitor } = config;
return this.axiosInstance.post(`/monitors`, monitor, {
headers: {
Authorization: `Bearer ${config.authToken}`,
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
});
@@ -284,9 +286,16 @@ class NetworkService {
* @returns {Promise<AxiosResponse>} The response from the axios PUT request.
*/
async updateMonitor(config) {
return this.axiosInstance.put(`/monitors/${config.monitorId}`, config.updatedFields, {
const { authToken, monitorId, monitor } = config;
const updatedFields = {
name: monitor.name,
description: monitor.description,
interval: monitor.interval,
notifications: monitor.notifications,
};
return this.axiosInstance.put(`/monitors/${monitorId}`, updatedFields, {
headers: {
Authorization: `Bearer ${config.authToken}`,
Authorization: `Bearer ${authToken}`,
"Content-Type": "application/json",
},
});

View File

@@ -295,12 +295,49 @@ class MonitorController {
try {
const monitor = await this.db.deleteMonitor(req, res, next);
// Delete associated checks,alerts,and notifications
try {
await this.jobQueue.deleteJob(monitor);
await this.db.deleteChecks(monitor._id);
await this.db.deletePageSpeedChecksByMonitorId(monitor._id);
await this.db.deleteNotificationsByMonitorId(monitor._id);
await this.db.deleteHardwareChecksByMonitorId(monitor._id);
const operations = [
{ name: "deleteJob", fn: () => this.jobQueue.deleteJob(monitor) },
{ name: "deleteChecks", fn: () => this.db.deleteChecks(monitor._id) },
{
name: "deletePageSpeedChecks",
fn: () => this.db.deletePageSpeedChecksByMonitorId(monitor._id),
},
{
name: "deleteNotifications",
fn: () => this.db.deleteNotificationsByMonitorId(monitor._id),
},
{
name: "deleteHardwareChecks",
fn: () => this.db.deleteHardwareChecksByMonitorId(monitor._id),
},
{
name: "deleteDistributedUptimeChecks",
fn: () => this.db.deleteDistributedChecksByMonitorId(monitor._id),
},
// TODO We don't actually want to delete the status page if there are other monitors in it
// We actually just want to remove the monitor being deleted from the status page.
// Only delete he status page if there are no other monitors in it.
{
name: "deleteStatusPages",
fn: () => this.db.deleteStatusPagesByMonitorId(monitor._id),
},
];
const results = await Promise.allSettled(operations.map((op) => op.fn()));
results.forEach((result, index) => {
if (result.status === "rejected") {
const operationName = operations[index].name;
logger.error({
message: `Failed to ${operationName} for monitor ${monitor._id}`,
service: SERVICE_NAME,
method: "deleteMonitor",
stack: result.reason.stack,
});
}
});
} catch (error) {
logger.error({
message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`,
@@ -390,10 +427,10 @@ class MonitorController {
await Promise.all(
notifications &&
notifications.map(async (notification) => {
notification.monitorId = editedMonitor._id;
await this.db.createNotification(notification);
})
notifications.map(async (notification) => {
notification.monitorId = editedMonitor._id;
await this.db.createNotification(notification);
})
);
// Delete the old job(editedMonitor has the same ID as the old monitor)

View File

@@ -12,4 +12,15 @@ const createDistributedCheck = async (checkData) => {
}
};
export { createDistributedCheck };
const deleteDistributedChecksByMonitorId = async (monitorId) => {
try {
const result = await DistributedUptimeCheck.deleteMany({ monitorId });
return result.deletedCount;
} catch (error) {
error.service = SERVICE_NAME;
error.method = "deleteDistributedChecksByMonitorId";
throw error;
}
};
export { createDistributedCheck, deleteDistributedChecksByMonitorId };

View File

@@ -313,7 +313,7 @@ const calculateGroupStats = (group) => {
avgResponseTime:
checksWithResponseTime.length > 0
? checksWithResponseTime.reduce((sum, check) => sum + check.responseTime, 0) /
checksWithResponseTime.length
checksWithResponseTime.length
: 0,
};
};
@@ -372,7 +372,10 @@ const getUptimeDetailsById = async (req) => {
const getDistributedUptimeDetailsById = async (req) => {
try {
const { monitorId } = req.params;
const { monitorId } = req?.params ?? {};
if (typeof monitorId === "undefined") {
throw new Error();
}
const monitor = await Monitor.findById(monitorId);
if (monitor === null || monitor === undefined) {
throw new Error(this.stringService.dbFindMonitorById(monitorId));
@@ -605,98 +608,98 @@ const getMonitorsByTeamId = async (req) => {
filteredMonitors: [
...(filter !== undefined
? [
{
$match: {
$or: [
{ name: { $regex: filter, $options: "i" } },
{ url: { $regex: filter, $options: "i" } },
],
{
$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"] },
{
$lookup: {
from: "checks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
},
},
},
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "standardchecks",
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "standardchecks",
},
},
},
]
]
: []),
...(limit
? [
{
$lookup: {
from: "pagespeedchecks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
{
$lookup: {
from: "pagespeedchecks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
},
},
},
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "pagespeedchecks",
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "pagespeedchecks",
},
},
},
]
]
: []),
...(limit
? [
{
$lookup: {
from: "hardwarechecks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
{
$lookup: {
from: "hardwarechecks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
},
},
},
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "hardwarechecks",
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "hardwarechecks",
},
},
},
]
]
: []),
...(limit
? [
{
$lookup: {
from: "distributeduptimechecks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
{
$lookup: {
from: "distributeduptimechecks",
let: { monitorId: "$_id" },
pipeline: [
{
$match: {
$expr: { $eq: ["$monitorId", "$$monitorId"] },
},
},
},
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "distributeduptimechecks",
{ $sort: { createdAt: -1 } },
...(limit ? [{ $limit: limit }] : []),
],
as: "distributeduptimechecks",
},
},
},
]
]
: []),
{

View File

@@ -190,6 +190,16 @@ const deleteStatusPage = async (url) => {
}
};
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,
@@ -197,4 +207,5 @@ export {
getStatusPage,
getStatusPageByUrl,
deleteStatusPage,
deleteStatusPagesByMonitorId,
};