From 1574624f690031c50efa705bcdccc8657882d2ad Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 12 Jun 2025 09:37:51 +0800 Subject: [PATCH 01/36] add a background component --- client/src/App.jsx | 16 ++---- .../Components/Layouts/AppLayout/index.jsx | 30 ++++++++++++ client/src/Components/Sidebar/index.jsx | 1 - client/src/Components/StarPrompt/index.jsx | 1 - client/src/Utils/Theme/constants.js | 7 +++ client/src/assets/Images/background.svg | 49 +++++++++++++++++++ 6 files changed, 91 insertions(+), 13 deletions(-) create mode 100644 client/src/Components/Layouts/AppLayout/index.jsx create mode 100644 client/src/assets/Images/background.svg diff --git a/client/src/App.jsx b/client/src/App.jsx index 7fb63ee61..b599890f1 100644 --- a/client/src/App.jsx +++ b/client/src/App.jsx @@ -10,6 +10,7 @@ import { logger } from "./Utils/Logger"; // Import the logger import { networkService } from "./main"; import { Routes } from "./Routes"; import WalletProvider from "./Components/WalletProvider"; +import AppLayout from "./Components/Layouts/AppLayout"; function App() { const mode = useSelector((state) => state.ui.mode); @@ -27,17 +28,10 @@ function App() { - { - return { - body: { - backgroundImage: `radial-gradient(circle, ${palette.gradient.color1}, ${palette.gradient.color2}, ${palette.gradient.color3}, ${palette.gradient.color4}, ${palette.gradient.color5})`, - color: palette.primary.contrastText, - }, - }; - }} - /> - + + + + diff --git a/client/src/Components/Layouts/AppLayout/index.jsx b/client/src/Components/Layouts/AppLayout/index.jsx new file mode 100644 index 000000000..564012d72 --- /dev/null +++ b/client/src/Components/Layouts/AppLayout/index.jsx @@ -0,0 +1,30 @@ +import Box from "@mui/material/Box"; +import PropTypes from "prop-types"; +import { useTheme } from "@emotion/react"; +import BackgroundSVG from "../../../assets/Images/background.svg"; + +const AppLayout = ({ children }) => { + const theme = useTheme(); + + return ( + + {children} + + ); +}; + +AppLayout.propTypes = { + children: PropTypes.node, +}; + +export default AppLayout; diff --git a/client/src/Components/Sidebar/index.jsx b/client/src/Components/Sidebar/index.jsx index 6be67a43d..f487148f5 100644 --- a/client/src/Components/Sidebar/index.jsx +++ b/client/src/Components/Sidebar/index.jsx @@ -231,7 +231,6 @@ function Sidebar() { borderRight: `1px solid ${theme.palette.primary.lowContrast}`, borderColor: theme.palette.primary.lowContrast, borderRadius: 0, - backgroundColor: theme.palette.primary.main, "& :is(p, span, .MuiListSubheader-root)": { /* Text color for unselected menu items and menu headings diff --git a/client/src/Components/StarPrompt/index.jsx b/client/src/Components/StarPrompt/index.jsx index 8bf434085..64fce1d3f 100644 --- a/client/src/Components/StarPrompt/index.jsx +++ b/client/src/Components/StarPrompt/index.jsx @@ -33,7 +33,6 @@ const StarPrompt = ({ repoUrl = "https://github.com/bluewave-labs/checkmate" }) borderBottom: `1px solid ${theme.palette.primary.lowContrast}`, borderRadius: 0, gap: theme.spacing(1.5), - backgroundColor: theme.palette.primary.main, }} > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From 948e5e932b824037799a10b35c4349af814bb3f7 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 12 Jun 2025 12:44:35 +0800 Subject: [PATCH 02/36] refactor hooks to common util --- .../checkHooks.js} | 15 +- client/src/Hooks/monitorHooks.js | 379 ++++++++++++++++++ .../src/Hooks/useFetchMonitorsWithChecks.js | 72 ---- .../src/Hooks/useFetchMonitorsWithSummary.js | 37 -- client/src/Hooks/useFetchUptimeMonitorById.js | 35 -- .../src/Hooks/useFetchUptimeMonitorDetails.js | 35 -- client/src/Hooks/useMonitorControls.js | 28 -- client/src/Hooks/useMonitorUtils.js | 22 +- .../Hooks/useHardwareMonitorsFetch.jsx | 38 -- .../Monitors/Hooks/useMonitorFetch.jsx | 46 --- .../Details/Hooks/useMonitorFetch.jsx | 39 -- .../Monitors/Hooks/useMonitorsFetch.jsx | 44 -- .../Uptime/Details/Hooks/useMonitorFetch.jsx | 33 -- .../Monitors/Hooks/useMonitorsFetch.jsx | 77 ---- .../Pages/Uptime/Monitors/Hooks/useUtils.jsx | 110 ----- 15 files changed, 405 insertions(+), 605 deletions(-) rename client/src/{Pages/Uptime/Details/Hooks/useChecksFetch.jsx => Hooks/checkHooks.js} (81%) create mode 100644 client/src/Hooks/monitorHooks.js delete mode 100644 client/src/Hooks/useFetchMonitorsWithChecks.js delete mode 100644 client/src/Hooks/useFetchMonitorsWithSummary.js delete mode 100644 client/src/Hooks/useFetchUptimeMonitorById.js delete mode 100644 client/src/Hooks/useFetchUptimeMonitorDetails.js delete mode 100644 client/src/Hooks/useMonitorControls.js delete mode 100644 client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx delete mode 100644 client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx delete mode 100644 client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx delete mode 100644 client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx delete mode 100644 client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx delete mode 100644 client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx delete mode 100644 client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx diff --git a/client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx b/client/src/Hooks/checkHooks.js similarity index 81% rename from client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx rename to client/src/Hooks/checkHooks.js index 586cc8359..cc942b294 100644 --- a/client/src/Pages/Uptime/Details/Hooks/useChecksFetch.jsx +++ b/client/src/Hooks/checkHooks.js @@ -1,14 +1,9 @@ import { useState } from "react"; import { useEffect } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; -export const useChecksFetch = ({ - monitorId, - monitorType, - dateRange, - page, - rowsPerPage, -}) => { +import { networkService } from "../main"; +import { createToast } from "../Utils/toastUtils"; + +const useFetchChecks = ({ monitorId, monitorType, dateRange, page, rowsPerPage }) => { const [checks, setChecks] = useState(undefined); const [checksCount, setChecksCount] = useState(undefined); const [isLoading, setIsLoading] = useState(false); @@ -47,4 +42,4 @@ export const useChecksFetch = ({ return [checks, checksCount, isLoading, networkError]; }; -export default useChecksFetch; +export { useFetchChecks }; diff --git a/client/src/Hooks/monitorHooks.js b/client/src/Hooks/monitorHooks.js new file mode 100644 index 000000000..a39fd3c5e --- /dev/null +++ b/client/src/Hooks/monitorHooks.js @@ -0,0 +1,379 @@ +import { useEffect, useState } from "react"; +import { networkService } from "../main"; +import { createToast } from "../Utils/toastUtils"; +import { useTheme } from "@emotion/react"; +import { useMonitorUtils } from "./useMonitorUtils"; +import { useNavigate } from "react-router-dom"; + +const useFetchMonitorsWithSummary = ({ teamId, types, monitorUpdateTrigger }) => { + const [isLoading, setIsLoading] = useState(false); + const [monitors, setMonitors] = useState(undefined); + const [monitorsSummary, setMonitorsSummary] = useState(undefined); + const [networkError, setNetworkError] = useState(false); + + useEffect(() => { + const fetchMonitors = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorsWithSummaryByTeamId({ + teamId, + types, + }); + const { monitors, summary } = res?.data?.data ?? {}; + setMonitors(monitors); + setMonitorsSummary(summary); + } catch (error) { + console.error(error); + setNetworkError(true); + createToast({ + body: error.message, + }); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [teamId, types, monitorUpdateTrigger]); + return [monitors, monitorsSummary, isLoading, networkError]; +}; + +const useFetchMonitorsWithChecks = ({ + teamId, + types, + limit, + page, + rowsPerPage, + filter, + field, + order, + monitorUpdateTrigger, +}) => { + const [isLoading, setIsLoading] = useState(false); + const [count, setCount] = useState(undefined); + const [monitors, setMonitors] = useState(undefined); + const [networkError, setNetworkError] = useState(false); + + const theme = useTheme(); + const { getMonitorWithPercentage } = useMonitorUtils(); + useEffect(() => { + const fetchMonitors = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorsWithChecksByTeamId({ + teamId, + limit, + types, + page, + rowsPerPage, + filter, + field, + order, + }); + const { count, monitors } = res?.data?.data ?? {}; + const mappedMonitors = monitors.map((monitor) => + getMonitorWithPercentage(monitor, theme) + ); + setMonitors(mappedMonitors); + setCount(count?.monitorsCount ?? 0); + } catch (error) { + console.error(error); + setNetworkError(true); + createToast({ + body: error.message, + }); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [ + field, + filter, + getMonitorWithPercentage, + limit, + order, + page, + rowsPerPage, + teamId, + theme, + types, + monitorUpdateTrigger, + ]); + return [monitors, count, isLoading, networkError]; +}; + +const useFetchMonitorsByTeamId = ({ + teamId, + types, + limit, + page, + rowsPerPage, + filter, + field, + order, + updateTrigger, +}) => { + const [isLoading, setIsLoading] = useState(false); + const [monitors, setMonitors] = useState(undefined); + const [summary, setSummary] = useState(undefined); + const [networkError, setNetworkError] = useState(false); + + useEffect(() => { + const fetchMonitors = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorsByTeamId({ + teamId: teamId, + limit, + types, + page, + rowsPerPage, + filter, + field, + order, + }); + if (res?.data?.data?.filteredMonitors) { + setMonitors(res.data.data.filteredMonitors); + setSummary(res.data.data.summary); + } + } catch (error) { + setNetworkError(true); + createToast({ + body: error.message, + }); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [teamId, types, limit, page, rowsPerPage, filter, field, order, updateTrigger]); + return [monitors, summary, isLoading, networkError]; +}; + +const useFetchStatsByMonitorId = ({ + monitorId, + sortOrder, + limit, + dateRange, + numToDisplay, + normalize, +}) => { + const [monitor, setMonitor] = useState(undefined); + const [audits, setAudits] = useState(undefined); + const [isLoading, setIsLoading] = useState(true); + const [networkError, setNetworkError] = useState(false); + useEffect(() => { + const fetchMonitor = async () => { + try { + setIsLoading(true); + const res = await networkService.getStatsByMonitorId({ + monitorId: monitorId, + sortOrder, + limit, + dateRange, + numToDisplay, + normalize, + }); + setMonitor(res?.data?.data ?? undefined); + setAudits(res?.data?.data?.checks?.[0]?.audits ?? undefined); + } catch (error) { + setNetworkError(true); + createToast({ body: error.message }); + } finally { + setIsLoading(false); + } + }; + fetchMonitor(); + }, [monitorId, dateRange, numToDisplay, normalize, sortOrder, limit]); + return [monitor, audits, isLoading, networkError]; +}; + +const useFetchMonitorById = ({ monitorId, setMonitor, updateTrigger }) => { + const [isLoading, setIsLoading] = useState(true); + useEffect(() => { + const fetchMonitor = async () => { + try { + setIsLoading(true); + const res = await networkService.getMonitorById({ monitorId: monitorId }); + setMonitor(res.data.data); + } catch (error) { + createToast({ body: error.message }); + } finally { + setIsLoading(false); + } + }; + fetchMonitor(); + }, [monitorId, setMonitor, updateTrigger]); + return [isLoading]; +}; + +const useFetchHardwareMonitorById = ({ monitorId, dateRange }) => { + const [isLoading, setIsLoading] = useState(true); + const [networkError, setNetworkError] = useState(false); + const [monitor, setMonitor] = useState(undefined); + + useEffect(() => { + const fetchMonitor = async () => { + try { + if (!monitorId) { + return { monitor: undefined, isLoading: false, networkError: undefined }; + } + const response = await networkService.getHardwareDetailsByMonitorId({ + monitorId: monitorId, + dateRange: dateRange, + }); + setMonitor(response.data.data); + } catch (error) { + setNetworkError(true); + } finally { + setIsLoading(false); + } + }; + fetchMonitor(); + }, [monitorId, dateRange]); + return [monitor, isLoading, networkError]; +}; + +const useFetchUptimeMonitorById = ({ monitorId, dateRange, trigger }) => { + const [networkError, setNetworkError] = useState(false); + const [isLoading, setIsLoading] = useState(true); + const [monitor, setMonitor] = useState(undefined); + const [monitorStats, setMonitorStats] = useState(undefined); + useEffect(() => { + const fetchMonitors = async () => { + try { + const res = await networkService.getUptimeDetailsById({ + monitorId: monitorId, + dateRange: dateRange, + normalize: true, + }); + const { monitorData, monitorStats } = res?.data?.data ?? {}; + setMonitor(monitorData); + setMonitorStats(monitorStats); + } catch (error) { + setNetworkError(true); + createToast({ body: error.message }); + } finally { + setIsLoading(false); + } + }; + fetchMonitors(); + }, [dateRange, monitorId, trigger]); + return [monitor, monitorStats, isLoading, networkError]; +}; + +const useCreateMonitor = () => { + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + const createMonitor = async ({ monitor, redirect }) => { + try { + setIsLoading(true); + await networkService.createMonitor({ monitor }); + createToast({ body: "Monitor created successfully!" }); + if (redirect) { + navigate(redirect); + } + } catch (error) { + createToast({ body: "Failed to create monitor." }); + } finally { + setIsLoading(false); + } + }; + return [createMonitor, isLoading]; +}; + +const useDeleteMonitor = () => { + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + const deleteMonitor = async ({ monitor, redirect }) => { + try { + setIsLoading(true); + await networkService.deleteMonitorById({ monitorId: monitor._id }); + createToast({ body: "Monitor deleted successfully!" }); + if (redirect) { + navigate(redirect); + } + } catch (error) { + createToast({ body: "Failed to delete monitor." }); + } finally { + setIsLoading(false); + } + }; + return [deleteMonitor, isLoading]; +}; + +const useUpdateMonitor = () => { + const [isLoading, setIsLoading] = useState(false); + const navigate = useNavigate(); + const updateMonitor = async ({ monitor, redirect }) => { + try { + setIsLoading(true); + const updatedFields = { + name: monitor.name, + description: monitor.description, + interval: monitor.interval, + notifications: monitor.notifications, + matchMethod: monitor.matchMethod, + expectedValue: monitor.expectedValue, + ignoreTlsErrors: monitor.ignoreTlsErrors, + jsonPath: monitor.jsonPath, + ...(monitor.type === "port" && { port: monitor.port }), + ...(monitor.type === "hardware" && { + thresholds: monitor.thresholds, + secret: monitor.secret, + }), + }; + await networkService.updateMonitor({ + monitorId: monitor._id, + updatedFields, + }); + + createToast({ body: "Monitor updated successfully!" }); + if (redirect) { + navigate(redirect); + } + } catch (error) { + createToast({ body: "Failed to update monitor." }); + } finally { + setIsLoading(false); + } + }; + return [updateMonitor, isLoading]; +}; + +const usePauseMonitor = () => { + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(undefined); + const pauseMonitor = async ({ monitorId, triggerUpdate }) => { + try { + setIsLoading(true); + const res = await networkService.pauseMonitorById({ monitorId }); + createToast({ + body: res.data.data.isActive + ? "Monitor resumed successfully" + : "Monitor paused successfully", + }); + triggerUpdate(); + } catch (error) { + setError(error); + } finally { + setIsLoading(false); + } + }; + + return [pauseMonitor, isLoading, error]; +}; + +export { + useFetchMonitorsWithSummary, + useFetchMonitorsWithChecks, + useFetchMonitorsByTeamId, + useFetchStatsByMonitorId, + useFetchMonitorById, + useFetchUptimeMonitorById, + useFetchHardwareMonitorById, + useCreateMonitor, + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, +}; diff --git a/client/src/Hooks/useFetchMonitorsWithChecks.js b/client/src/Hooks/useFetchMonitorsWithChecks.js deleted file mode 100644 index a6f0437b9..000000000 --- a/client/src/Hooks/useFetchMonitorsWithChecks.js +++ /dev/null @@ -1,72 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../main"; -import { createToast } from "../Utils/toastUtils"; -import { useTheme } from "@emotion/react"; -import { useMonitorUtils } from "./useMonitorUtils"; - -export const useFetchMonitorsWithChecks = ({ - teamId, - types, - limit, - page, - rowsPerPage, - filter, - field, - order, - monitorUpdateTrigger, -}) => { - const [isLoading, setIsLoading] = useState(false); - const [count, setCount] = useState(undefined); - const [monitors, setMonitors] = useState(undefined); - const [networkError, setNetworkError] = useState(false); - - const theme = useTheme(); - const { getMonitorWithPercentage } = useMonitorUtils(); - useEffect(() => { - const fetchMonitors = async () => { - try { - setIsLoading(true); - const res = await networkService.getMonitorsWithChecksByTeamId({ - teamId, - limit, - types, - page, - rowsPerPage, - filter, - field, - order, - }); - const { count, monitors } = res?.data?.data ?? {}; - const mappedMonitors = monitors.map((monitor) => - getMonitorWithPercentage(monitor, theme) - ); - setMonitors(mappedMonitors); - setCount(count?.monitorsCount ?? 0); - } catch (error) { - console.error(error); - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - fetchMonitors(); - }, [ - field, - filter, - getMonitorWithPercentage, - limit, - order, - page, - rowsPerPage, - teamId, - theme, - types, - monitorUpdateTrigger, - ]); - return [monitors, count, isLoading, networkError]; -}; - -export default useFetchMonitorsWithChecks; diff --git a/client/src/Hooks/useFetchMonitorsWithSummary.js b/client/src/Hooks/useFetchMonitorsWithSummary.js deleted file mode 100644 index cd21271d4..000000000 --- a/client/src/Hooks/useFetchMonitorsWithSummary.js +++ /dev/null @@ -1,37 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../main"; -import { createToast } from "../Utils/toastUtils"; - -export const useFetchMonitorsWithSummary = ({ teamId, types, monitorUpdateTrigger }) => { - const [isLoading, setIsLoading] = useState(false); - const [monitors, setMonitors] = useState(undefined); - const [monitorsSummary, setMonitorsSummary] = useState(undefined); - const [networkError, setNetworkError] = useState(false); - - useEffect(() => { - const fetchMonitors = async () => { - try { - setIsLoading(true); - const res = await networkService.getMonitorsWithSummaryByTeamId({ - teamId, - types, - }); - const { monitors, summary } = res?.data?.data ?? {}; - setMonitors(monitors); - setMonitorsSummary(summary); - } catch (error) { - console.error(error); - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - fetchMonitors(); - }, [teamId, types, monitorUpdateTrigger]); - return [monitors, monitorsSummary, isLoading, networkError]; -}; - -export default useFetchMonitorsWithSummary; diff --git a/client/src/Hooks/useFetchUptimeMonitorById.js b/client/src/Hooks/useFetchUptimeMonitorById.js deleted file mode 100644 index cb344569c..000000000 --- a/client/src/Hooks/useFetchUptimeMonitorById.js +++ /dev/null @@ -1,35 +0,0 @@ -import { useState, useEffect } from "react"; -import { useDispatch } from "react-redux"; -import { getUptimeMonitorById } from "../Features/UptimeMonitors/uptimeMonitorsSlice"; -import { useNavigate } from "react-router"; - -const useFetchUptimeMonitorById = (monitorId, updateTrigger) => { - const [isLoading, setIsLoading] = useState(true); - const [error, setError] = useState(null); - const [monitor, setMonitor] = useState(null); - const navigate = useNavigate(); - const dispatch = useDispatch(); - useEffect(() => { - const fetchMonitor = async () => { - try { - setIsLoading(true); - const action = await dispatch(getUptimeMonitorById({ monitorId })); - - if (getUptimeMonitorById.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - } else if (getUptimeMonitorById.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - navigate("/not-found", { replace: true }); - } finally { - setIsLoading(false); - } - }; - fetchMonitor(); - }, [monitorId, dispatch, navigate, updateTrigger]); - return [monitor, isLoading, error]; -}; - -export { useFetchUptimeMonitorById }; diff --git a/client/src/Hooks/useFetchUptimeMonitorDetails.js b/client/src/Hooks/useFetchUptimeMonitorDetails.js deleted file mode 100644 index 3c0ac7ee6..000000000 --- a/client/src/Hooks/useFetchUptimeMonitorDetails.js +++ /dev/null @@ -1,35 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../main"; -import { useNavigate } from "react-router-dom"; -import { createToast } from "../Utils/toastUtils"; - -export const useFetchUptimeMonitorDetails = ({ monitorId, dateRange, trigger }) => { - const [networkError, setNetworkError] = useState(false); - const [isLoading, setIsLoading] = useState(true); - const [monitor, setMonitor] = useState(undefined); - const [monitorStats, setMonitorStats] = useState(undefined); - const navigate = useNavigate(); - useEffect(() => { - const fetchMonitors = async () => { - try { - const res = await networkService.getUptimeDetailsById({ - monitorId: monitorId, - dateRange: dateRange, - normalize: true, - }); - const { monitorData, monitorStats } = res?.data?.data ?? {}; - setMonitor(monitorData); - setMonitorStats(monitorStats); - } catch (error) { - setNetworkError(true); - createToast({ body: error.message }); - } finally { - setIsLoading(false); - } - }; - fetchMonitors(); - }, [dateRange, monitorId, navigate, trigger]); - return [monitor, monitorStats, isLoading, networkError]; -}; - -export default useFetchUptimeMonitorDetails; diff --git a/client/src/Hooks/useMonitorControls.js b/client/src/Hooks/useMonitorControls.js deleted file mode 100644 index b47603b58..000000000 --- a/client/src/Hooks/useMonitorControls.js +++ /dev/null @@ -1,28 +0,0 @@ -import { useState } from "react"; -import { networkService } from "../main"; -import { createToast } from "../Utils/toastUtils"; - -const usePauseMonitor = ({ monitorId, triggerUpdate }) => { - const [isLoading, setIsLoading] = useState(false); - const [error, setError] = useState(undefined); - const pauseMonitor = async () => { - try { - setIsLoading(true); - const res = await networkService.pauseMonitorById({ monitorId }); - createToast({ - body: res.data.data.isActive - ? "Monitor resumed successfully" - : "Monitor paused successfully", - }); - triggerUpdate(); - } catch (error) { - setError(error); - } finally { - setIsLoading(false); - } - }; - - return [pauseMonitor, isLoading, error]; -}; - -export { usePauseMonitor }; diff --git a/client/src/Hooks/useMonitorUtils.js b/client/src/Hooks/useMonitorUtils.js index 424112a81..417c6eadd 100644 --- a/client/src/Hooks/useMonitorUtils.js +++ b/client/src/Hooks/useMonitorUtils.js @@ -46,7 +46,27 @@ const useMonitorUtils = () => { pending: theme.palette.warning.lowContrast, }; - return { getMonitorWithPercentage, determineState, statusColor }; + const statusToTheme = { + up: "success", + down: "error", + paused: "warning", + pending: "secondary", + "cannot resolve": "tertiary", + }; + + const pagespeedStatusMsg = { + up: "Live (collecting data)", + down: "Inactive", + paused: "Paused", + }; + + return { + getMonitorWithPercentage, + determineState, + statusColor, + statusToTheme, + pagespeedStatusMsg, + }; }; export { useMonitorUtils }; diff --git a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx b/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx deleted file mode 100644 index c0b61ef62..000000000 --- a/client/src/Pages/Infrastructure/Details/Hooks/useHardwareMonitorsFetch.jsx +++ /dev/null @@ -1,38 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; - -const useHardwareMonitorsFetch = ({ monitorId, dateRange }) => { - // Abort early if creating monitor - - const [isLoading, setIsLoading] = useState(true); - const [networkError, setNetworkError] = useState(false); - const [monitor, setMonitor] = useState(undefined); - - useEffect(() => { - const fetchData = async () => { - try { - if (!monitorId) { - return { monitor: undefined, isLoading: false, networkError: undefined }; - } - const response = await networkService.getHardwareDetailsByMonitorId({ - monitorId: monitorId, - dateRange: dateRange, - }); - setMonitor(response.data.data); - } catch (error) { - setNetworkError(true); - } finally { - setIsLoading(false); - } - }; - fetchData(); - }, [monitorId, dateRange]); - - return { - isLoading, - networkError, - monitor, - }; -}; - -export { useHardwareMonitorsFetch }; diff --git a/client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx b/client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx deleted file mode 100644 index 883205c77..000000000 --- a/client/src/Pages/Infrastructure/Monitors/Hooks/useMonitorFetch.jsx +++ /dev/null @@ -1,46 +0,0 @@ -import { useState, useEffect } from "react"; -import { useSelector } from "react-redux"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; - -const useMonitorFetch = ({ page, field, filter, rowsPerPage, updateTrigger }) => { - // Redux state - const { user } = useSelector((state) => state.auth); - - // Local state - const [isLoading, setIsLoading] = useState(true); - const [networkError, setNetworkError] = useState(false); - const [monitors, setMonitors] = useState(undefined); - const [summary, setSummary] = useState(undefined); - - useEffect(() => { - const fetchMonitors = async () => { - try { - const response = await networkService.getMonitorsByTeamId({ - teamId: user.teamId, - limit: 1, - field: field, - filter: filter, - types: ["hardware"], - page: page, - rowsPerPage: rowsPerPage, - }); - setMonitors(response?.data?.data?.filteredMonitors ?? []); - setSummary(response?.data?.data?.summary ?? {}); - } catch (error) { - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - - fetchMonitors(); - }, [page, field, filter, rowsPerPage, user.teamId, updateTrigger]); - - return { monitors, summary, isLoading, networkError }; -}; - -export { useMonitorFetch }; diff --git a/client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx b/client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx deleted file mode 100644 index 18ffd8782..000000000 --- a/client/src/Pages/PageSpeed/Details/Hooks/useMonitorFetch.jsx +++ /dev/null @@ -1,39 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; -import { useNavigate } from "react-router-dom"; -const useMonitorFetch = ({ monitorId }) => { - const navigate = useNavigate(); - - const [monitor, setMonitor] = useState(undefined); - const [audits, setAudits] = useState(undefined); - const [isLoading, setIsLoading] = useState(true); - const [networkError, setNetworkError] = useState(false); - useEffect(() => { - const fetchMonitor = async () => { - try { - const res = await networkService.getStatsByMonitorId({ - monitorId: monitorId, - sortOrder: "desc", - limit: 50, - dateRange: "day", - numToDisplay: null, - normalize: null, - }); - setMonitor(res?.data?.data ?? undefined); - setAudits(res?.data?.data?.checks?.[0]?.audits ?? undefined); - } catch (error) { - setNetworkError(true); - createToast({ body: error.message }); - } finally { - setIsLoading(false); - } - }; - - fetchMonitor(); - }, [monitorId, navigate]); - - return { monitor, audits, isLoading }; -}; - -export { useMonitorFetch }; diff --git a/client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx b/client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx deleted file mode 100644 index d2f0f4842..000000000 --- a/client/src/Pages/PageSpeed/Monitors/Hooks/useMonitorsFetch.jsx +++ /dev/null @@ -1,44 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; - -const useMonitorsFetch = ({ teamId }) => { - const [isLoading, setIsLoading] = useState(true); - const [monitors, setMonitors] = useState([]); - const [summary, setSummary] = useState({}); - const [networkError, setNetworkError] = useState(false); - - useEffect(() => { - const fetchMonitors = async () => { - try { - setIsLoading(true); - const res = await networkService.getMonitorsByTeamId({ - teamId: teamId, - limit: 10, - types: ["pagespeed"], - page: null, - rowsPerPage: null, - filter: null, - field: null, - order: null, - }); - if (res?.data?.data?.filteredMonitors) { - setMonitors(res.data.data.filteredMonitors); - setSummary(res.data.data.summary); - } - } catch (error) { - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setIsLoading(false); - } - }; - - fetchMonitors(); - }, [teamId]); - return { isLoading, monitors, summary, networkError }; -}; - -export default useMonitorsFetch; diff --git a/client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx b/client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx deleted file mode 100644 index 9491e7cc3..000000000 --- a/client/src/Pages/Uptime/Details/Hooks/useMonitorFetch.jsx +++ /dev/null @@ -1,33 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; -import { useNavigate } from "react-router-dom"; -import { createToast } from "../../../../Utils/toastUtils"; - -export const useMonitorFetch = ({ monitorId, dateRange }) => { - const [networkError, setNetworkError] = useState(false); - const [isLoading, setIsLoading] = useState(true); - const [monitor, setMonitor] = useState(undefined); - const navigate = useNavigate(); - - useEffect(() => { - const fetchMonitors = async () => { - try { - const res = await networkService.getUptimeDetailsById({ - monitorId: monitorId, - dateRange: dateRange, - normalize: true, - }); - setMonitor(res?.data?.data ?? {}); - } catch (error) { - setNetworkError(true); - createToast({ body: error.message }); - } finally { - setIsLoading(false); - } - }; - fetchMonitors(); - }, [dateRange, monitorId, navigate]); - return [monitor, isLoading, networkError]; -}; - -export default useMonitorFetch; diff --git a/client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx b/client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx deleted file mode 100644 index 1f1d15212..000000000 --- a/client/src/Pages/Uptime/Monitors/Hooks/useMonitorsFetch.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import { useEffect, useState } from "react"; -import { networkService } from "../../../../main"; -import { createToast } from "../../../../Utils/toastUtils"; -import { useTheme } from "@emotion/react"; -import { useMonitorUtils } from "../../../../Hooks/useMonitorUtils"; - -export const useMonitorFetch = ({ - teamId, - limit, - page, - rowsPerPage, - filter, - field, - order, - triggerUpdate, -}) => { - const [monitorsAreLoading, setMonitorsAreLoading] = useState(false); - const [monitors, setMonitors] = useState(undefined); - const [filteredMonitors, setFilteredMonitors] = useState(undefined); - const [monitorsSummary, setMonitorsSummary] = useState(undefined); - const [networkError, setNetworkError] = useState(false); - - const theme = useTheme(); - const { getMonitorWithPercentage } = useMonitorUtils(); - useEffect(() => { - const fetchMonitors = async () => { - try { - setMonitorsAreLoading(true); - const res = await networkService.getMonitorsByTeamId({ - teamId, - limit, - types: ["http", "ping", "docker", "port"], - page, - rowsPerPage, - filter, - field, - order, - }); - const { monitors, filteredMonitors, summary } = res.data.data; - const mappedMonitors = filteredMonitors.map((monitor) => - getMonitorWithPercentage(monitor, theme) - ); - setMonitors(monitors); - setFilteredMonitors(mappedMonitors); - setMonitorsSummary(summary); - } catch (error) { - setNetworkError(true); - createToast({ - body: error.message, - }); - } finally { - setMonitorsAreLoading(false); - } - }; - fetchMonitors(); - }, [ - teamId, - limit, - field, - filter, - order, - page, - rowsPerPage, - theme, - triggerUpdate, - getMonitorWithPercentage, - ]); - return { - monitors, - filteredMonitors, - monitorsSummary, - monitorsAreLoading, - networkError, - }; -}; - -export default useMonitorFetch; diff --git a/client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx b/client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx deleted file mode 100644 index ff3288cdf..000000000 --- a/client/src/Pages/Uptime/Monitors/Hooks/useUtils.jsx +++ /dev/null @@ -1,110 +0,0 @@ -import { useTheme } from "@mui/material"; - -const useUtils = () => { - const determineState = (monitor) => { - if (typeof monitor === "undefined") return "pending"; - if (monitor.isActive === false) return "paused"; - if (monitor?.status === undefined) return "pending"; - return monitor?.status == true ? "up" : "down"; - }; - - /* TODO Refactor: from here on shouldn't live in a custom hook, but on theme, or constants */ - const theme = useTheme(); - - const statusColor = { - up: theme.palette.success.lowContrast, - down: theme.palette.error.lowContrast, - paused: theme.palette.warning.lowContrast, - pending: theme.palette.warning.lowContrast, - }; - - const statusMsg = { - up: "Your site is up.", - down: "Your site is down.", - paused: "Pending...", - }; - - const pagespeedStatusMsg = { - up: "Live (collecting data)", - down: "Inactive", - paused: "Paused", - }; - - /* - TODO - This is used on - 1) Details > Gradient card */ - /* These are rediections. We should do something that maps up to success, down to error, and get the theme by that - See Client\src\Components\Label\index.jsx - */ - - const statusToTheme = { - up: "success", - down: "error", - paused: "warning", - pending: "secondary", - "cannot resolve": "tertiary", - }; - - const getStatusStyles = (status) => { - const themeColor = statusToTheme[status]; - - return { - backgroundColor: theme.palette[themeColor].lowContrast, - background: `linear-gradient(340deg, ${theme.palette[themeColor].main} -60%, ${theme.palette[themeColor].lowContrast} 35%)`, - borderColor: theme.palette[themeColor].lowContrast, - "& h2": { - color: theme.palette[themeColor].contrastText, - textTransform: "uppercase", - }, - "& p": { - color: theme.palette[themeColor].contrastText, - }, - }; - }; - - const statusStyles = { - up: { - backgroundColor: theme.palette.success.lowContrast, - background: `linear-gradient(340deg, ${theme.palette.tertiary.main} -60%, ${theme.palette.success.lowContrast} 35%)`, // CAIO_REVIEW - borderColor: theme.palette.success.contrastText, - // "& h2": { color: theme.palette.success.contrastText }, // CAIO_REVIEW - }, - down: { - backgroundColor: theme.palette.error.lowContrast, - background: `linear-gradient(340deg, ${theme.palette.tertiary.main} -60%, ${theme.palette.error.lowContrast} 35%)`, // CAIO_REVIEW - borderColor: theme.palette.error.contrastText, - "& h2": { color: theme.palette.error.contrastText }, // CAIO_REVIEW - "& .MuiTypography-root": { color: theme.palette.error.contrastText }, // CAIO_REVIEW - }, - paused: { - backgroundColor: theme.palette.warning.lowContrast, - background: `linear-gradient(340deg, ${theme.palette.tertiary.main} -60%, ${theme.palette.warning.lowContrast} 35%)`, // CAIO_REVIEW - borderColor: theme.palette.warning.contrastText, - "& h2": { color: theme.palette.warning.contrastText }, // CAIO_REVIEW - "& .MuiTypography-root": { color: theme.palette.warning.contrastText }, // CAIO_REVIEW - }, - pending: { - backgroundColor: theme.palette.warning.lowContrast, - background: `linear-gradient(340deg, ${theme.palette.tertiary.main} -60%, ${theme.palette.warning.lowContrast} 35%)`, // CAIO_REVIEW - borderColor: theme.palette.warning.contrastText, - "& h2": { color: theme.palette.warning.contrastText }, // CAIO_REVIEW - }, - }; - - /* These are rediections. We should do something that maps up to success, down to error, and get the theme by that - - */ - - return { - determineState, - statusColor, - statusMsg, - pagespeedStatusMsg, - statusStyles, - statusToTheme, - getStatusStyles, - }; -}; - -export default useUtils; From 50986d44938243351e98214371cad3b5cff18cbc Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 12 Jun 2025 12:44:46 +0800 Subject: [PATCH 03/36] use hooks --- .../src/Components/Inputs/Checkbox/index.jsx | 2 +- .../MonitorDetailsControlHeader/index.jsx | 13 +- .../MonitorDetailsControlHeader/status.jsx | 5 +- .../Components/MonitorStatusHeader/index.jsx | 4 +- client/src/Components/StatBox/index.jsx | 4 +- .../src/Pages/Infrastructure/Create/index.jsx | 37 ++---- .../Details/Components/StatusBoxes/index.jsx | 4 +- .../Pages/Infrastructure/Details/index.jsx | 4 +- .../Components/MonitorsTable/index.jsx | 4 +- .../Pages/Infrastructure/Monitors/index.jsx | 12 +- .../src/Pages/PageSpeed/Configure/index.jsx | 91 ++++---------- client/src/Pages/PageSpeed/Create/index.jsx | 61 +++------ client/src/Pages/PageSpeed/Details/index.jsx | 10 +- .../Monitors/Components/Card/index.jsx | 8 +- client/src/Pages/PageSpeed/Monitors/index.jsx | 17 ++- .../Status/Components/MonitorsList/index.jsx | 4 +- client/src/Pages/Uptime/Configure/index.jsx | 116 +++++++++--------- client/src/Pages/Uptime/Create/index.jsx | 47 ++++--- .../Components/UptimeStatusBoxes/index.jsx | 4 +- client/src/Pages/Uptime/Details/index.jsx | 13 +- .../Components/UptimeDataTable/index.jsx | 4 +- client/src/Pages/Uptime/Monitors/index.jsx | 8 +- client/src/locales/en.json | 1 + 23 files changed, 204 insertions(+), 269 deletions(-) diff --git a/client/src/Components/Inputs/Checkbox/index.jsx b/client/src/Components/Inputs/Checkbox/index.jsx index 58b83c7c7..1497c44fb 100644 --- a/client/src/Components/Inputs/Checkbox/index.jsx +++ b/client/src/Components/Inputs/Checkbox/index.jsx @@ -105,7 +105,7 @@ const Checkbox = ({ }; Checkbox.propTypes = { - id: PropTypes.string.isRequired, + id: PropTypes.string, name: PropTypes.string, label: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, size: PropTypes.oneOf(["small", "medium", "large"]), diff --git a/client/src/Components/MonitorDetailsControlHeader/index.jsx b/client/src/Components/MonitorDetailsControlHeader/index.jsx index aa9ef91b9..8c9f845e9 100644 --- a/client/src/Components/MonitorDetailsControlHeader/index.jsx +++ b/client/src/Components/MonitorDetailsControlHeader/index.jsx @@ -11,10 +11,9 @@ import EmailIcon from "@mui/icons-material/Email"; import PropTypes from "prop-types"; import { useNavigate } from "react-router-dom"; import { useTheme } from "@mui/material/styles"; -import { usePauseMonitor } from "../../Hooks/useMonitorControls"; +import { usePauseMonitor } from "../../Hooks/monitorHooks"; import { useSendTestEmail } from "../../Hooks/useSendTestEmail"; import { useTranslation } from "react-i18next"; - /** * MonitorDetailsControlHeader component displays the control header for monitor details. * It includes status display, pause/resume button, and a configure button for admins. @@ -38,10 +37,7 @@ const MonitorDetailsControlHeader = ({ const navigate = useNavigate(); const theme = useTheme(); const { t } = useTranslation(); - const [pauseMonitor, isPausing, error] = usePauseMonitor({ - monitorId: monitor?._id, - triggerUpdate, - }); + const [pauseMonitor, isPausing, error] = usePauseMonitor(); const [isSending, emailError, sendTestEmail] = useSendTestEmail(); @@ -88,7 +84,10 @@ const MonitorDetailsControlHeader = ({ monitor?.isActive ? : } onClick={() => { - pauseMonitor(); + pauseMonitor({ + monitorId: monitor?._id, + triggerUpdate, + }); }} > {monitor?.isActive ? "Pause" : "Resume"} diff --git a/client/src/Components/MonitorDetailsControlHeader/status.jsx b/client/src/Components/MonitorDetailsControlHeader/status.jsx index 3d6e62c60..fdfb955f4 100644 --- a/client/src/Components/MonitorDetailsControlHeader/status.jsx +++ b/client/src/Components/MonitorDetailsControlHeader/status.jsx @@ -7,8 +7,7 @@ import Dot from "../../Components/Dot"; import { formatDurationRounded } from "../../Utils/timeUtils"; import PropTypes from "prop-types"; import { useTheme } from "@emotion/react"; -import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; - +import { useMonitorUtils } from "../../Hooks/useMonitorUtils"; /** * Status component displays the status information of a monitor. * It includes the monitor's name, URL, and check interval. @@ -23,7 +22,7 @@ import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; */ const Status = ({ monitor }) => { const theme = useTheme(); - const { statusColor, determineState } = useUtils(); + const { statusColor, determineState } = useMonitorUtils(); return ( diff --git a/client/src/Components/MonitorStatusHeader/index.jsx b/client/src/Components/MonitorStatusHeader/index.jsx index 7ea95410b..a81131152 100644 --- a/client/src/Components/MonitorStatusHeader/index.jsx +++ b/client/src/Components/MonitorStatusHeader/index.jsx @@ -2,7 +2,7 @@ import { Stack, Typography } from "@mui/material"; import PulseDot from "../Animated/PulseDot"; import Dot from "../Dot"; import { useTheme } from "@emotion/react"; -import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../Hooks/useMonitorUtils"; import { formatDurationRounded } from "../../Utils/timeUtils"; import ConfigButton from "./ConfigButton"; import SkeletonLayout from "./skeleton"; @@ -12,7 +12,7 @@ import { useTranslation } from "react-i18next"; const MonitorStatusHeader = ({ path, isLoading = false, isAdmin, monitor }) => { const theme = useTheme(); const { t } = useTranslation(); - const { statusColor, determineState } = useUtils(); + const { statusColor, determineState } = useMonitorUtils(); if (isLoading) { return ; } diff --git a/client/src/Components/StatBox/index.jsx b/client/src/Components/StatBox/index.jsx index 673736ea9..93fa46c2b 100644 --- a/client/src/Components/StatBox/index.jsx +++ b/client/src/Components/StatBox/index.jsx @@ -2,7 +2,7 @@ import { Stack, Typography } from "@mui/material"; import Image from "../Image"; import { useTheme } from "@mui/material/styles"; import PropTypes from "prop-types"; -import useUtils from "../../Pages/Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../Hooks/useMonitorUtils"; /** * StatBox Component @@ -41,7 +41,7 @@ const StatBox = ({ sx, }) => { const theme = useTheme(); - const { statusToTheme } = useUtils(); + const { statusToTheme } = useMonitorUtils(); const themeColor = statusToTheme[status]; const statusBoxStyles = gradient diff --git a/client/src/Pages/Infrastructure/Create/index.jsx b/client/src/Pages/Infrastructure/Create/index.jsx index 9425b7429..fa63dd4dc 100644 --- a/client/src/Pages/Infrastructure/Create/index.jsx +++ b/client/src/Pages/Infrastructure/Create/index.jsx @@ -1,20 +1,17 @@ // React, Redux, Router import { useTheme } from "@emotion/react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useParams } from "react-router-dom"; import { useState, useEffect } from "react"; -import { useSelector, useDispatch } from "react-redux"; +import { useSelector } from "react-redux"; // Utility and Network import { infrastructureMonitorValidation } from "../../../Validation/validation"; -import { - createInfrastructureMonitor, - updateInfrastructureMonitor, -} from "../../../Features/InfrastructureMonitors/infrastructureMonitorsSlice"; -import { useHardwareMonitorsFetch } from "../Details/Hooks/useHardwareMonitorsFetch"; +import { useFetchHardwareMonitorById } from "../../../Hooks/monitorHooks"; import { capitalizeFirstLetter } from "../../../Utils/stringUtils"; import { useTranslation } from "react-i18next"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; import NotificationsConfig from "../../../Components/NotificationConfig"; +import { useUpdateMonitor, useCreateMonitor } from "../../../Hooks/monitorHooks"; // MUI import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material"; @@ -55,9 +52,6 @@ const getAlertError = (errors) => { const CreateInfrastructureMonitor = () => { const theme = useTheme(); const { user } = useSelector((state) => state.auth); - const monitorState = useSelector((state) => state.infrastructureMonitor); - const dispatch = useDispatch(); - const navigate = useNavigate(); const { monitorId } = useParams(); const { t } = useTranslation(); @@ -65,9 +59,11 @@ const CreateInfrastructureMonitor = () => { const isCreate = typeof monitorId === "undefined"; // Fetch monitor details if editing - const { monitor, isLoading, networkError } = useHardwareMonitorsFetch({ monitorId }); + const [monitor, isLoading, networkError] = useFetchHardwareMonitorById({ monitorId }); const [notifications, notificationsAreLoading, notificationsError] = useGetNotificationsByTeamId(); + const [updateMonitor, isUpdating] = useUpdateMonitor(); + const [createMonitor, isCreating] = useCreateMonitor(); // State const [errors, setErrors] = useState({}); @@ -187,6 +183,7 @@ const CreateInfrastructureMonitor = () => { }; form = { + ...(isCreate ? {} : { _id: monitorId }), ...rest, description: form.name, teamId: user.teamId, @@ -197,19 +194,9 @@ const CreateInfrastructureMonitor = () => { }; // Handle create or update - const action = isCreate - ? await dispatch(createInfrastructureMonitor({ monitor: form })) - : await dispatch(updateInfrastructureMonitor({ monitorId, monitor: form })); - if (action.meta.requestStatus === "fulfilled") { - createToast({ - body: isCreate - ? t("infrastructureMonitorCreated") - : t("infrastructureMonitorUpdated"), - }); - navigate("/infrastructure"); - } else { - createToast({ body: "Failed to save monitor." }); - } + isCreate + ? await createMonitor({ monitor: form, redirect: "/infrastructure" }) + : await updateMonitor({ monitor: form, redirect: "/infrastructure" }); }; const onChange = (event) => { @@ -448,7 +435,7 @@ const CreateInfrastructureMonitor = () => { type="submit" variant="contained" color="accent" - loading={monitorState?.isLoading} + loading={isLoading || isUpdating || isCreating || notificationsAreLoading} > {t(isCreate ? "infrastructureCreateMonitor" : "infrastructureEditMonitor")} diff --git a/client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx b/client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx index 66483d448..6c11e7c82 100644 --- a/client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx +++ b/client/src/Pages/Infrastructure/Details/Components/StatusBoxes/index.jsx @@ -4,14 +4,14 @@ import StatusBoxes from "../../../../../Components/StatusBoxes"; import StatBox from "../../../../../Components/StatBox"; //Utils -import useUtils from "../../../../../Pages/Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; import { useHardwareUtils } from "../../Hooks/useHardwareUtils"; import { useTranslation } from "react-i18next"; const InfraStatBoxes = ({ shouldRender, monitor }) => { // Utils const { formatBytes } = useHardwareUtils(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); const { t } = useTranslation(); const { stats } = monitor ?? {}; diff --git a/client/src/Pages/Infrastructure/Details/index.jsx b/client/src/Pages/Infrastructure/Details/index.jsx index fefb7d2aa..842e0f6d9 100644 --- a/client/src/Pages/Infrastructure/Details/index.jsx +++ b/client/src/Pages/Infrastructure/Details/index.jsx @@ -11,7 +11,7 @@ import GenericFallback from "../../../Components/GenericFallback"; // Utils import { useTheme } from "@emotion/react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import { useHardwareMonitorsFetch } from "./Hooks/useHardwareMonitorsFetch"; +import { useFetchHardwareMonitorById } from "../../../Hooks/monitorHooks"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { useParams } from "react-router-dom"; @@ -33,7 +33,7 @@ const InfrastructureDetails = () => { const { t } = useTranslation(); const isAdmin = useIsAdmin(); - const { isLoading, networkError, monitor } = useHardwareMonitorsFetch({ + const [monitor, isLoading, networkError] = useFetchHardwareMonitorById({ monitorId, dateRange, }); diff --git a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx index 0ec435c60..761ecaa0e 100644 --- a/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx +++ b/client/src/Pages/Infrastructure/Monitors/Components/MonitorsTable/index.jsx @@ -10,7 +10,7 @@ import CustomGauge from "../../../../../Components/Charts/CustomGauge"; // Utils import { useTheme } from "@emotion/react"; -import useUtils from "../../../../Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; import { useNavigate } from "react-router-dom"; import PropTypes from "prop-types"; import { useTranslation } from "react-i18next"; @@ -19,7 +19,7 @@ const MonitorsTable = ({ shouldRender, monitors, isAdmin, handleActionMenuDelete // Utils const theme = useTheme(); const { t } = useTranslation(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); const navigate = useNavigate(); // Handlers diff --git a/client/src/Pages/Infrastructure/Monitors/index.jsx b/client/src/Pages/Infrastructure/Monitors/index.jsx index bfba843c0..6d49f64a7 100644 --- a/client/src/Pages/Infrastructure/Monitors/index.jsx +++ b/client/src/Pages/Infrastructure/Monitors/index.jsx @@ -10,15 +10,20 @@ import Fallback from "../../../Components/Fallback"; import Filter from "./Components/Filters"; // Utils import { useTheme } from "@emotion/react"; -import { useMonitorFetch } from "./Hooks/useMonitorFetch"; import { useState } from "react"; +import { useSelector } from "react-redux"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; import { useTranslation } from "react-i18next"; +import { useFetchMonitorsByTeamId } from "../../../Hooks/monitorHooks"; // Constants +const TYPES = ["hardware"]; const BREADCRUMBS = [{ name: `infrastructure`, path: "/infrastructure" }]; const InfrastructureMonitors = () => { // Redux state + const { user } = useSelector((state) => state.auth); + + // Local state const [page, setPage] = useState(0); const [rowsPerPage, setRowsPerPage] = useState(5); const [updateTrigger, setUpdateTrigger] = useState(false); @@ -50,7 +55,10 @@ const InfrastructureMonitors = () => { const field = toFilterStatus !== undefined ? "status" : undefined; - const { monitors, summary, isLoading, networkError } = useMonitorFetch({ + const [monitors, summary, isLoading, networkError] = useFetchMonitorsByTeamId({ + teamId: user.teamId, + limit: 1, + types: TYPES, page, field: field, filter: toFilterStatus, diff --git a/client/src/Pages/PageSpeed/Configure/index.jsx b/client/src/Pages/PageSpeed/Configure/index.jsx index 487106247..71e1e111e 100644 --- a/client/src/Pages/PageSpeed/Configure/index.jsx +++ b/client/src/Pages/PageSpeed/Configure/index.jsx @@ -12,45 +12,43 @@ import NotificationsConfig from "../../../Components/NotificationConfig"; import Dialog from "../../../Components/Dialog"; // Utils -import { useEffect, useState } from "react"; +import { useState } from "react"; import { useTheme } from "@emotion/react"; -import { useDispatch, useSelector } from "react-redux"; -import { useNavigate, useParams } from "react-router"; -import { - deletePageSpeed, - getPagespeedMonitorById, - getPageSpeedByTeamId, - updatePageSpeed, - pausePageSpeed, -} from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice"; +import { useParams } from "react-router"; import { monitorValidation } from "../../../Validation/validation"; -import { createToast } from "../../../Utils/toastUtils"; import { useTranslation } from "react-i18next"; -import useUtils from "../../Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; - +import { + useFetchMonitorById, + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, +} from "../../../Hooks/monitorHooks"; const PageSpeedConfigure = () => { // Redux state - const { isLoading } = useSelector((state) => state.pageSpeedMonitors); // Local state const [monitor, setMonitor] = useState({}); const [errors, setErrors] = useState({}); - const [buttonLoading, setButtonLoading] = useState(false); const [isOpen, setIsOpen] = useState(false); + const [updateTrigger, setUpdateTrigger] = useState(false); // Utils const theme = useTheme(); const { t } = useTranslation(); - const navigate = useNavigate(); - const dispatch = useDispatch(); const MS_PER_MINUTE = 60000; const { monitorId } = useParams(); - const { statusColor, pagespeedStatusMsg, determineState } = useUtils(); + const { statusColor, pagespeedStatusMsg, determineState } = useMonitorUtils(); const [notifications, notificationsAreLoading, notificationsError] = useGetNotificationsByTeamId(); + const [isLoading] = useFetchMonitorById({ monitorId, setMonitor, updateTrigger }); + const [deleteMonitor, isDeleting] = useDeleteMonitor(); + const [updateMonitor, isUpdating] = useUpdateMonitor(); + const [pauseMonitor, isPausing] = usePauseMonitor(); + const frequencies = [ { _id: 3, name: "3 minutes" }, { _id: 5, name: "5 minutes" }, @@ -61,24 +59,10 @@ const PageSpeedConfigure = () => { { _id: 10080, name: "1 week" }, ]; - useEffect(() => { - const fetchMonitor = async () => { - try { - const action = await dispatch(getPagespeedMonitorById({ monitorId })); - - if (getPagespeedMonitorById.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - } else if (getPagespeedMonitorById.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - logger.error("Error fetching monitor of id: " + monitorId); - navigate("/not-found", { replace: true }); - } - }; - fetchMonitor(); - }, [dispatch, monitorId, navigate]); + // Handlers + const triggerUpdate = () => { + setUpdateTrigger(!updateTrigger); + }; const onChange = (event) => { let { value, name } = event.target; @@ -106,42 +90,17 @@ const PageSpeedConfigure = () => { }; const handlePause = async () => { - try { - const action = await dispatch(pausePageSpeed({ monitorId })); - if (pausePageSpeed.fulfilled.match(action)) { - const monitor = action.payload.data; - setMonitor(monitor); - const state = action?.payload?.data.isActive === false ? "paused" : "resumed"; - createToast({ body: `Monitor ${state} successfully.` }); - } else if (pausePageSpeed.rejected.match(action)) { - throw new Error(action.error.message); - } - } catch (error) { - createToast({ body: "Failed to pause monitor" }); - } + await pauseMonitor({ monitorId, triggerUpdate }); }; const onSubmit = async (event) => { event.preventDefault(); - const action = await dispatch(updatePageSpeed({ monitor: monitor })); - if (action.meta.requestStatus === "fulfilled") { - createToast({ body: "Monitor updated successfully!" }); - dispatch(getPageSpeedByTeamId()); - } else { - createToast({ body: "Failed to update monitor." }); - } + await updateMonitor({ monitor, redirect: "/pagespeed" }); }; const handleRemove = async (event) => { event.preventDefault(); - setButtonLoading(true); - const action = await dispatch(deletePageSpeed({ monitor })); - if (action.meta.requestStatus === "fulfilled") { - navigate("/pagespeed"); - } else { - createToast({ body: "Failed to delete monitor." }); - } - setButtonLoading(false); + await deleteMonitor({ monitor, redirect: "/pagespeed" }); }; return ( @@ -363,7 +322,7 @@ const PageSpeedConfigure = () => { mt="auto" > diff --git a/client/src/Pages/PageSpeed/Details/index.jsx b/client/src/Pages/PageSpeed/Details/index.jsx index 5724f12a1..e8afcf8c8 100644 --- a/client/src/Pages/PageSpeed/Details/index.jsx +++ b/client/src/Pages/PageSpeed/Details/index.jsx @@ -11,8 +11,7 @@ import GenericFallback from "../../../Components/GenericFallback"; import { useTheme } from "@emotion/react"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; import { useParams } from "react-router-dom"; -import { useSelector } from "react-redux"; -import { useMonitorFetch } from "./Hooks/useMonitorFetch"; +import { useFetchStatsByMonitorId } from "../../../Hooks/monitorHooks"; import { useState } from "react"; import { useTranslation } from "react-i18next"; // Constants @@ -28,8 +27,13 @@ const PageSpeedDetails = () => { const isAdmin = useIsAdmin(); const { monitorId } = useParams(); - const { monitor, audits, isLoading, networkError } = useMonitorFetch({ + const [monitor, audits, isLoading, networkError] = useFetchStatsByMonitorId({ monitorId, + sortOrder: "desc", + limit: 50, + dateRange: "day", + numToDisplay: null, + normalize: null, }); const [metrics, setMetrics] = useState({ diff --git a/client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx b/client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx index 8c83f102a..2220a4b16 100644 --- a/client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx +++ b/client/src/Pages/PageSpeed/Monitors/Components/Card/index.jsx @@ -8,7 +8,7 @@ import { useTheme } from "@emotion/react"; import { Area, AreaChart, CartesianGrid, ResponsiveContainer, Tooltip } from "recharts"; import { useSelector } from "react-redux"; import { formatDateWithTz, formatDurationSplit } from "../../../../../Utils/timeUtils"; -import useUtils from "../../../../Uptime/Monitors/Hooks/useUtils"; +import { useMonitorUtils } from "../../../../../Hooks/useMonitorUtils"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import IconBox from "../../../../../Components/IconBox"; @@ -118,7 +118,7 @@ const processData = (data) => { const PagespeedAreaChart = ({ data, status }) => { const theme = useTheme(); const [isHovered, setIsHovered] = useState(false); - const { statusToTheme } = useUtils(); + const { statusToTheme } = useMonitorUtils(); const themeColor = statusToTheme[status]; @@ -206,7 +206,7 @@ PagespeedAreaChart.propTypes = { * @returns {JSX.Element} - The rendered card. */ const Card = ({ monitor }) => { - const { determineState, pagespeedStatusMsg } = useUtils(); + const { determineState, pagespeedStatusMsg } = useMonitorUtils(); const theme = useTheme(); const { t } = useTranslation(); const navigate = useNavigate(); @@ -275,7 +275,7 @@ const Card = ({ monitor }) => { sx={{ gridColumnStart: 1, gridColumnEnd: 4 }} > diff --git a/client/src/Pages/PageSpeed/Monitors/index.jsx b/client/src/Pages/PageSpeed/Monitors/index.jsx index 808541cee..319b0da21 100644 --- a/client/src/Pages/PageSpeed/Monitors/index.jsx +++ b/client/src/Pages/PageSpeed/Monitors/index.jsx @@ -5,17 +5,17 @@ import CreateMonitorHeader from "../../../Components/MonitorCreateHeader"; import MonitorCountHeader from "../../../Components/MonitorCountHeader"; import MonitorGrid from "./Components/MonitorGrid"; import Fallback from "../../../Components/Fallback"; +import GenericFallback from "../../../Components/GenericFallback"; // Utils import { useTheme } from "@emotion/react"; import { useSelector } from "react-redux"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import useMonitorsFetch from "./Hooks/useMonitorsFetch"; -import GenericFallback from "../../../Components/GenericFallback"; import { useTranslation } from "react-i18next"; +import { useFetchMonitorsByTeamId } from "../../../Hooks/monitorHooks"; // Constants const BREADCRUMBS = [{ name: `pagespeed`, path: "/pagespeed" }]; - +const TYPES = ["pagespeed"]; const PageSpeed = () => { const theme = useTheme(); const { t } = useTranslation(); @@ -23,8 +23,15 @@ const PageSpeed = () => { const { user } = useSelector((state) => state.auth); const { pagespeedApiKey } = useSelector((state) => state.settings); - const { isLoading, monitors, summary, networkError } = useMonitorsFetch({ + const [monitors, monitorsSummary, isLoading, networkError] = useFetchMonitorsByTeamId({ teamId: user.teamId, + limit: 10, + types: TYPES, + page: null, + rowsPerPage: null, + filter: null, + field: null, + order: null, }); if (networkError === true) { @@ -68,7 +75,7 @@ const PageSpeed = () => { /> { const theme = useTheme(); - const { determineState } = useUtils(); + const { determineState } = useMonitorUtils(); const { showURL } = useSelector((state) => state.ui); diff --git a/client/src/Pages/Uptime/Configure/index.jsx b/client/src/Pages/Uptime/Configure/index.jsx index 8718a2209..5a380f8e5 100644 --- a/client/src/Pages/Uptime/Configure/index.jsx +++ b/client/src/Pages/Uptime/Configure/index.jsx @@ -1,37 +1,37 @@ -import { useNavigate, useParams } from "react-router"; -import { useTheme } from "@emotion/react"; -import { useDispatch, useSelector } from "react-redux"; -import { useState, useEffect } from "react"; -import { - Box, - Stack, - Tooltip, - Typography, - Button, - FormControlLabel, - Switch, -} from "@mui/material"; -import { monitorValidation } from "../../../Validation/validation"; -import { createToast } from "../../../Utils/toastUtils"; -import { useTranslation } from "react-i18next"; +// Components +import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; +import Tooltip from "@mui/material/Tooltip"; +import Typography from "@mui/material/Typography"; +import Button from "@mui/material/Button"; +import FormControlLabel from "@mui/material/FormControlLabel"; +import Switch from "@mui/material/Switch"; import ConfigBox from "../../../Components/ConfigBox"; -import { - updateUptimeMonitor, - deleteUptimeMonitor, -} from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import Breadcrumbs from "../../../Components/Breadcrumbs"; import TextInput from "../../../Components/Inputs/TextInput"; import { HttpAdornment } from "../../../Components/Inputs/TextInput/Adornments"; import Select from "../../../Components/Inputs/Select"; -import Breadcrumbs from "../../../Components/Breadcrumbs"; -import PulseDot from "../../../Components/Animated/PulseDot"; -import "./index.css"; import Dialog from "../../../Components/Dialog"; -import { usePauseMonitor } from "../../../Hooks/useMonitorControls"; +import PulseDot from "../../../Components/Animated/PulseDot"; +import Checkbox from "../../../Components/Inputs/Checkbox"; + +// Utils +import { useParams } from "react-router"; +import { useTheme } from "@emotion/react"; +import { useState } from "react"; +import { monitorValidation } from "../../../Validation/validation"; +import { createToast } from "../../../Utils/toastUtils"; +import { useTranslation } from "react-i18next"; import PauseOutlinedIcon from "@mui/icons-material/PauseOutlined"; import PlayArrowOutlinedIcon from "@mui/icons-material/PlayArrowOutlined"; import { useMonitorUtils } from "../../../Hooks/useMonitorUtils"; -import { useFetchUptimeMonitorById } from "../../../Hooks/useFetchUptimeMonitorById"; import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; +import { + useDeleteMonitor, + useUpdateMonitor, + usePauseMonitor, + useFetchMonitorById, +} from "../../../Hooks/monitorHooks"; import NotificationsConfig from "../../../Components/NotificationConfig"; /** @@ -76,18 +76,19 @@ const Configure = () => { }; // Network - const [monitor, isLoading, error] = useFetchUptimeMonitorById(monitorId, updateTrigger); const [notifications, notificationsAreLoading, notificationsError] = useGetNotificationsByTeamId(); - const [pauseMonitor, isPausing, pauseError] = usePauseMonitor({ - monitorId: monitor?._id, - triggerUpdate, + const [pauseMonitor, isPausing, pauseError] = usePauseMonitor({}); + const [deleteMonitor, isDeleting] = useDeleteMonitor(); + const [updateMonitor, isUpdating] = useUpdateMonitor(); + const [isLoading] = useFetchMonitorById({ + monitorId, + setMonitor: setForm, + updateTrigger, }); const MS_PER_MINUTE = 60000; - const navigate = useNavigate(); const theme = useTheme(); - const dispatch = useDispatch(); const matchMethodOptions = [ { _id: "equal", name: "Equal" }, @@ -111,7 +112,7 @@ const Configure = () => { // Handlers const handlePause = async () => { - const res = await pauseMonitor(); + const res = await pauseMonitor({ monitorId: form?._id, triggerUpdate }); if (typeof res !== "undefined") { triggerUpdate(); } @@ -119,12 +120,7 @@ const Configure = () => { const handleRemove = async (event) => { event.preventDefault(); - const action = await dispatch(deleteUptimeMonitor({ monitor })); - if (action.meta.requestStatus === "fulfilled") { - navigate("/uptime"); - } else { - createToast({ body: "Failed to delete monitor." }); - } + await deleteMonitor({ monitor: form, redirect: "/uptime" }); }; const onChange = (event) => { @@ -134,6 +130,17 @@ const Configure = () => { value = checked; } + if (name === "useAdvancedMatching") { + setForm((prevForm) => { + return { + ...prevForm, + matchMethod: "equal", + }; + }); + setUseAdvancedMatching(!useAdvancedMatching); + return; + } + if (name === "interval") { value = value * MS_PER_MINUTE; } @@ -146,7 +153,6 @@ const Configure = () => { setErrors((prev) => { const updatedErrors = { ...prev }; - if (validation.error) updatedErrors[name] = validation.error.details[0].message; else delete updatedErrors[name]; return updatedErrors; @@ -183,7 +189,7 @@ const Configure = () => { if (validation.error) { const newErrors = {}; - error.details.forEach((err) => { + validation.error.details.forEach((err) => { newErrors[err.path[0]] = err.message; }); setErrors(newErrors); @@ -192,27 +198,12 @@ const Configure = () => { } toSubmit.notifications = form.notifications; - const action = await dispatch(updateUptimeMonitor({ monitor: toSubmit })); - if (action.meta.requestStatus === "fulfilled") { - createToast({ body: "Monitor updated successfully!" }); - } else { - createToast({ body: "Failed to update monitor." }); - } + console.log(JSON.stringify(toSubmit, null, 2)); + // await updateMonitor({ monitor: toSubmit, redirect: "/uptime" }); }; - // Effects - useEffect(() => { - if (monitor?.matchMethod) { - setUseAdvancedMatching(true); - } - - setForm({ - ...monitor, - }); - }, [monitor, notifications]); - // Parse the URL - const parsedUrl = parseUrl(monitor?.url); + const parsedUrl = parseUrl(form?.url); const protocol = parsedUrl?.protocol?.replace(":", "") || ""; const { determineState, statusColor } = useMonitorUtils(); @@ -434,7 +425,13 @@ const Configure = () => { onChange={onChange} items={frequencies} /> - {form.type === "http" && ( + + {form.type === "http" && useAdvancedMatching && ( <> Date: Fri, 13 Jun 2025 11:15:42 +0800 Subject: [PATCH 22/36] remove team id from notificaitons --- client/src/Hooks/useNotifications.js | 7 ++----- client/src/Pages/Notifications/index.jsx | 1 - client/src/Utils/NetworkService.js | 3 +-- 3 files changed, 3 insertions(+), 8 deletions(-) diff --git a/client/src/Hooks/useNotifications.js b/client/src/Hooks/useNotifications.js index 54e3d1bd9..b269c5edc 100644 --- a/client/src/Hooks/useNotifications.js +++ b/client/src/Hooks/useNotifications.js @@ -37,15 +37,12 @@ const useGetNotificationsByTeamId = (updateTrigger) => { const [notifications, setNotifications] = useState([]); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); - const { user } = useSelector((state) => state.auth); const { t } = useTranslation(); const getNotifications = useCallback(async () => { try { setIsLoading(true); - const response = await networkService.getNotificationsByTeamId({ - teamId: user.teamId, - }); + const response = await networkService.getNotificationsByTeamId(); setNotifications(response?.data?.data ?? []); } catch (error) { setError(error); @@ -55,7 +52,7 @@ const useGetNotificationsByTeamId = (updateTrigger) => { } finally { setIsLoading(false); } - }, [user.teamId]); + }, [t]); useEffect(() => { getNotifications(); diff --git a/client/src/Pages/Notifications/index.jsx b/client/src/Pages/Notifications/index.jsx index 9ad5f1578..ce5cbb547 100644 --- a/client/src/Pages/Notifications/index.jsx +++ b/client/src/Pages/Notifications/index.jsx @@ -17,7 +17,6 @@ import { useDeleteNotification, } from "../../Hooks/useNotifications"; import { useTranslation } from "react-i18next"; -import { useParams } from "react-router-dom"; const Notifications = () => { const navigate = useNavigate(); diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index e84596b97..1ab56d9ad 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -996,8 +996,7 @@ class NetworkService { } async getNotificationsByTeamId(config) { - const { teamId } = config; - return this.axiosInstance.get(`/notifications/team/${teamId}`); + return this.axiosInstance.get(`/notifications/team`); } async deleteNotificationById(config) { From 4168c8673471357bf9d421d35199a33702be79d0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 11:47:29 +0800 Subject: [PATCH 23/36] remove team id from monitor controller/db methods, standardize parameters --- client/src/Hooks/useBulkMonitors.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/client/src/Hooks/useBulkMonitors.js b/client/src/Hooks/useBulkMonitors.js index a834f0140..ba9086dac 100644 --- a/client/src/Hooks/useBulkMonitors.js +++ b/client/src/Hooks/useBulkMonitors.js @@ -9,8 +9,6 @@ export const useBulkMonitors = () => { const formData = new FormData(); formData.append("csvFile", file); - formData.append("userId", user._id); - formData.append("teamId", user.teamId); try { const response = await networkService.createBulkMonitors(formData); From b9fbfd2a2560a4609783560e8f06de4afc973c1f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 11:49:03 +0800 Subject: [PATCH 24/36] remove team id from monitor controller/db methods, standardize parameters --- server/controllers/monitorController.js | 106 ++++++++++++------ server/controllers/notificationController.js | 2 +- server/db/mongo/modules/diagnosticModule.js | 3 - server/db/mongo/modules/monitorModule.js | 97 ++++++++++------ .../db/mongo/modules/monitorModuleQueries.js | 98 ---------------- server/routes/notificationRoute.js | 5 +- 6 files changed, 138 insertions(+), 173 deletions(-) diff --git a/server/controllers/monitorController.js b/server/controllers/monitorController.js index 00b525125..4262b9818 100755 --- a/server/controllers/monitorController.js +++ b/server/controllers/monitorController.js @@ -78,7 +78,14 @@ class MonitorController { getUptimeDetailsById = async (req, res, next) => { try { - const data = await this.db.getUptimeDetailsById(req); + const { monitorId } = req.params; + const { dateRange, normalize } = req.query; + + const data = await this.db.getUptimeDetailsById({ + monitorId, + dateRange, + normalize, + }); return res.success({ msg: this.stringService.monitorGetByIdSuccess, data, @@ -107,7 +114,17 @@ class MonitorController { } try { - const monitorStats = await this.db.getMonitorStatsById(req); + let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query; + const { monitorId } = req.params; + + const monitorStats = await this.db.getMonitorStatsById({ + monitorId, + limit, + sortOrder, + dateRange, + numToDisplay, + normalize, + }); return res.success({ msg: this.stringService.monitorStatsById, data: monitorStats, @@ -135,7 +152,9 @@ class MonitorController { return; } try { - const monitor = await this.db.getHardwareDetailsById(req); + const { monitorId } = req.params; + const { dateRange } = req.query; + const monitor = await this.db.getHardwareDetailsById({ monitorId, dateRange }); return res.success({ msg: this.stringService.monitorGetByIdSuccess, data: monitor, @@ -219,7 +238,7 @@ class MonitorController { } try { - const monitor = await this.db.createMonitor(req, res); + const monitor = await this.db.createMonitor(req.body); // Add monitor to job queue this.jobQueue.addJob(monitor._id, monitor); @@ -261,10 +280,10 @@ class MonitorController { throw new Error("File is empty"); } - const { userId, teamId } = req.body; + const { _id, teamId } = req.user; - if (!userId || !teamId) { - throw new Error("Missing userId or teamId in form data"); + if (!_id || !teamId) { + throw new Error("Missing userId or teamId"); } // Get file buffer from memory and convert to string @@ -299,7 +318,7 @@ class MonitorController { } const enrichedData = data.map((monitor) => ({ - userId, + userId: _id, teamId, ...monitor, description: monitor.description || monitor.name || monitor.url, @@ -388,7 +407,8 @@ class MonitorController { } try { - const monitor = await this.db.deleteMonitor(req, res, next); + const monitorId = req.params.monitorId; + const monitor = await this.db.deleteMonitor({ monitorId }); await this.jobQueue.deleteJob(monitor); await this.db.deleteStatusPagesByMonitorId(monitor._id); return res.success({ msg: this.stringService.monitorDelete }); @@ -410,9 +430,7 @@ class MonitorController { */ deleteAllMonitors = async (req, res, next) => { try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); + const { teamId } = req.user; const { monitors, deletedCount } = await this.db.deleteAllMonitors(teamId); await Promise.all( monitors.map(async (monitor) => { @@ -494,18 +512,8 @@ class MonitorController { } try { - const monitor = await Monitor.findOneAndUpdate( - { _id: req.params.monitorId }, - [ - { - $set: { - isActive: { $not: "$isActive" }, - status: "$$REMOVE", - }, - }, - ], - { new: true } - ); + const monitorId = req.params.monitorId; + const monitor = await this.db.pauseMonitor({ monitorId }); monitor.isActive === true ? await this.jobQueue.resumeJob(monitor._id, monitor) : await this.jobQueue.pauseJob(monitor); @@ -534,9 +542,7 @@ class MonitorController { */ addDemoMonitors = async (req, res, next) => { try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { _id, teamId } = jwt.verify(token, jwtSecret); + const { _id, teamId } = req.user; const demoMonitors = await this.db.addDemoMonitors(_id, teamId); await Promise.all( demoMonitors.map((monitor) => this.jobQueue.addJob(monitor._id, monitor)) @@ -603,7 +609,19 @@ class MonitorController { } try { - const monitors = await this.db.getMonitorsByTeamId(req); + let { limit, type, page, rowsPerPage, filter, field, order } = req.query; + const teamId = req.user.teamId; + + const monitors = await this.db.getMonitorsByTeamId({ + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + teamId, + }); return res.success({ msg: this.stringService.monitorGetByTeamId, data: monitors, @@ -622,7 +640,15 @@ class MonitorController { } try { - const result = await this.db.getMonitorsAndSummaryByTeamId(req); + const { explain } = req; + const { type } = req.query; + const { teamId } = req.user; + + const result = await this.db.getMonitorsAndSummaryByTeamId({ + type, + explain, + teamId, + }); return res.success({ msg: "OK", // TODO data: result, @@ -641,7 +667,21 @@ class MonitorController { } try { - const result = await this.db.getMonitorsWithChecksByTeamId(req); + const { explain } = req; + let { limit, type, page, rowsPerPage, filter, field, order } = req.query; + const { teamId } = req.user; + + const result = await this.db.getMonitorsWithChecksByTeamId({ + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + teamId, + explain, + }); return res.success({ msg: "OK", // TODO data: result, @@ -653,11 +693,7 @@ class MonitorController { seedDb = async (req, res, next) => { try { - const { type } = req.body; - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { _id, teamId } = jwt.verify(token, jwtSecret); - + const { _id, teamId } = req.user; await seedDb(_id, teamId); res.success({ msg: "Database seeded" }); } catch (error) { diff --git a/server/controllers/notificationController.js b/server/controllers/notificationController.js index 4efcf4988..a9c399901 100755 --- a/server/controllers/notificationController.js +++ b/server/controllers/notificationController.js @@ -140,7 +140,7 @@ class NotificationController { getNotificationsByTeamId = async (req, res, next) => { try { - const notifications = await this.db.getNotificationsByTeamId(req.params.teamId); + const notifications = await this.db.getNotificationsByTeamId(req.user.teamId); return res.success({ msg: "Notifications fetched successfully", data: notifications, diff --git a/server/db/mongo/modules/diagnosticModule.js b/server/db/mongo/modules/diagnosticModule.js index af6d83764..7a51ad193 100755 --- a/server/db/mongo/modules/diagnosticModule.js +++ b/server/db/mongo/modules/diagnosticModule.js @@ -6,10 +6,7 @@ import { buildMonitorSummaryByTeamIdPipeline, buildMonitorsByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline, - buildDePINDetailsByDateRange, - buildDePINLatestChecks, } from "./monitorModuleQueries.js"; -import { getDateRange } from "./monitorModule.js"; const getMonitorsByTeamIdExecutionStats = async (req) => { try { diff --git a/server/db/mongo/modules/monitorModule.js b/server/db/mongo/modules/monitorModule.js index b6044f2c9..700625b73 100755 --- a/server/db/mongo/modules/monitorModule.js +++ b/server/db/mongo/modules/monitorModule.js @@ -3,7 +3,6 @@ import MonitorStats from "../../models/MonitorStats.js"; import Check from "../../models/Check.js"; import PageSpeedCheck from "../../models/PageSpeedCheck.js"; import HardwareCheck from "../../models/HardwareCheck.js"; -import Notification from "../../models/Notification.js"; import { NormalizeData, NormalizeDataUptimeDetails } from "../../../utils/dataUtils.js"; import ServiceRegistry from "../../../service/serviceRegistry.js"; import StringService from "../../../service/stringService.js"; @@ -15,14 +14,11 @@ import { ObjectId } from "mongodb"; import { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline, - buildMonitorStatsPipeline, buildMonitorSummaryByTeamIdPipeline, buildMonitorsByTeamIdPipeline, buildMonitorsAndSummaryByTeamIdPipeline, buildMonitorsWithChecksByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline, - buildDePINDetailsByDateRange, - buildDePINLatestChecks, } from "./monitorModuleQueries.js"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -335,11 +331,9 @@ const calculateGroupStats = (group) => { * @returns {Promise} * @throws {Error} */ -const getUptimeDetailsById = async (req) => { +const getUptimeDetailsById = async ({ monitorId, dateRange, normalize }) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { - const { monitorId } = req.params; - const { dateRange, normalize } = req.query; const dates = getDateRange(dateRange); const formatLookup = { recent: "%Y-%m-%dT%H:%M:00Z", @@ -392,11 +386,16 @@ const getUptimeDetailsById = async (req) => { * @returns {Promise} * @throws {Error} */ -const getMonitorStatsById = async (req) => { +const getMonitorStatsById = async ({ + monitorId, + limit, + sortOrder, + dateRange, + numToDisplay, + normalize, +}) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { - const { monitorId } = req.params; - // Get monitor, if we can't find it, abort with error const monitor = await Monitor.findById(monitorId); if (monitor === null || monitor === undefined) { @@ -404,7 +403,6 @@ const getMonitorStatsById = async (req) => { } // Get query params - let { limit, sortOrder, dateRange, numToDisplay, normalize } = req.query; const sort = sortOrder === "asc" ? 1 : -1; // Get Checks for monitor in date range requested @@ -454,10 +452,8 @@ const getMonitorStatsById = async (req) => { } }; -const getHardwareDetailsById = async (req) => { +const getHardwareDetailsById = async ({ monitorId, dateRange }) => { try { - const { monitorId } = req.params; - const { dateRange } = req.query; const monitor = await Monitor.findById(monitorId); const dates = getDateRange(dateRange); const formatLookup = { @@ -509,8 +505,16 @@ const getMonitorById = async (monitorId) => { } }; -const getMonitorsByTeamId = async (req) => { - let { limit, type, page, rowsPerPage, filter, field, order } = req.query; +const getMonitorsByTeamId = async ({ + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + teamId, +}) => { limit = parseInt(limit); page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); @@ -519,7 +523,6 @@ const getMonitorsByTeamId = async (req) => { order = "asc"; } // Build match stage - const teamId = req.user.teamId; const matchStage = { teamId: ObjectId.createFromHexString(teamId) }; if (type !== undefined) { matchStage.type = Array.isArray(type) ? { $in: type } : type; @@ -558,16 +561,15 @@ const getMonitorsByTeamId = async (req) => { return { summary, monitors, filteredMonitors: normalizedFilteredMonitors }; }; -const getMonitorsAndSummaryByTeamId = async (req) => { +const getMonitorsAndSummaryByTeamId = async ({ type, explain, teamId }) => { try { - const { type } = req.query; - const teamId = ObjectId.createFromHexString(req.user.teamId); - const matchStage = { teamId }; + const parsedTeamId = ObjectId.createFromHexString(teamId); + const matchStage = { teamId: parsedTeamId }; if (type !== undefined) { matchStage.type = Array.isArray(type) ? { $in: type } : type; } - if (req.explain === true) { + if (explain === true) { return Monitor.aggregate( buildMonitorsAndSummaryByTeamIdPipeline({ matchStage }) ).explain("executionStats"); @@ -585,9 +587,18 @@ const getMonitorsAndSummaryByTeamId = async (req) => { } }; -const getMonitorsWithChecksByTeamId = async (req) => { +const getMonitorsWithChecksByTeamId = async ({ + limit, + type, + page, + rowsPerPage, + filter, + field, + order, + teamId, + explain, +}) => { try { - let { limit, type, page, rowsPerPage, filter, field, order } = req.query; limit = parseInt(limit); page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); @@ -595,14 +606,14 @@ const getMonitorsWithChecksByTeamId = async (req) => { field = "name"; order = "asc"; } - const teamId = ObjectId.createFromHexString(req.user.teamId); + const parsedTeamId = ObjectId.createFromHexString(teamId); // Build match stage - const matchStage = { teamId }; + const matchStage = { teamId: parsedTeamId }; if (type !== undefined) { matchStage.type = Array.isArray(type) ? { $in: type } : type; } - if (req.explain === true) { + if (explain === true) { return Monitor.aggregate( buildMonitorsWithChecksByTeamIdPipeline({ matchStage, @@ -654,9 +665,9 @@ const getMonitorsWithChecksByTeamId = async (req) => { * @returns {Promise} * @throws {Error} */ -const createMonitor = async (req, res) => { +const createMonitor = async (body) => { try { - const monitor = new Monitor({ ...req.body }); + const monitor = new Monitor({ ...body }); const saved = await monitor.save(); return saved; } catch (error) { @@ -695,10 +706,8 @@ const createBulkMonitors = async (req) => { * @returns {Promise} * @throws {Error} */ -const deleteMonitor = async (req, res) => { +const deleteMonitor = async ({ monitorId }) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); - - const monitorId = req.params.monitorId; try { const monitor = await Monitor.findByIdAndDelete(monitorId); if (!monitor) { @@ -789,6 +798,29 @@ const addDemoMonitors = async (userId, teamId) => { } }; +const pauseMonitor = async ({ monitorId }) => { + try { + const monitor = await Monitor.findOneAndUpdate( + { _id: monitorId }, + [ + { + $set: { + isActive: { $not: "$isActive" }, + status: "$$REMOVE", + }, + }, + ], + { new: true } + ); + + return monitor; + } catch (error) { + error.service = SERVICE_NAME; + error.method = "pauseMonitor"; + throw error; + } +}; + export { getAllMonitors, getAllMonitorsWithUptimeStats, @@ -806,6 +838,7 @@ export { editMonitor, addDemoMonitors, getHardwareDetailsById, + pauseMonitor, }; // Helper functions diff --git a/server/db/mongo/modules/monitorModuleQueries.js b/server/db/mongo/modules/monitorModuleQueries.js index f17eff2c2..35ddb8478 100755 --- a/server/db/mongo/modules/monitorModuleQueries.js +++ b/server/db/mongo/modules/monitorModuleQueries.js @@ -902,102 +902,6 @@ const buildGetMonitorsByTeamIdPipeline = (req) => { ]; }; -const buildDePINDetailsByDateRange = (monitor, dates, dateString) => { - return [ - { - $match: { - monitorId: monitor._id, - updatedAt: { $gte: dates.start, $lte: dates.end }, - }, - }, - { - $project: { - _id: 0, - city: 1, - updatedAt: 1, - "location.lat": 1, - "location.lng": 1, - responseTime: 1, - }, - }, - { - $facet: { - groupedMapChecks: [ - { - $group: { - _id: { - city: "$city", - lat: "$location.lat", - lng: "$location.lng", - }, - - avgResponseTime: { - $avg: "$responseTime", - }, - }, - }, - ], - groupedChecks: [ - { - $group: { - _id: { - date: { - $dateToString: { - format: dateString, - date: "$updatedAt", - }, - }, - }, - avgResponseTime: { - $avg: "$responseTime", - }, - }, - }, - { - $sort: { - "_id.date": 1, - }, - }, - ], - }, - }, - { - $project: { - groupedMapChecks: "$groupedMapChecks", - groupedChecks: "$groupedChecks", - }, - }, - ]; -}; - -const buildDePINLatestChecks = (monitor) => { - return [ - { - $match: { - monitorId: monitor._id, - }, - }, - { - $sort: { updatedAt: -1 }, - }, - { - $limit: 5, - }, - { - $project: { - responseTime: 1, - city: 1, - countryCode: 1, - uptBurnt: { - $toString: { - $ifNull: ["$uptBurnt", 0], - }, - }, - }, - }, - ]; -}; - export { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline, @@ -1008,6 +912,4 @@ export { buildMonitorsAndSummaryByTeamIdPipeline, buildMonitorsWithChecksByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline, - buildDePINDetailsByDateRange, - buildDePINLatestChecks, }; diff --git a/server/routes/notificationRoute.js b/server/routes/notificationRoute.js index 064cd4b5d..ace8bdae1 100755 --- a/server/routes/notificationRoute.js +++ b/server/routes/notificationRoute.js @@ -17,10 +17,7 @@ class NotificationRoutes { this.router.post("/", this.notificationController.createNotification); - this.router.get( - "/team/:teamId", - this.notificationController.getNotificationsByTeamId - ); + this.router.get("/team", this.notificationController.getNotificationsByTeamId); this.router.delete("/:id", this.notificationController.deleteNotification); From cedd660fd99ccf0afbecb8c9414531eb1fa758de Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 11:52:10 +0800 Subject: [PATCH 25/36] get teamID from req.user --- server/controllers/maintenanceWindowController.js | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/server/controllers/maintenanceWindowController.js b/server/controllers/maintenanceWindowController.js index a11f957ed..20077cc65 100755 --- a/server/controllers/maintenanceWindowController.js +++ b/server/controllers/maintenanceWindowController.js @@ -7,8 +7,6 @@ import { getMaintenanceWindowsByTeamIdQueryValidation, deleteMaintenanceWindowByIdParamValidation, } from "../validation/joi.js"; -import jwt from "jsonwebtoken"; -import { getTokenFromHeaders } from "../utils/utils.js"; import { handleValidationError, handleError } from "./controllerUtils.js"; const SERVICE_NAME = "maintenanceWindowController"; @@ -28,9 +26,7 @@ class MaintenanceWindowController { return; } try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); + const { teamId } = req.user; const monitorIds = req.body.monitors; const dbTransactions = monitorIds.map((monitorId) => { return this.db.createMaintenanceWindow({ @@ -81,9 +77,7 @@ class MaintenanceWindowController { } try { - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); + const { teamId } = req.user; const maintenanceWindows = await this.db.getMaintenanceWindowsByTeamId( teamId, req.query From 9da3d3ef552c74a86834ca09bd6234fe8f42bd08 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 12:03:32 +0800 Subject: [PATCH 26/36] remove teamId from checks --- client/src/Utils/NetworkService.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index 1ab56d9ad..f7ebdc3f4 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -272,7 +272,7 @@ class NetworkService { * @returns {Promise} The response from the axios DELETE request. */ async deleteChecksByTeamId(config) { - return this.axiosInstance.delete(`/checks/team/${config.teamId}`, { + return this.axiosInstance.delete(`/checks/team`, { headers: { "Content-Type": "application/json", }, @@ -595,7 +595,7 @@ class NetworkService { if (config.page) params.append("page", config.page); if (config.rowsPerPage) params.append("rowsPerPage", config.rowsPerPage); if (config.status !== undefined) params.append("status", config.status); - return this.axiosInstance.get(`/checks/team/${config.teamId}?${params.toString()}`); + return this.axiosInstance.get(`/checks/team?${params.toString()}`); }; /** From d2233d2e3eb89e469854db82d16f73e6958e0181 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 12:04:39 +0800 Subject: [PATCH 27/36] remove teamId from checks --- server/controllers/checkController.js | 31 +++++++++++++++++++++----- server/db/mongo/modules/checkModule.js | 24 +++++++++++++++----- server/routes/checkRoute.js | 16 ++++++------- server/validation/joi.js | 8 ++----- 4 files changed, 53 insertions(+), 26 deletions(-) diff --git a/server/controllers/checkController.js b/server/controllers/checkController.js index 2addd7359..387839beb 100755 --- a/server/controllers/checkController.js +++ b/server/controllers/checkController.js @@ -54,7 +54,18 @@ class CheckController { } try { - const result = await this.db.getChecksByMonitor(req); + const { monitorId } = req.params; + let { type, sortOrder, dateRange, filter, page, rowsPerPage, status } = req.query; + const result = await this.db.getChecksByMonitor({ + monitorId, + type, + sortOrder, + dateRange, + filter, + page, + rowsPerPage, + status, + }); return res.success({ msg: this.stringService.checkGet, @@ -74,8 +85,17 @@ class CheckController { return; } try { - const checkData = await this.db.getChecksByTeam(req); + let { sortOrder, dateRange, filter, page, rowsPerPage } = req.query; + const { teamId } = req.user; + const checkData = await this.db.getChecksByTeam({ + sortOrder, + dateRange, + filter, + page, + rowsPerPage, + teamId, + }); return res.success({ msg: this.stringService.checkGet, data: checkData, @@ -114,7 +134,8 @@ class CheckController { } try { - const deletedCount = await this.db.deleteChecksByTeamId(req.params.teamId); + const { teamId } = req.user; + const deletedCount = await this.db.deleteChecksByTeamId(teamId); return res.success({ msg: this.stringService.checkDelete, @@ -137,9 +158,7 @@ class CheckController { try { // Get user's teamId - const token = getTokenFromHeaders(req.headers); - const { jwtSecret } = this.settingsService.getSettings(); - const { teamId } = jwt.verify(token, jwtSecret); + const { teamId } = req.user; const ttl = parseInt(req.body.ttl, 10) * SECONDS_PER_DAY; await this.db.updateChecksTTL(teamId, ttl); diff --git a/server/db/mongo/modules/checkModule.js b/server/db/mongo/modules/checkModule.js index 8e71dd6eb..f409fd3d2 100755 --- a/server/db/mongo/modules/checkModule.js +++ b/server/db/mongo/modules/checkModule.js @@ -57,10 +57,17 @@ const createChecks = async (checks) => { * @returns {Promise>} * @throws {Error} */ -const getChecksByMonitor = async (req) => { +const getChecksByMonitor = async ({ + monitorId, + type, + sortOrder, + dateRange, + filter, + page, + rowsPerPage, + status, +}) => { try { - const { monitorId } = req.params; - let { type, sortOrder, dateRange, filter, page, rowsPerPage, status } = req.query; status = typeof status !== "undefined" ? false : undefined; page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); @@ -142,12 +149,17 @@ const getChecksByMonitor = async (req) => { } }; -const getChecksByTeam = async (req) => { +const getChecksByTeam = async ({ + sortOrder, + dateRange, + filter, + page, + rowsPerPage, + teamId, +}) => { try { - let { sortOrder, dateRange, filter, page, rowsPerPage } = req.query; page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); - const { teamId } = req.params; const matchStage = { teamId: ObjectId.createFromHexString(teamId), status: false, diff --git a/server/routes/checkRoute.js b/server/routes/checkRoute.js index bab45e4d4..aedca991a 100755 --- a/server/routes/checkRoute.js +++ b/server/routes/checkRoute.js @@ -11,7 +11,15 @@ class CheckRoutes { } initRoutes() { + this.router.get("/team", this.checkController.getChecksByTeam); + this.router.delete( + "/team", + isAllowed(["admin", "superadmin"]), + this.checkController.deleteChecksByTeamId + ); + this.router.get("/:monitorId", this.checkController.getChecksByMonitor); + this.router.post( "/:monitorId", verifyOwnership(Monitor, "monitorId"), @@ -23,14 +31,6 @@ class CheckRoutes { this.checkController.deleteChecks ); - this.router.get("/team/:teamId", this.checkController.getChecksByTeam); - - this.router.delete( - "/team/:teamId", - isAllowed(["admin", "superadmin"]), - this.checkController.deleteChecksByTeamId - ); - this.router.put( "/team/ttl", isAllowed(["admin", "superadmin"]), diff --git a/server/validation/joi.js b/server/validation/joi.js index aba7118d1..2d1f10d29 100755 --- a/server/validation/joi.js +++ b/server/validation/joi.js @@ -301,9 +301,7 @@ const getChecksQueryValidation = joi.object({ status: joi.boolean(), }); -const getTeamChecksParamValidation = joi.object({ - teamId: joi.string().required(), -}); +const getTeamChecksParamValidation = joi.object({}); const getTeamChecksQueryValidation = joi.object({ sortOrder: joi.string().valid("asc", "desc"), @@ -319,9 +317,7 @@ const deleteChecksParamValidation = joi.object({ monitorId: joi.string().required(), }); -const deleteChecksByTeamIdParamValidation = joi.object({ - teamId: joi.string().required(), -}); +const deleteChecksByTeamIdParamValidation = joi.object({}); const updateChecksTTLBodyValidation = joi.object({ ttl: joi.number().required(), From c32cde61894b10b77d915dc6a1a887a6f1acb7c0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 12:08:59 +0800 Subject: [PATCH 28/36] fix incidents page --- .../src/Pages/Incidents/Components/IncidentTable/index.jsx | 5 ++--- client/src/Pages/Incidents/index.jsx | 6 +----- 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/client/src/Pages/Incidents/Components/IncidentTable/index.jsx b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx index bc4872735..cf6fbb49d 100644 --- a/client/src/Pages/Incidents/Components/IncidentTable/index.jsx +++ b/client/src/Pages/Incidents/Components/IncidentTable/index.jsx @@ -24,7 +24,6 @@ const IncidentTable = ({ }) => { //Redux state const uiTimezone = useSelector((state) => state.ui.timezone); - const { user } = useSelector((state) => state.auth); //Local state const [teamId, setTeamId] = useState(undefined); @@ -61,13 +60,13 @@ const IncidentTable = ({ useEffect(() => { if (selectedMonitor === "0") { - setTeamId(user.teamId); + setTeamId("placeholder"); // TODO this isn't needed any longer, fix hook setMonitorId(undefined); } else { setMonitorId(selectedMonitor); setTeamId(undefined); } - }, [selectedMonitor, user.teamId]); + }, [selectedMonitor]); const headers = [ { diff --git a/client/src/Pages/Incidents/index.jsx b/client/src/Pages/Incidents/index.jsx index d01543f8a..80a7db702 100644 --- a/client/src/Pages/Incidents/index.jsx +++ b/client/src/Pages/Incidents/index.jsx @@ -8,7 +8,6 @@ import OptionsHeader from "./Components/OptionsHeader"; //Utils import { useTheme } from "@emotion/react"; import { useFetchMonitorsByTeamId } from "../../Hooks/monitorHooks"; -import { useSelector } from "react-redux"; import { useState, useEffect } from "react"; import NetworkError from "../../Components/GenericFallback/NetworkError"; import { useTranslation } from "react-i18next"; @@ -16,7 +15,6 @@ import { useTranslation } from "react-i18next"; //Constants const Incidents = () => { // Redux state - const { user } = useSelector((state) => state.auth); const { t } = useTranslation(); const BREADCRUMBS = [ @@ -31,9 +29,7 @@ const Incidents = () => { //Utils const theme = useTheme(); - const [monitors, , isLoading, networkError] = useFetchMonitorsByTeamId({ - teamId: user.teamId, - }); + const [monitors, , isLoading, networkError] = useFetchMonitorsByTeamId(); useEffect(() => { const monitorLookup = monitors?.reduce((acc, monitor) => { From 5e23a2b79c73334ab6ab4dfbcde5e833d19de8c4 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 12:16:52 +0800 Subject: [PATCH 29/36] Remove team and user id --- client/src/Hooks/monitorHooks.js | 4 ++-- client/src/Pages/Infrastructure/Create/index.jsx | 2 -- client/src/Pages/Notifications/create/index.jsx | 2 -- client/src/Pages/PageSpeed/Create/index.jsx | 2 -- client/src/Pages/Settings/index.jsx | 2 +- client/src/Pages/Uptime/Create/index.jsx | 2 -- server/controllers/monitorController.js | 7 ++++++- server/db/mongo/modules/monitorModule.js | 4 ++-- 8 files changed, 11 insertions(+), 14 deletions(-) diff --git a/client/src/Hooks/monitorHooks.js b/client/src/Hooks/monitorHooks.js index 5072b5594..8df0faa3b 100644 --- a/client/src/Hooks/monitorHooks.js +++ b/client/src/Hooks/monitorHooks.js @@ -414,10 +414,10 @@ const useDeleteAllMonitors = () => { const UseDeleteMonitorStats = () => { const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); - const deleteMonitorStats = async ({ teamId }) => { + const deleteMonitorStats = async () => { setIsLoading(true); try { - const res = await networkService.deleteChecksByTeamId({ teamId }); + await networkService.deleteChecksByTeamId(); createToast({ body: t("settingsStatsCleared") }); } catch (error) { createToast({ body: t("settingsFailedToClearStats") }); diff --git a/client/src/Pages/Infrastructure/Create/index.jsx b/client/src/Pages/Infrastructure/Create/index.jsx index fa63dd4dc..bcdf09785 100644 --- a/client/src/Pages/Infrastructure/Create/index.jsx +++ b/client/src/Pages/Infrastructure/Create/index.jsx @@ -186,8 +186,6 @@ const CreateInfrastructureMonitor = () => { ...(isCreate ? {} : { _id: monitorId }), ...rest, description: form.name, - teamId: user.teamId, - userId: user._id, type: "hardware", notifications: infrastructureMonitor.notifications, thresholds, diff --git a/client/src/Pages/Notifications/create/index.jsx b/client/src/Pages/Notifications/create/index.jsx index 19335c790..3c17c113c 100644 --- a/client/src/Pages/Notifications/create/index.jsx +++ b/client/src/Pages/Notifications/create/index.jsx @@ -48,8 +48,6 @@ const CreateNotifications = () => { // local state const [notification, setNotification] = useState({ - userId: user._id, - teamId: user.teamId, notificationName: "", address: "", type: NOTIFICATION_TYPES[0]._id, diff --git a/client/src/Pages/PageSpeed/Create/index.jsx b/client/src/Pages/PageSpeed/Create/index.jsx index a138fe0a2..5cc4a1a54 100644 --- a/client/src/Pages/PageSpeed/Create/index.jsx +++ b/client/src/Pages/PageSpeed/Create/index.jsx @@ -86,8 +86,6 @@ const CreatePageSpeed = () => { form = { ...form, description: form.name, - teamId: user.teamId, - userId: user._id, notifications: monitor.notifications, }; diff --git a/client/src/Pages/Settings/index.jsx b/client/src/Pages/Settings/index.jsx index d8bd79551..11e432e17 100644 --- a/client/src/Pages/Settings/index.jsx +++ b/client/src/Pages/Settings/index.jsx @@ -96,7 +96,7 @@ const Settings = () => { } if (name === "deleteStats") { - await deleteMonitorStats({ teamId: user.teamId }); + await deleteMonitorStats(); return; } diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx index 184beffe5..c800e295c 100644 --- a/client/src/Pages/Uptime/Create/index.jsx +++ b/client/src/Pages/Uptime/Create/index.jsx @@ -142,8 +142,6 @@ const CreateMonitor = () => { form = { ...form, description: monitor.name || monitor.url, - teamId: user.teamId, - userId: user._id, notifications: monitor.notifications, }; diff --git a/server/controllers/monitorController.js b/server/controllers/monitorController.js index 4262b9818..228eabbb0 100755 --- a/server/controllers/monitorController.js +++ b/server/controllers/monitorController.js @@ -238,7 +238,12 @@ class MonitorController { } try { - const monitor = await this.db.createMonitor(req.body); + const { _id, teamId } = req.user; + const monitor = await this.db.createMonitor({ + body: req.body, + teamId, + userId: _id, + }); // Add monitor to job queue this.jobQueue.addJob(monitor._id, monitor); diff --git a/server/db/mongo/modules/monitorModule.js b/server/db/mongo/modules/monitorModule.js index 700625b73..6c03dcd4c 100755 --- a/server/db/mongo/modules/monitorModule.js +++ b/server/db/mongo/modules/monitorModule.js @@ -665,9 +665,9 @@ const getMonitorsWithChecksByTeamId = async ({ * @returns {Promise} * @throws {Error} */ -const createMonitor = async (body) => { +const createMonitor = async ({ body, teamId, userId }) => { try { - const monitor = new Monitor({ ...body }); + const monitor = new Monitor({ ...body, teamId, userId }); const saved = await monitor.save(); return saved; } catch (error) { From 42372ab111e231edc2e4ff1da893e439e8bf1e7e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 13:10:56 +0800 Subject: [PATCH 30/36] remove userID and teamID from status page --- client/src/Utils/NetworkService.js | 4 +--- server/controllers/statusPageController.js | 8 +++++++- server/db/mongo/modules/statusPageModule.js | 9 ++++++--- server/validation/joi.js | 2 -- 4 files changed, 14 insertions(+), 9 deletions(-) diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index f7ebdc3f4..03225c440 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -847,11 +847,9 @@ class NetworkService { } async createStatusPage(config) { - const { user, form, isCreate } = config; + const { form, isCreate } = config; const fd = new FormData(); - fd.append("teamId", user.teamId); - fd.append("userId", user._id); fd.append("type", form.type); form.isPublished !== undefined && fd.append("isPublished", form.isPublished); form.companyName && fd.append("companyName", form.companyName); diff --git a/server/controllers/statusPageController.js b/server/controllers/statusPageController.js index eb3e69484..842eb657a 100755 --- a/server/controllers/statusPageController.js +++ b/server/controllers/statusPageController.js @@ -24,7 +24,13 @@ class StatusPageController { } try { - const statusPage = await this.db.createStatusPage(req.body, req.file); + const { _id, teamId } = req.user; + const statusPage = await this.db.createStatusPage({ + statusPageData: req.body, + image: req.file, + userId: _id, + teamId, + }); return res.success({ msg: this.stringService.statusPageCreate, data: statusPage, diff --git a/server/db/mongo/modules/statusPageModule.js b/server/db/mongo/modules/statusPageModule.js index cd985588a..b2f7ef7d6 100755 --- a/server/db/mongo/modules/statusPageModule.js +++ b/server/db/mongo/modules/statusPageModule.js @@ -1,16 +1,19 @@ 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 createStatusPage = async ({ statusPageData, image, userId, teamId }) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { - const statusPage = new StatusPage({ ...statusPageData }); + const statusPage = new StatusPage({ + ...statusPageData, + userId, + teamId, + }); if (image) { statusPage.logo = { data: image.buffer, diff --git a/server/validation/joi.js b/server/validation/joi.js index 2d1f10d29..e43f6233f 100755 --- a/server/validation/joi.js +++ b/server/validation/joi.js @@ -424,8 +424,6 @@ const getStatusPageQueryValidation = joi.object({ }); const createStatusPageBodyValidation = joi.object({ - userId: joi.string().required(), - teamId: joi.string().required(), type: joi.string().valid("uptime").required(), companyName: joi.string().required(), url: joi From 18ea55111807c18656e9a3d3152e71f2b5765cc2 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 13:11:58 +0800 Subject: [PATCH 31/36] remove user --- .../Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/client/src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx b/client/src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx index 8407955a6..a2ac5f55d 100644 --- a/client/src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx +++ b/client/src/Pages/StatusPage/Create/Hooks/useCreateStatusPage.jsx @@ -1,17 +1,14 @@ import { useState } from "react"; import { networkService } from "../../../../main"; -import { useSelector } from "react-redux"; import { createToast } from "../../../../Utils/toastUtils"; const useCreateStatusPage = (isCreate, url) => { - const { user } = useSelector((state) => state.auth); - const [isLoading, setIsLoading] = useState(false); const [networkError, setNetworkError] = useState(false); const createStatusPage = async ({ form }) => { setIsLoading(true); try { - await networkService.createStatusPage({ user, form, isCreate, url }); + await networkService.createStatusPage({ form, isCreate, url }); return true; } catch (error) { setNetworkError(true); From b499b2879cbd4e024c3555eb4712b0a09f986942 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 13:29:03 +0800 Subject: [PATCH 32/36] remove teamId from status pages --- client/src/Pages/Incidents/index.jsx | 2 +- .../StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx | 4 +--- client/src/Utils/NetworkService.js | 3 +-- server/controllers/statusPageController.js | 2 +- server/routes/statusPageRoute.js | 6 +----- server/validation/joi.js | 2 -- 6 files changed, 5 insertions(+), 14 deletions(-) diff --git a/client/src/Pages/Incidents/index.jsx b/client/src/Pages/Incidents/index.jsx index 80a7db702..011961503 100644 --- a/client/src/Pages/Incidents/index.jsx +++ b/client/src/Pages/Incidents/index.jsx @@ -29,7 +29,7 @@ const Incidents = () => { //Utils const theme = useTheme(); - const [monitors, , isLoading, networkError] = useFetchMonitorsByTeamId(); + const [monitors, , isLoading, networkError] = useFetchMonitorsByTeamId({}); useEffect(() => { const monitorLookup = monitors?.reduce((acc, monitor) => { diff --git a/client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx b/client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx index db7a33396..b236368c8 100644 --- a/client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx +++ b/client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx @@ -13,9 +13,7 @@ const useStatusPagesFetch = () => { useEffect(() => { const fetchStatusPages = async () => { try { - const res = await networkService.getStatusPagesByTeamId({ - teamId: user.teamId, - }); + const res = await networkService.getStatusPagesByTeamId(); setStatusPages(res?.data?.data); } catch (error) { setNetworkError(true); diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index 03225c440..ecde44a9e 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -838,8 +838,7 @@ class NetworkService { } async getStatusPagesByTeamId(config) { - const { teamId } = config; - return this.axiosInstance.get(`/status-page/team/${teamId}`, { + return this.axiosInstance.get(`/status-page/team`, { headers: { "Content-Type": "application/json", }, diff --git a/server/controllers/statusPageController.js b/server/controllers/statusPageController.js index 842eb657a..577cf4997 100755 --- a/server/controllers/statusPageController.js +++ b/server/controllers/statusPageController.js @@ -99,7 +99,7 @@ class StatusPageController { getStatusPagesByTeamId = async (req, res, next) => { try { - const teamId = req.params.teamId; + const teamId = req.user.teamId; const statusPages = await this.db.getStatusPagesByTeamId(teamId); return res.success({ diff --git a/server/routes/statusPageRoute.js b/server/routes/statusPageRoute.js index 4e6a4d248..98b3a3433 100755 --- a/server/routes/statusPageRoute.js +++ b/server/routes/statusPageRoute.js @@ -12,11 +12,7 @@ class StatusPageRoutes { initRoutes() { this.router.get("/", this.statusPageController.getStatusPage); - this.router.get( - "/team/:teamId", - verifyJWT, - this.statusPageController.getStatusPagesByTeamId - ); + this.router.get("/team", verifyJWT, this.statusPageController.getStatusPagesByTeamId); this.router.get("/:url", this.statusPageController.getStatusPageByUrl); this.router.post( diff --git a/server/validation/joi.js b/server/validation/joi.js index e43f6233f..60f462280 100755 --- a/server/validation/joi.js +++ b/server/validation/joi.js @@ -165,8 +165,6 @@ const getCertificateParamValidation = joi.object({ const createMonitorBodyValidation = joi.object({ _id: joi.string(), - userId: joi.string().required(), - teamId: joi.string().required(), name: joi.string().required(), description: joi.string().required(), type: joi.string().required(), From ab3f867b5bd7e06652b519031728997720502c3e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 13:32:55 +0800 Subject: [PATCH 33/36] remove unused redux state --- client/src/Hooks/useNotifications.js | 1 - client/src/Pages/Uptime/Create/index.jsx | 4 ---- 2 files changed, 5 deletions(-) diff --git a/client/src/Hooks/useNotifications.js b/client/src/Hooks/useNotifications.js index b269c5edc..f19b76dde 100644 --- a/client/src/Hooks/useNotifications.js +++ b/client/src/Hooks/useNotifications.js @@ -2,7 +2,6 @@ import { useState, useEffect, useCallback } from "react"; import { createToast } from "../Utils/toastUtils"; import { networkService } from "../main"; import { useNavigate } from "react-router-dom"; -import { useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { NOTIFICATION_TYPES } from "../Pages/Notifications/utils"; diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx index c800e295c..0dce03889 100644 --- a/client/src/Pages/Uptime/Create/index.jsx +++ b/client/src/Pages/Uptime/Create/index.jsx @@ -18,7 +18,6 @@ import FormControlLabel from "@mui/material/FormControlLabel"; import { useTheme } from "@emotion/react"; import { useNavigate } from "react-router-dom"; import { useState } from "react"; -import { useSelector } from "react-redux"; import { useTranslation } from "react-i18next"; import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; @@ -26,9 +25,6 @@ import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications"; import { useCreateMonitor } from "../../../Hooks/monitorHooks"; const CreateMonitor = () => { - // Redux state - const { user } = useSelector((state) => state.auth); - // Local state const [errors, setErrors] = useState({}); const [https, setHttps] = useState(true); From 985ca3334da1ce631a53a779136d7a1e47039f8f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 13:49:54 +0800 Subject: [PATCH 34/36] move style to global theme --- client/src/Components/Inputs/Search/index.jsx | 22 +-------------- client/src/Utils/Theme/globalTheme.js | 27 +++++++++++++++++++ 2 files changed, 28 insertions(+), 21 deletions(-) diff --git a/client/src/Components/Inputs/Search/index.jsx b/client/src/Components/Inputs/Search/index.jsx index 86ce57779..e5654faeb 100644 --- a/client/src/Components/Inputs/Search/index.jsx +++ b/client/src/Components/Inputs/Search/index.jsx @@ -102,27 +102,7 @@ const Search = ({ ...(endAdornment && { endAdornment: endAdornment }), }, }} - sx={{ - "& fieldset": { - borderColor: theme.palette.primary.lowContrast, - borderRadius: theme.shape.borderRadius, - }, - "& .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset": - { - borderColor: theme.palette.primary.lowContrast, - }, - "& .MuiOutlinedInput-root": { - paddingY: 0, - }, - "& .MuiAutocomplete-tag": { - // CAIO_REVIEW - color: theme.palette.primary.contrastText, - backgroundColor: theme.palette.primary.lowContrast, - }, - "& .MuiChip-deleteIcon": { - color: theme.palette.primary.contrastText, // CAIO_REVIEW - }, - }} + sx={{}} /> {error && ( ({ }, }, }, + + MuiAutocomplete: { + styleOverrides: { + root: ({ theme }) => ({ + "& .MuiOutlinedInput-root": { + paddingTop: 0, + paddingBottom: 0, + }, + "& fieldset": { + borderColor: theme.palette.primary.lowContrast, + borderRadius: theme.shape.borderRadius, + }, + "& .MuiOutlinedInput-root:hover:not(:has(input:focus)):not(:has(textarea:focus)) fieldset": + { + borderColor: theme.palette.primary.lowContrast, + }, + + "& .MuiAutocomplete-tag": { + color: theme.palette.primary.contrastText, + backgroundColor: theme.palette.primary.lowContrast, + }, + "& .MuiChip-deleteIcon": { + color: theme.palette.primary.contrastText, // CAIO_REVIEW + }, + }), + }, + }, MuiTab: { styleOverrides: { root: ({ theme }) => ({ From cc8949538f4e0036eadecb630f18e3bcb75bc563 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 14:04:08 +0800 Subject: [PATCH 35/36] move bulk monitors hook to monitorHooks --- client/src/Hooks/monitorHooks.js | 28 ++++++++++++++++++-- client/src/Hooks/useBulkMonitors.js | 25 ----------------- client/src/Pages/Settings/index.jsx | 4 +-- client/src/Pages/Uptime/BulkImport/index.jsx | 4 +-- server/validation/joi.js | 7 ++++- 5 files changed, 36 insertions(+), 32 deletions(-) delete mode 100644 client/src/Hooks/useBulkMonitors.js diff --git a/client/src/Hooks/monitorHooks.js b/client/src/Hooks/monitorHooks.js index 8df0faa3b..d88132dae 100644 --- a/client/src/Hooks/monitorHooks.js +++ b/client/src/Hooks/monitorHooks.js @@ -411,7 +411,7 @@ const useDeleteAllMonitors = () => { return [deleteAllMonitors, isLoading]; }; -const UseDeleteMonitorStats = () => { +const useDeleteMonitorStats = () => { const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); const deleteMonitorStats = async () => { @@ -429,6 +429,29 @@ const UseDeleteMonitorStats = () => { return [deleteMonitorStats, isLoading]; }; +const useCreateBulkMonitors = () => { + const [isLoading, setIsLoading] = useState(false); + + const createBulkMonitors = async (file, user) => { + setIsLoading(true); + + const formData = new FormData(); + formData.append("csvFile", file); + + try { + const response = await networkService.createBulkMonitors(formData); + return [true, response.data, null]; // [success, data, error] + } catch (err) { + const errorMessage = err?.response?.data?.msg || err.message; + return [false, null, errorMessage]; + } finally { + setIsLoading(false); + } + }; + + return [createBulkMonitors, isLoading]; +}; + export { useFetchMonitorsWithSummary, useFetchMonitorsWithChecks, @@ -443,5 +466,6 @@ export { usePauseMonitor, useAddDemoMonitors, useDeleteAllMonitors, - UseDeleteMonitorStats, + useDeleteMonitorStats, + useCreateBulkMonitors, }; diff --git a/client/src/Hooks/useBulkMonitors.js b/client/src/Hooks/useBulkMonitors.js deleted file mode 100644 index ba9086dac..000000000 --- a/client/src/Hooks/useBulkMonitors.js +++ /dev/null @@ -1,25 +0,0 @@ -import { useState } from "react"; -import { networkService } from "../main"; - -export const useBulkMonitors = () => { - const [isLoading, setIsLoading] = useState(false); - - const createBulkMonitors = async (file, user) => { - setIsLoading(true); - - const formData = new FormData(); - formData.append("csvFile", file); - - try { - const response = await networkService.createBulkMonitors(formData); - return [true, response.data, null]; // [success, data, error] - } catch (err) { - const errorMessage = err?.response?.data?.msg || err.message; - return [false, null, errorMessage]; - } finally { - setIsLoading(false); - } - }; - - return [createBulkMonitors, isLoading]; -}; diff --git a/client/src/Pages/Settings/index.jsx b/client/src/Pages/Settings/index.jsx index 11e432e17..e7a50e321 100644 --- a/client/src/Pages/Settings/index.jsx +++ b/client/src/Pages/Settings/index.jsx @@ -23,7 +23,7 @@ import { useIsAdmin } from "../../Hooks/useIsAdmin"; import { useAddDemoMonitors, useDeleteAllMonitors, - UseDeleteMonitorStats, + useDeleteMonitorStats, } from "../../Hooks/monitorHooks"; // Constants const BREADCRUMBS = [{ name: `Settings`, path: "/settings" }]; @@ -45,7 +45,7 @@ const Settings = () => { const [isSaving, saveError, saveSettings] = useSaveSettings(); const [deleteAllMonitors, isDeletingMonitors] = useDeleteAllMonitors(); - const [deleteMonitorStats, isDeletingMonitorStats] = UseDeleteMonitorStats(); + const [deleteMonitorStats, isDeletingMonitorStats] = useDeleteMonitorStats(); // Setup const isAdmin = useIsAdmin(); diff --git a/client/src/Pages/Uptime/BulkImport/index.jsx b/client/src/Pages/Uptime/BulkImport/index.jsx index 99a6d51d8..e7fa75665 100644 --- a/client/src/Pages/Uptime/BulkImport/index.jsx +++ b/client/src/Pages/Uptime/BulkImport/index.jsx @@ -12,7 +12,7 @@ import UploadFile from "./Upload"; import { useSelector } from "react-redux"; import { useNavigate } from "react-router"; import { Trans, useTranslation } from "react-i18next"; -import { useBulkMonitors } from "../../../Hooks/useBulkMonitors"; +import { useCreateBulkMonitors } from "../../../Hooks/monitorHooks"; const BulkImport = () => { const theme = useTheme(); @@ -26,7 +26,7 @@ const BulkImport = () => { { name: t("bulkImport.title"), path: `/uptime/bulk-import` }, ]; - const [createBulkMonitors, hookLoading] = useBulkMonitors(); + const [createBulkMonitors, hookLoading] = useCreateBulkMonitors(); const handleSubmit = async () => { if (!selectedFile) { diff --git a/server/validation/joi.js b/server/validation/joi.js index 60f462280..622cb9148 100755 --- a/server/validation/joi.js +++ b/server/validation/joi.js @@ -186,7 +186,12 @@ const createMonitorBodyValidation = joi.object({ matchMethod: joi.string(), }); -const createMonitorsBodyValidation = joi.array().items(createMonitorBodyValidation); +const createMonitorsBodyValidation = joi.array().items( + createMonitorBodyValidation.keys({ + userId: joi.string().required(), + teamId: joi.string().required(), + }) +); const editMonitorBodyValidation = joi.object({ name: joi.string(), From 4b621a228f217f49c6e70db4efb15b41b40bdaf8 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 13 Jun 2025 14:54:59 +0800 Subject: [PATCH 36/36] remove monitors from status page on delete --- client/src/Components/Fallback/index.jsx | 2 +- .../TextLink/index.jsx} | 0 .../{useFetchSettings.js => settingsHooks.js} | 0 client/src/Pages/Auth/Login/index.jsx | 2 +- client/src/Pages/Settings/index.jsx | 2 +- client/src/Pages/StatusPage/Status/index.jsx | 22 +++++++++++++++++++ client/src/Pages/Uptime/Create/index.jsx | 3 +-- server/db/models/Monitor.js | 4 ++++ server/db/mongo/modules/monitorModule.js | 2 ++ server/db/mongo/modules/statusPageModule.js | 19 ++++++++++++---- 10 files changed, 47 insertions(+), 9 deletions(-) rename client/src/{Pages/Auth/components/TextLink.jsx => Components/TextLink/index.jsx} (100%) rename client/src/Hooks/{useFetchSettings.js => settingsHooks.js} (100%) diff --git a/client/src/Components/Fallback/index.jsx b/client/src/Components/Fallback/index.jsx index b00ddbecc..47497ba02 100644 --- a/client/src/Components/Fallback/index.jsx +++ b/client/src/Components/Fallback/index.jsx @@ -11,7 +11,7 @@ import { useSelector } from "react-redux"; import Alert from "../Alert"; import { useTranslation } from "react-i18next"; import "./index.css"; -import { useFetchSettings } from "../../Hooks/useFetchSettings"; +import { useFetchSettings } from "../../Hooks/settingsHooks"; import { useState } from "react"; /** * Fallback component to display a fallback UI with a title, a list of checks, and a navigation button. diff --git a/client/src/Pages/Auth/components/TextLink.jsx b/client/src/Components/TextLink/index.jsx similarity index 100% rename from client/src/Pages/Auth/components/TextLink.jsx rename to client/src/Components/TextLink/index.jsx diff --git a/client/src/Hooks/useFetchSettings.js b/client/src/Hooks/settingsHooks.js similarity index 100% rename from client/src/Hooks/useFetchSettings.js rename to client/src/Hooks/settingsHooks.js diff --git a/client/src/Pages/Auth/Login/index.jsx b/client/src/Pages/Auth/Login/index.jsx index a969706e7..098fbbf08 100644 --- a/client/src/Pages/Auth/Login/index.jsx +++ b/client/src/Pages/Auth/Login/index.jsx @@ -5,7 +5,7 @@ import Button from "@mui/material/Button"; import TextInput from "../../../Components/Inputs/TextInput"; import { PasswordEndAdornment } from "../../../Components/Inputs/TextInput/Adornments"; import { loginCredentials } from "../../../Validation/validation"; -import TextLink from "../components/TextLink"; +import TextLink from "../../../Components/TextLink"; import Typography from "@mui/material/Typography"; // Utils diff --git a/client/src/Pages/Settings/index.jsx b/client/src/Pages/Settings/index.jsx index e7a50e321..a3cd88fca 100644 --- a/client/src/Pages/Settings/index.jsx +++ b/client/src/Pages/Settings/index.jsx @@ -18,7 +18,7 @@ import { useSelector, useDispatch } from "react-redux"; import { setTimezone, setMode, setLanguage, setShowURL } from "../../Features/UI/uiSlice"; import SettingsStats from "./SettingsStats"; -import { useFetchSettings, useSaveSettings } from "../../Hooks/useFetchSettings"; +import { useFetchSettings, useSaveSettings } from "../../Hooks/settingsHooks"; import { useIsAdmin } from "../../Hooks/useIsAdmin"; import { useAddDemoMonitors, diff --git a/client/src/Pages/StatusPage/Status/index.jsx b/client/src/Pages/StatusPage/Status/index.jsx index ee1ec436c..727daeeb1 100644 --- a/client/src/Pages/StatusPage/Status/index.jsx +++ b/client/src/Pages/StatusPage/Status/index.jsx @@ -8,6 +8,7 @@ import StatusBar from "./Components/StatusBar"; import MonitorsList from "./Components/MonitorsList"; import Dialog from "../../../Components/Dialog"; import Breadcrumbs from "../../../Components/Breadcrumbs/index.jsx"; +import TextLink from "../../../Components/TextLink"; // Utils import { useStatusPageFetch } from "./Hooks/useStatusPageFetch"; @@ -30,6 +31,7 @@ const PublicStatus = () => { const { t } = useTranslation(); const location = useLocation(); const navigate = useNavigate(); + const isAdmin = useIsAdmin(); const [statusPage, monitors, isLoading, networkError, fetchStatusPage] = useStatusPageFetch(false, url); @@ -60,6 +62,26 @@ const PublicStatus = () => { return ; } + if (monitors.length === 0) { + return ( + + + {"Theres nothing here yet"} + + {isAdmin && ( + + )} + + ); + } + // Error fetching data if (networkError === true) { return ( diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx index e2d51153c..6527334ab 100644 --- a/client/src/Pages/Uptime/Create/index.jsx +++ b/client/src/Pages/Uptime/Create/index.jsx @@ -142,8 +142,7 @@ const CreateMonitor = () => { notifications: monitor.notifications, }; - console.log(JSON.stringify(form, null, 2)); - // await createMonitor({ monitor: form, redirect: "/uptime" }); + await createMonitor({ monitor: form, redirect: "/uptime" }); }; const onChange = (event) => { diff --git a/server/db/models/Monitor.js b/server/db/models/Monitor.js index 26a79b815..7bb38f520 100755 --- a/server/db/models/Monitor.js +++ b/server/db/models/Monitor.js @@ -3,6 +3,7 @@ import HardwareCheck from "./HardwareCheck.js"; import PageSpeedCheck from "./PageSpeedCheck.js"; import Check from "./Check.js"; import MonitorStats from "./MonitorStats.js"; +import StatusPage from "./StatusPage.js"; const MonitorSchema = mongoose.Schema( { @@ -105,6 +106,9 @@ MonitorSchema.pre("findOneAndDelete", async function (next) { await Check.deleteMany({ monitorId: doc._id }); } + // Deal with status pages + await StatusPage.updateMany({ monitors: doc._id }, { $pull: { monitors: doc._id } }); + await MonitorStats.deleteMany({ monitorId: doc._id.toString() }); next(); } catch (error) { diff --git a/server/db/mongo/modules/monitorModule.js b/server/db/mongo/modules/monitorModule.js index 6c03dcd4c..32ed8b146 100755 --- a/server/db/mongo/modules/monitorModule.js +++ b/server/db/mongo/modules/monitorModule.js @@ -710,9 +710,11 @@ const deleteMonitor = async ({ monitorId }) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { const monitor = await Monitor.findByIdAndDelete(monitorId); + if (!monitor) { throw new Error(stringService.getDbFindMonitorById(monitorId)); } + return monitor; } catch (error) { error.service = SERVICE_NAME; diff --git a/server/db/mongo/modules/statusPageModule.js b/server/db/mongo/modules/statusPageModule.js index b2f7ef7d6..d6331fcf8 100755 --- a/server/db/mongo/modules/statusPageModule.js +++ b/server/db/mongo/modules/statusPageModule.js @@ -106,7 +106,10 @@ const getStatusPage = async (url) => { }, }, { - $unwind: "$monitors", + $unwind: { + path: "$monitors", + preserveNullAndEmptyArrays: true, + }, }, { $lookup: { @@ -153,9 +156,17 @@ const getStatusPage = async (url) => { url: 1, }, monitors: { - $sortArray: { - input: "$monitors", - sortBy: { orderIndex: 1 }, + $filter: { + input: { + $sortArray: { + input: "$monitors", + sortBy: { orderIndex: 1 }, + }, + }, + as: "monitor", + cond: { + $ne: ["$$monitor.orderIndex", -1], + }, }, }, },