mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-26 03:39:27 -06:00
Merge pull request #1752 from bluewave-labs/feat/fe/config-du-monitor
feat: fe/config du monitor
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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 };
|
||||
@@ -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;
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
@@ -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 };
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -41,7 +41,7 @@ const DistributedUptimeMonitors = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof monitorsSummary !== "undefined" && monitorsSummary.totalMonitors === 0) {
|
||||
if (typeof monitorsSummary === "undefined" || monitorsSummary.totalMonitors === 0) {
|
||||
return (
|
||||
<Fallback
|
||||
vowelStart={false}
|
||||
|
||||
@@ -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] =
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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}
|
||||
/>
|
||||
|
||||
@@ -107,6 +107,14 @@ const Routes = () => {
|
||||
</ProtectedDistributedUptimeRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/distributed-uptime/configure/:monitorId"
|
||||
element={
|
||||
<ProtectedDistributedUptimeRoute>
|
||||
<CreateDistributedUptime />
|
||||
</ProtectedDistributedUptimeRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/distributed-uptime/:monitorId"
|
||||
element={
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
});
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 };
|
||||
|
||||
@@ -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",
|
||||
},
|
||||
},
|
||||
},
|
||||
]
|
||||
]
|
||||
: []),
|
||||
|
||||
{
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user