mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-05-19 07:58:46 -05:00
Merge pull request #1533 from bluewave-labs/feat/be/unified-uptime-query
feat: be/unified uptime query
This commit is contained in:
@@ -40,7 +40,7 @@ export const checkEndpointResolution = createAsyncThunk(
|
||||
const res = await networkService.checkEndpointResolution({
|
||||
authToken: authToken,
|
||||
monitorURL: monitorURL,
|
||||
})
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
@@ -53,7 +53,7 @@ export const checkEndpointResolution = createAsyncThunk(
|
||||
return thunkApi.rejectWithValue(payload);
|
||||
}
|
||||
}
|
||||
)
|
||||
);
|
||||
|
||||
export const getPagespeedMonitorById = createAsyncThunk(
|
||||
"monitors/getMonitorById",
|
||||
@@ -88,7 +88,6 @@ export const getPageSpeedByTeamId = createAsyncThunk(
|
||||
teamId: user.teamId,
|
||||
types: ["pagespeed"],
|
||||
});
|
||||
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
|
||||
@@ -78,62 +78,6 @@ export const getUptimeMonitorById = createAsyncThunk(
|
||||
}
|
||||
);
|
||||
|
||||
export const getUptimeSummaryByTeamId = createAsyncThunk(
|
||||
"monitors/getSummaryByTeamId",
|
||||
async (token, thunkApi) => {
|
||||
const user = jwtDecode(token);
|
||||
try {
|
||||
const res = await networkService.getMonitorsSummaryByTeamId({
|
||||
authToken: token,
|
||||
teamId: user.teamId,
|
||||
types: ["http", "ping", "docker", "port"],
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
return thunkApi.rejectWithValue(error.response.data);
|
||||
}
|
||||
const payload = {
|
||||
status: false,
|
||||
msg: error.message ? error.message : "Unknown error",
|
||||
};
|
||||
return thunkApi.rejectWithValue(payload);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const getUptimeMonitorsByTeamId = createAsyncThunk(
|
||||
"monitors/getMonitorsByTeamId",
|
||||
async (config, thunkApi) => {
|
||||
try {
|
||||
const res = await networkService.getMonitorsByTeamId({
|
||||
authToken: config.authToken,
|
||||
teamId: config.teamId,
|
||||
limit: 25,
|
||||
types: ["http", "ping", "docker", "port"],
|
||||
status: null,
|
||||
checkOrder: "desc",
|
||||
normalize: true,
|
||||
page: config.page,
|
||||
rowsPerPage: config.rowsPerPage,
|
||||
filter: config.filter,
|
||||
field: config.sort.field,
|
||||
order: config.sort.order,
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
if (error.response && error.response.data) {
|
||||
return thunkApi.rejectWithValue(error.response.data);
|
||||
}
|
||||
const payload = {
|
||||
status: false,
|
||||
msg: error.message ? error.message : "Unknown error",
|
||||
};
|
||||
return thunkApi.rejectWithValue(payload);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const updateUptimeMonitor = createAsyncThunk(
|
||||
"monitors/updateMonitor",
|
||||
async (data, thunkApi) => {
|
||||
@@ -289,42 +233,6 @@ const uptimeMonitorsSlice = createSlice({
|
||||
},
|
||||
extraReducers: (builder) => {
|
||||
builder
|
||||
// *****************************************************
|
||||
// Summary by teamId
|
||||
// *****************************************************
|
||||
|
||||
.addCase(getUptimeSummaryByTeamId.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
})
|
||||
.addCase(getUptimeSummaryByTeamId.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = action.payload.msg;
|
||||
state.monitorsSummary = action.payload.data;
|
||||
})
|
||||
.addCase(getUptimeSummaryByTeamId.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = false;
|
||||
state.msg = action.payload ? action.payload.msg : "Getting uptime summary failed";
|
||||
})
|
||||
|
||||
// *****************************************************
|
||||
// Monitors by teamId
|
||||
// *****************************************************
|
||||
.addCase(getUptimeMonitorsByTeamId.pending, (state) => {
|
||||
state.isLoading = true;
|
||||
})
|
||||
.addCase(getUptimeMonitorsByTeamId.fulfilled, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = action.payload.success;
|
||||
state.msg = action.payload.msg;
|
||||
})
|
||||
.addCase(getUptimeMonitorsByTeamId.rejected, (state, action) => {
|
||||
state.isLoading = false;
|
||||
state.success = false;
|
||||
state.msg = action.payload
|
||||
? action.payload.msg
|
||||
: "Getting uptime monitors failed";
|
||||
})
|
||||
// *****************************************************
|
||||
// Create Monitor
|
||||
// *****************************************************
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useState, useCallback } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { /* useDispatch, */ useSelector } from "react-redux";
|
||||
import { useTheme } from "@emotion/react";
|
||||
@@ -50,12 +50,13 @@ function Infrastructure() {
|
||||
setRowsPerPage(parseInt(event.target.value));
|
||||
setPage(0);
|
||||
};
|
||||
const [monitorState, setMonitorState] = useState({ monitors: [], total: 0 });
|
||||
const [monitors, setMonitors] = useState([]);
|
||||
const [summary, setSummary] = useState({});
|
||||
|
||||
const { authToken } = useSelector((state) => state.auth);
|
||||
const user = jwtDecode(authToken);
|
||||
|
||||
const fetchMonitors = async () => {
|
||||
const fetchMonitors = useCallback(async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const response = await networkService.getMonitorsByTeamId({
|
||||
@@ -63,29 +64,23 @@ function Infrastructure() {
|
||||
teamId: user.teamId,
|
||||
limit: 1,
|
||||
types: ["hardware"],
|
||||
status: null,
|
||||
checkOrder: "desc",
|
||||
normalize: true,
|
||||
page: page,
|
||||
rowsPerPage: rowsPerPage,
|
||||
});
|
||||
setMonitorState({
|
||||
monitors: response?.data?.data?.monitors ?? [],
|
||||
total: response?.data?.data?.monitorCount ?? 0,
|
||||
});
|
||||
setMonitors(response?.data?.data?.monitors ?? []);
|
||||
setSummary(response?.data?.data?.summary ?? {});
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
}, [page, rowsPerPage, authToken, user.teamId]);
|
||||
|
||||
useEffect(() => {
|
||||
fetchMonitors();
|
||||
}, [page, rowsPerPage]);
|
||||
}, [fetchMonitors]);
|
||||
|
||||
const { determineState } = useUtils();
|
||||
const { monitors, total: totalMonitors } = monitorState;
|
||||
// do it here
|
||||
function openDetails(id) {
|
||||
navigate(`/infrastructure/${id}`);
|
||||
@@ -191,7 +186,7 @@ function Infrastructure() {
|
||||
};
|
||||
});
|
||||
|
||||
let isActuallyLoading = isLoading && monitorState.monitors?.length === 0;
|
||||
let isActuallyLoading = isLoading && monitors?.length === 0;
|
||||
return (
|
||||
<Box
|
||||
className="infrastructure-monitor"
|
||||
@@ -209,7 +204,7 @@ function Infrastructure() {
|
||||
>
|
||||
{isActuallyLoading ? (
|
||||
<SkeletonLayout />
|
||||
) : monitorState.monitors?.length !== 0 ? (
|
||||
) : monitors?.length !== 0 ? (
|
||||
<Stack gap={theme.spacing(8)}>
|
||||
<Box>
|
||||
<Breadcrumbs list={BREADCRUMBS} />
|
||||
@@ -254,7 +249,7 @@ function Infrastructure() {
|
||||
borderColor={theme.palette.border.light}
|
||||
backgroundColor={theme.palette.background.accent}
|
||||
>
|
||||
{totalMonitors}
|
||||
{summary?.totalMonitors ?? 0}
|
||||
</Box>
|
||||
</Stack>
|
||||
|
||||
@@ -272,7 +267,7 @@ function Infrastructure() {
|
||||
data={monitorsAsRows}
|
||||
/>
|
||||
<Pagination
|
||||
monitorCount={totalMonitors}
|
||||
monitorCount={summary?.totalMonitors ?? 0}
|
||||
page={page}
|
||||
rowsPerPage={rowsPerPage}
|
||||
handleChangePage={handleChangePage}
|
||||
|
||||
@@ -267,7 +267,7 @@ const Card = ({ monitor }) => {
|
||||
sx={{ gridColumnStart: 1, gridColumnEnd: 4 }}
|
||||
>
|
||||
<PagespeedAreaChart
|
||||
data={monitor.checks}
|
||||
data={monitor.checks.slice().reverse()}
|
||||
status={monitorState}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { Box, Button, Grid, Stack } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
import { getPageSpeedByTeamId } from "../../Features/PageSpeedMonitor/pageSpeedMonitorSlice";
|
||||
import { useSelector } from "react-redux";
|
||||
import Fallback from "../../Components/Fallback";
|
||||
import "./index.css";
|
||||
import { useNavigate } from "react-router";
|
||||
@@ -15,16 +14,12 @@ import { Heading } from "../../Components/Heading";
|
||||
import { useIsAdmin } from "../../Hooks/useIsAdmin";
|
||||
const PageSpeed = () => {
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const isAdmin = useIsAdmin();
|
||||
const { user, authToken } = useSelector((state) => state.auth);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [monitors, setMonitors] = useState([]);
|
||||
const [monitorCount, setMonitorCount] = useState(0);
|
||||
useEffect(() => {
|
||||
dispatch(getPageSpeedByTeamId(authToken));
|
||||
}, [authToken, dispatch]);
|
||||
const [summary, setSummary] = useState({});
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMonitors = async () => {
|
||||
@@ -35,9 +30,6 @@ const PageSpeed = () => {
|
||||
teamId: user.teamId,
|
||||
limit: 10,
|
||||
types: ["pagespeed"],
|
||||
status: null,
|
||||
checkOrder: "desc",
|
||||
normalize: true,
|
||||
page: null,
|
||||
rowsPerPage: null,
|
||||
filter: null,
|
||||
@@ -46,7 +38,7 @@ const PageSpeed = () => {
|
||||
});
|
||||
if (res?.data?.data?.monitors) {
|
||||
setMonitors(res.data.data.monitors);
|
||||
setMonitorCount(res.data.data.monitorCount);
|
||||
setSummary(res.data.data.summary);
|
||||
}
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
@@ -118,7 +110,7 @@ const PageSpeed = () => {
|
||||
borderColor={theme.palette.border.light}
|
||||
backgroundColor={theme.palette.background.accent}
|
||||
>
|
||||
{monitorCount}
|
||||
{summary?.totalMonitors ?? 0}
|
||||
</Box>
|
||||
</Stack>
|
||||
<Grid
|
||||
|
||||
@@ -12,13 +12,42 @@ import ActionsMenu from "../actionsMenu";
|
||||
|
||||
// Utils
|
||||
import { useTheme } from "@emotion/react";
|
||||
import useDebounce from "../../../../Utils/debounce";
|
||||
import { useState } from "react";
|
||||
import useUtils from "../../utils";
|
||||
import { memo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import "../index.css";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
/**
|
||||
* UptimeDataTable displays a table of uptime monitors with sorting, searching, and action capabilities
|
||||
* @param {Object} props - Component props
|
||||
* @param {boolean} props.isAdmin - Whether the current user has admin privileges
|
||||
* @param {boolean} props.isLoading - Loading state of the table
|
||||
* @param {Array<{
|
||||
* _id: string,
|
||||
* url: string,
|
||||
* title: string,
|
||||
* percentage: number,
|
||||
* percentageColor: string,
|
||||
* monitor: {
|
||||
* _id: string,
|
||||
* type: string,
|
||||
* checks: Array
|
||||
* }
|
||||
* }>} props.monitors - Array of monitor objects to display
|
||||
* @param {number} props.monitorCount - Total count of monitors
|
||||
* @param {Object} props.sort - Current sort configuration
|
||||
* @param {string} props.sort.field - Field to sort by
|
||||
* @param {'asc'|'desc'} props.sort.order - Sort direction
|
||||
* @param {Function} props.setSort - Callback to update sort configuration
|
||||
* @param {string} props.search - Current search query
|
||||
* @param {Function} props.setSearch - Callback to update search query
|
||||
* @param {boolean} props.isSearching - Whether a search is in progress
|
||||
* @param {Function} props.setIsSearching - Callback to update search state
|
||||
* @param {Function} props.setIsLoading - Callback to update loading state
|
||||
* @param {Function} props.triggerUpdate - Callback to trigger a data refresh
|
||||
* @returns {JSX.Element} Rendered component
|
||||
*/
|
||||
const UptimeDataTable = ({
|
||||
isAdmin,
|
||||
isLoading,
|
||||
@@ -30,6 +59,7 @@ const UptimeDataTable = ({
|
||||
setSearch,
|
||||
isSearching,
|
||||
setIsSearching,
|
||||
setIsLoading,
|
||||
triggerUpdate,
|
||||
}) => {
|
||||
const { determineState } = useUtils();
|
||||
@@ -37,8 +67,6 @@ const UptimeDataTable = ({
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
|
||||
//Utils
|
||||
const debouncedFilter = useDebounce(search, 500);
|
||||
const handleSearch = (value) => {
|
||||
setIsSearching(true);
|
||||
setSearch(value);
|
||||
@@ -146,6 +174,7 @@ const UptimeDataTable = ({
|
||||
monitor={row.monitor}
|
||||
isAdmin={isAdmin}
|
||||
updateRowCallback={triggerUpdate}
|
||||
setIsLoading={setIsLoading}
|
||||
pauseCallback={triggerUpdate}
|
||||
/>
|
||||
),
|
||||
@@ -187,7 +216,7 @@ const UptimeDataTable = ({
|
||||
</Box>
|
||||
</Stack>
|
||||
<Box position="relative">
|
||||
{isSearching && (
|
||||
{(isSearching || isLoading) && (
|
||||
<>
|
||||
<Box
|
||||
width="100%"
|
||||
@@ -240,3 +269,21 @@ const UptimeDataTable = ({
|
||||
|
||||
const MemoizedUptimeDataTable = memo(UptimeDataTable);
|
||||
export default MemoizedUptimeDataTable;
|
||||
|
||||
UptimeDataTable.propTypes = {
|
||||
isAdmin: PropTypes.bool,
|
||||
isLoading: PropTypes.bool,
|
||||
monitors: PropTypes.array,
|
||||
monitorCount: PropTypes.number,
|
||||
sort: PropTypes.shape({
|
||||
field: PropTypes.string,
|
||||
order: PropTypes.oneOf(["asc", "desc"]),
|
||||
}),
|
||||
setSort: PropTypes.func,
|
||||
search: PropTypes.string,
|
||||
setSearch: PropTypes.func,
|
||||
isSearching: PropTypes.bool,
|
||||
setIsSearching: PropTypes.func,
|
||||
setIsLoading: PropTypes.func,
|
||||
triggerUpdate: PropTypes.func,
|
||||
};
|
||||
|
||||
@@ -8,13 +8,18 @@ import { IconButton, Menu, MenuItem } from "@mui/material";
|
||||
import {
|
||||
deleteUptimeMonitor,
|
||||
pauseUptimeMonitor,
|
||||
getUptimeMonitorsByTeamId,
|
||||
} from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import Settings from "../../../assets/icons/settings-bold.svg?react";
|
||||
import PropTypes from "prop-types";
|
||||
import Dialog from "../../../Components/Dialog";
|
||||
|
||||
const ActionsMenu = ({ monitor, isAdmin, updateRowCallback, pauseCallback }) => {
|
||||
const ActionsMenu = ({
|
||||
monitor,
|
||||
isAdmin,
|
||||
updateRowCallback,
|
||||
pauseCallback,
|
||||
setIsLoading,
|
||||
}) => {
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [actions, setActions] = useState({});
|
||||
const [isOpen, setIsOpen] = useState(false);
|
||||
@@ -33,7 +38,6 @@ const ActionsMenu = ({ monitor, isAdmin, updateRowCallback, pauseCallback }) =>
|
||||
);
|
||||
if (action.meta.requestStatus === "fulfilled") {
|
||||
setIsOpen(false); // close modal
|
||||
dispatch(getUptimeMonitorsByTeamId(authState.authToken));
|
||||
updateRowCallback();
|
||||
createToast({ body: "Monitor deleted successfully." });
|
||||
} else {
|
||||
@@ -43,6 +47,7 @@ const ActionsMenu = ({ monitor, isAdmin, updateRowCallback, pauseCallback }) =>
|
||||
|
||||
const handlePause = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
const action = await dispatch(
|
||||
pauseUptimeMonitor({ authToken, monitorId: monitor._id })
|
||||
);
|
||||
@@ -223,6 +228,7 @@ ActionsMenu.propTypes = {
|
||||
isAdmin: PropTypes.bool,
|
||||
updateRowCallback: PropTypes.func,
|
||||
pauseCallback: PropTypes.func,
|
||||
setIsLoading: PropTypes.func,
|
||||
};
|
||||
|
||||
export default ActionsMenu;
|
||||
|
||||
@@ -9,11 +9,7 @@ import { Pagination } from "../../../Components/Table/TablePagination";
|
||||
|
||||
// Utils
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useEffect, useState, useCallback, useMemo, useRef } from "react";
|
||||
import {
|
||||
getUptimeSummaryByTeamId,
|
||||
getUptimeMonitorsByTeamId,
|
||||
} from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import { useEffect, useState, useCallback, useMemo } from "react";
|
||||
import { setRowsPerPage } from "../../../Features/UI/uiSlice";
|
||||
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
@@ -21,6 +17,7 @@ import { useNavigate } from "react-router-dom";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import useDebounce from "../../../Utils/debounce";
|
||||
import { networkService } from "../../../main";
|
||||
|
||||
const BREADCRUMBS = [{ name: `Uptime`, path: "/uptime" }];
|
||||
|
||||
@@ -33,7 +30,9 @@ const UptimeMonitors = () => {
|
||||
const [search, setSearch] = useState("");
|
||||
const [page, setPage] = useState(0);
|
||||
const [isSearching, setIsSearching] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [monitorUpdateTrigger, setMonitorUpdateTrigger] = useState(false);
|
||||
const [monitorsSummary, setMonitorsSummary] = useState({});
|
||||
|
||||
// Utils
|
||||
const debouncedFilter = useDebounce(search, 500);
|
||||
@@ -41,7 +40,6 @@ const UptimeMonitors = () => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const isAdmin = useIsAdmin();
|
||||
const { isLoading, monitorsSummary } = useSelector((state) => state.uptimeMonitors);
|
||||
const authState = useSelector((state) => state.auth);
|
||||
|
||||
const fetchParams = useMemo(
|
||||
@@ -89,30 +87,38 @@ const UptimeMonitors = () => {
|
||||
|
||||
const fetchMonitors = useCallback(async () => {
|
||||
try {
|
||||
const action = await dispatch(getUptimeMonitorsByTeamId(fetchParams));
|
||||
if (action.payload.success) {
|
||||
const { monitors } = action.payload.data;
|
||||
const mappedMonitors = monitors.map((monitor) =>
|
||||
getMonitorWithPercentage(monitor, theme)
|
||||
);
|
||||
setMonitors(mappedMonitors);
|
||||
} else {
|
||||
// TODO: Check for other errors?
|
||||
throw new Error("Error fetching monitors");
|
||||
}
|
||||
setIsLoading(true);
|
||||
const config = fetchParams;
|
||||
const res = await networkService.getMonitorsByTeamId({
|
||||
authToken: config.authToken,
|
||||
teamId: config.teamId,
|
||||
limit: 25,
|
||||
types: ["http", "ping", "docker", "port"],
|
||||
page: config.page,
|
||||
rowsPerPage: config.rowsPerPage,
|
||||
filter: config.filter,
|
||||
field: config.sort.field,
|
||||
order: config.sort.order,
|
||||
});
|
||||
const { monitors, summary } = res.data.data;
|
||||
const mappedMonitors = monitors.map((monitor) =>
|
||||
getMonitorWithPercentage(monitor, theme)
|
||||
);
|
||||
setMonitors(mappedMonitors);
|
||||
setMonitorsSummary(summary);
|
||||
} catch (error) {
|
||||
createToast({
|
||||
body: "Error fetching monitors",
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
setIsSearching(false);
|
||||
}
|
||||
}, [fetchParams, dispatch, getMonitorWithPercentage, theme]);
|
||||
}, [fetchParams, getMonitorWithPercentage, theme]);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(getUptimeSummaryByTeamId(authState.authToken));
|
||||
fetchMonitors();
|
||||
}, [fetchMonitors, monitorUpdateTrigger, authState.authToken, dispatch]);
|
||||
}, [fetchMonitors, monitorUpdateTrigger]);
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
setPage(newPage);
|
||||
@@ -131,7 +137,7 @@ const UptimeMonitors = () => {
|
||||
const triggerUpdate = () => {
|
||||
setMonitorUpdateTrigger((prev) => !prev);
|
||||
};
|
||||
const totalMonitors = monitorsSummary?.monitorCounts?.total;
|
||||
const totalMonitors = monitorsSummary.totalMonitors;
|
||||
const hasMonitors = totalMonitors > 0;
|
||||
const canAddMonitor = isAdmin && hasMonitors;
|
||||
|
||||
@@ -176,19 +182,20 @@ const UptimeMonitors = () => {
|
||||
>
|
||||
<StatusBox
|
||||
title="up"
|
||||
value={monitorsSummary?.monitorCounts?.up ?? 0}
|
||||
value={monitorsSummary?.upMonitors ?? 0}
|
||||
/>
|
||||
<StatusBox
|
||||
title="down"
|
||||
value={monitorsSummary?.monitorCounts?.down ?? 0}
|
||||
value={monitorsSummary?.downMonitors ?? 0}
|
||||
/>
|
||||
<StatusBox
|
||||
title="paused"
|
||||
value={monitorsSummary?.monitorCounts?.paused ?? 0}
|
||||
value={monitorsSummary?.pausedMonitors ?? 0}
|
||||
/>
|
||||
</Stack>
|
||||
<UptimeDataTable
|
||||
isAdmin={isAdmin}
|
||||
isLoading={isLoading}
|
||||
monitors={monitors}
|
||||
monitorCount={totalMonitors}
|
||||
sort={sort}
|
||||
@@ -197,6 +204,7 @@ const UptimeMonitors = () => {
|
||||
setSearch={setSearch}
|
||||
isSearching={isSearching}
|
||||
setIsSearching={setIsSearching}
|
||||
setIsLoading={setIsLoading}
|
||||
triggerUpdate={triggerUpdate}
|
||||
/>
|
||||
<Pagination
|
||||
|
||||
@@ -157,9 +157,6 @@ class NetworkService {
|
||||
* @param {string} config.teamId - The ID of the team whose monitors are to be retrieved.
|
||||
* @param {number} [config.limit] - The maximum number of checks to retrieve. 0 for all, -1 for none
|
||||
* @param {Array<string>} [config.types] - The types of monitors to retrieve.
|
||||
* @param {string} [config.status] - The status of the monitors to retrieve.
|
||||
* @param {string} [config.checkOrder] - The order in which to sort the retrieved monitors.
|
||||
* @param {boolean} [config.normalize] - Whether to normalize the retrieved monitors.
|
||||
* @param {number} [config.page] - The page number for pagination.
|
||||
* @param {number} [config.rowsPerPage] - The number of rows per page for pagination.
|
||||
* @param {string} [config.filter] - The filter to apply to the monitors.
|
||||
@@ -167,21 +164,10 @@ class NetworkService {
|
||||
* @param {string} [config.order] - The order in which to sort the field.
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
|
||||
*/
|
||||
|
||||
async getMonitorsByTeamId(config) {
|
||||
const {
|
||||
authToken,
|
||||
teamId,
|
||||
limit,
|
||||
types,
|
||||
status,
|
||||
checkOrder,
|
||||
normalize,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
} = config;
|
||||
const { authToken, teamId, limit, types, page, rowsPerPage, filter, field, order } =
|
||||
config;
|
||||
|
||||
const params = new URLSearchParams();
|
||||
|
||||
@@ -191,9 +177,6 @@ class NetworkService {
|
||||
params.append("type", type);
|
||||
});
|
||||
}
|
||||
if (status) params.append("status", status);
|
||||
if (checkOrder) params.append("checkOrder", checkOrder);
|
||||
if (normalize) params.append("normalize", normalize);
|
||||
if (page) params.append("page", page);
|
||||
if (rowsPerPage) params.append("rowsPerPage", rowsPerPage);
|
||||
if (filter) params.append("filter", filter);
|
||||
|
||||
@@ -1,13 +1,11 @@
|
||||
import {
|
||||
getMonitorByIdParamValidation,
|
||||
getMonitorByIdQueryValidation,
|
||||
getMonitorsByTeamIdValidation,
|
||||
getMonitorsByTeamIdParamValidation,
|
||||
getMonitorsByTeamIdQueryValidation,
|
||||
createMonitorBodyValidation,
|
||||
getMonitorURLByQueryValidation,
|
||||
editMonitorBodyValidation,
|
||||
getMonitorsSummaryByTeamIdParamValidation,
|
||||
getMonitorsSummaryByTeamIdQueryValidation,
|
||||
getMonitorsByTeamIdQueryValidation,
|
||||
pauseMonitorParamValidation,
|
||||
getMonitorStatsByIdParamValidation,
|
||||
getMonitorStatsByIdQueryValidation,
|
||||
@@ -204,77 +202,6 @@ class MonitorController {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves all monitors and a summary for a team based on the team ID.
|
||||
* @async
|
||||
* @param {Object} req - The Express request object.
|
||||
* @property {Object} req.params - The parameters of the request.
|
||||
* @property {string} req.params.teamId - The ID of the team.
|
||||
* @property {Object} req.query - The query parameters of the request.
|
||||
* @property {string} req.query.type - The type of the request.
|
||||
* @param {Object} res - The Express response object.
|
||||
* @param {function} next - The next middleware function.
|
||||
* @returns {Object} The response object with a success status, a message, and the data containing the monitors and summary for the team.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
getMonitorsSummaryByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
await getMonitorsSummaryByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsSummaryByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { teamId } = req.params;
|
||||
const { type } = req.query;
|
||||
const monitorsSummary = await this.db.getMonitorsSummaryByTeamId(teamId, type);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.MONITOR_GET_BY_USER_ID(teamId),
|
||||
data: monitorsSummary,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorsAndSummaryByTeamId"));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Retrieves all monitors associated with a team by the team's ID.
|
||||
* @async
|
||||
* @param {Object} req - The Express request object.
|
||||
* @property {Object} req.params - The parameters of the request.
|
||||
* @property {string} req.params.teamId - The ID of the team.
|
||||
* @property {Object} req.query - The query parameters of the request.
|
||||
* @param {Object} res - The Express response object.
|
||||
* @param {function} next - The next middleware function.
|
||||
* @returns {Object} The response object with a success status, a message, and the data containing the monitors for the team.
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422).
|
||||
*/
|
||||
getMonitorsByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
await getMonitorsByTeamIdValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const teamId = req.params.teamId;
|
||||
const monitors = await this.db.getMonitorsByTeamId(req, res);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: successMessages.MONITOR_GET_BY_USER_ID(teamId),
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorsByTeamId"));
|
||||
next(error);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a new monitor and adds it to the job queue.
|
||||
* @async
|
||||
@@ -564,6 +491,26 @@ class MonitorController {
|
||||
next(handleError(error, SERVICE_NAME, "addDemoMonitors"));
|
||||
}
|
||||
};
|
||||
|
||||
getMonitorsByTeamId = async (req, res, next) => {
|
||||
try {
|
||||
await getMonitorsByTeamIdParamValidation.validateAsync(req.params);
|
||||
await getMonitorsByTeamIdQueryValidation.validateAsync(req.query);
|
||||
} catch (error) {
|
||||
next(handleValidationError(error, SERVICE_NAME));
|
||||
}
|
||||
|
||||
try {
|
||||
const monitors = await this.db.getMonitorsByTeamId(req);
|
||||
return res.status(200).json({
|
||||
success: true,
|
||||
msg: "good",
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "getMonitorsForDisplay"));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export default MonitorController;
|
||||
|
||||
@@ -505,202 +505,162 @@ const getMonitorById = async (monitorId) => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get monitors and Summary by TeamID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<Array<Monitor>>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getMonitorsByTeamId = async (req) => {
|
||||
let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
|
||||
|
||||
const getMonitorsSummaryByTeamId = async (teamId, type) => {
|
||||
try {
|
||||
const monitorCounts = await Monitor.aggregate([
|
||||
{
|
||||
$match: {
|
||||
type: { $in: type },
|
||||
},
|
||||
},
|
||||
{
|
||||
$facet: {
|
||||
total: [{ $count: "count" }],
|
||||
up: [{ $match: { status: true } }, { $count: "count" }],
|
||||
down: [{ $match: { status: false } }, { $count: "count" }],
|
||||
paused: [{ $match: { isActive: false } }, { $count: "count" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
total: { $arrayElemAt: ["$total.count", 0] },
|
||||
up: { $arrayElemAt: ["$up.count", 0] },
|
||||
down: { $arrayElemAt: ["$down.count", 0] },
|
||||
paused: { $arrayElemAt: ["$paused.count", 0] },
|
||||
},
|
||||
},
|
||||
]);
|
||||
return { monitorCounts: monitorCounts[0] };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMonitorsAndSummaryByTeamId";
|
||||
throw error;
|
||||
// Parse ints
|
||||
limit = parseInt(limit);
|
||||
page = parseInt(page);
|
||||
rowsPerPage = parseInt(rowsPerPage);
|
||||
|
||||
// Build the match stage
|
||||
const matchStage = { teamId: ObjectId.createFromHexString(req.params.teamId) };
|
||||
if (type !== undefined) {
|
||||
matchStage.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get monitors by TeamID
|
||||
* @async
|
||||
* @param {Express.Request} req
|
||||
* @param {Express.Response} res
|
||||
* @returns {Promise<Array<Monitor>>}
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getMonitorsByTeamId = async (req, res) => {
|
||||
try {
|
||||
let {
|
||||
limit,
|
||||
type,
|
||||
status,
|
||||
checkOrder,
|
||||
normalize,
|
||||
page,
|
||||
rowsPerPage,
|
||||
filter,
|
||||
field,
|
||||
order,
|
||||
} = req.query;
|
||||
const skip = page && rowsPerPage ? page * rowsPerPage : 0;
|
||||
|
||||
const monitorQuery = { teamId: req.params.teamId };
|
||||
const monitorCount = await Monitor.countDocuments(monitorQuery);
|
||||
const sort = { [field]: order === "asc" ? 1 : -1 };
|
||||
|
||||
if (type !== undefined) {
|
||||
monitorQuery.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
// Add filter if provided
|
||||
// $options: "i" makes the search case-insensitive
|
||||
if (filter !== undefined) {
|
||||
monitorQuery.$or = [
|
||||
{ name: { $regex: filter, $options: "i" } },
|
||||
{ url: { $regex: filter, $options: "i" } },
|
||||
];
|
||||
}
|
||||
if (filter !== undefined) {
|
||||
matchStage.$or = [
|
||||
{ name: { $regex: filter, $options: "i" } },
|
||||
{ url: { $regex: filter, $options: "i" } },
|
||||
];
|
||||
}
|
||||
|
||||
// Pagination
|
||||
const skip = page && rowsPerPage ? page * rowsPerPage : 0;
|
||||
|
||||
// Build Sort option
|
||||
const sort = { [field]: order === "asc" ? 1 : -1 };
|
||||
|
||||
const matchStage = { teamId: new ObjectId(req.params.teamId) };
|
||||
if (type !== undefined) {
|
||||
matchStage.type = Array.isArray(type) ? { $in: type } : type;
|
||||
}
|
||||
if (filter !== undefined) {
|
||||
matchStage.$or = [
|
||||
{ name: { $regex: filter, $options: "i" } },
|
||||
{ url: { $regex: filter, $options: "i" } },
|
||||
];
|
||||
}
|
||||
|
||||
let result = await Monitor.aggregate([
|
||||
{ $match: matchStage },
|
||||
{ $skip: parseInt(skip) },
|
||||
...(rowsPerPage ? [{ $limit: parseInt(rowsPerPage) }] : []),
|
||||
{ $sort: sort },
|
||||
{
|
||||
$lookup: {
|
||||
from: "checks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
...(status && { status }),
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: checkOrder === "asc" ? 1 : -1 } },
|
||||
{ $limit: parseInt(limit) || 0 },
|
||||
],
|
||||
as: "standardchecks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "pagespeedchecks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
...(status && { status }),
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: checkOrder === "asc" ? 1 : -1 } },
|
||||
{ $limit: parseInt(limit) || 0 },
|
||||
],
|
||||
as: "pagespeedchecks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "hardwarechecks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
...(status && { status }),
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: checkOrder === "asc" ? 1 : -1 } },
|
||||
{ $limit: parseInt(limit) || 0 },
|
||||
],
|
||||
as: "hardwarechecks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
checks: {
|
||||
$switch: {
|
||||
branches: [
|
||||
{
|
||||
case: { $in: ["$type", ["http", "ping", "docker", "port"]] },
|
||||
then: "$standardchecks",
|
||||
const results = await Monitor.aggregate([
|
||||
{ $match: matchStage },
|
||||
{
|
||||
$facet: {
|
||||
summary: [
|
||||
{
|
||||
$group: {
|
||||
_id: null,
|
||||
totalMonitors: { $sum: 1 },
|
||||
upMonitors: {
|
||||
$sum: {
|
||||
$cond: [{ $eq: ["$status", true] }, 1, 0],
|
||||
},
|
||||
{
|
||||
case: { $eq: ["$type", "pagespeed"] },
|
||||
then: "$pagespeedchecks",
|
||||
},
|
||||
downMonitors: {
|
||||
$sum: {
|
||||
$cond: [{ $eq: ["$status", false] }, 1, 0],
|
||||
},
|
||||
{
|
||||
case: { $eq: ["$type", "hardware"] },
|
||||
then: "$hardwarechecks",
|
||||
},
|
||||
pausedMonitors: {
|
||||
$sum: {
|
||||
$cond: [{ $eq: ["$isActive", false] }, 1, 0],
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
_id: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
monitors: [
|
||||
{ $sort: sort },
|
||||
{ $skip: skip },
|
||||
...(rowsPerPage ? [{ $limit: rowsPerPage }] : []),
|
||||
{
|
||||
$lookup: {
|
||||
from: "checks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
...(limit ? [{ $limit: limit }] : []),
|
||||
],
|
||||
as: "standardchecks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "pagespeedchecks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
...(limit ? [{ $limit: limit }] : []),
|
||||
],
|
||||
as: "pagespeedchecks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$lookup: {
|
||||
from: "hardwarechecks",
|
||||
let: { monitorId: "$_id" },
|
||||
pipeline: [
|
||||
{
|
||||
$match: {
|
||||
$expr: { $eq: ["$monitorId", "$$monitorId"] },
|
||||
},
|
||||
},
|
||||
{ $sort: { createdAt: -1 } },
|
||||
...(limit ? [{ $limit: limit }] : []),
|
||||
],
|
||||
as: "hardwarechecks",
|
||||
},
|
||||
},
|
||||
{
|
||||
$addFields: {
|
||||
checks: {
|
||||
$switch: {
|
||||
branches: [
|
||||
{
|
||||
case: { $in: ["$type", ["http", "ping", "docker", "port"]] },
|
||||
then: "$standardchecks",
|
||||
},
|
||||
{
|
||||
case: { $eq: ["$type", "pagespeed"] },
|
||||
then: "$pagespeedchecks",
|
||||
},
|
||||
{
|
||||
case: { $eq: ["$type", "hardware"] },
|
||||
then: "$hardwarechecks",
|
||||
},
|
||||
],
|
||||
default: [],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
standardchecks: 0,
|
||||
pagespeedchecks: 0,
|
||||
hardwarechecks: 0,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
standardchecks: 0,
|
||||
pagespeedchecks: 0,
|
||||
hardwarechecks: 0,
|
||||
},
|
||||
},
|
||||
{
|
||||
$project: {
|
||||
summary: { $arrayElemAt: ["$summary", 0] },
|
||||
monitors: 1,
|
||||
},
|
||||
]);
|
||||
if (normalize) {
|
||||
result = result.map((monitor) => {
|
||||
monitor.checks = NormalizeData(monitor.checks, 10, 100);
|
||||
return monitor;
|
||||
});
|
||||
}
|
||||
return { monitors: result, monitorCount };
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "getMonitorsByTeamId";
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
]);
|
||||
|
||||
let { monitors, summary } = results[0];
|
||||
monitors = monitors.map((monitor) => {
|
||||
monitor.checks = NormalizeData(monitor.checks, 10, 100);
|
||||
return monitor;
|
||||
});
|
||||
return { monitors, summary };
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -832,9 +792,8 @@ export {
|
||||
getAllMonitorsWithUptimeStats,
|
||||
getMonitorStatsById,
|
||||
getMonitorById,
|
||||
getUptimeDetailsById,
|
||||
getMonitorsSummaryByTeamId,
|
||||
getMonitorsByTeamId,
|
||||
getUptimeDetailsById,
|
||||
createMonitor,
|
||||
deleteMonitor,
|
||||
deleteAllMonitors,
|
||||
|
||||
@@ -17,6 +17,7 @@ class MonitorRoutes {
|
||||
"/hardware/details/:monitorId",
|
||||
this.monitorController.getHardwareDetailsById
|
||||
);
|
||||
|
||||
this.router.get(
|
||||
"/uptime/details/:monitorId",
|
||||
this.monitorController.getUptimeDetailsById
|
||||
@@ -30,10 +31,7 @@ class MonitorRoutes {
|
||||
);
|
||||
});
|
||||
this.router.get("/:monitorId", this.monitorController.getMonitorById);
|
||||
this.router.get(
|
||||
"/team/summary/:teamId",
|
||||
this.monitorController.getMonitorsSummaryByTeamId
|
||||
);
|
||||
|
||||
this.router.get("/team/:teamId", this.monitorController.getMonitorsByTeamId);
|
||||
|
||||
this.router.get(
|
||||
|
||||
@@ -136,32 +136,12 @@ const getMonitorByIdQueryValidation = joi.object({
|
||||
normalize: joi.boolean(),
|
||||
});
|
||||
|
||||
const getMonitorsSummaryByTeamIdParamValidation = joi.object({
|
||||
teamId: joi.string().required(),
|
||||
});
|
||||
|
||||
const getMonitorsSummaryByTeamIdQueryValidation = joi.object({
|
||||
type: joi
|
||||
.alternatives()
|
||||
.try(
|
||||
joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port"),
|
||||
joi
|
||||
.array()
|
||||
.items(
|
||||
joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port")
|
||||
)
|
||||
),
|
||||
});
|
||||
|
||||
const getMonitorsByTeamIdValidation = joi.object({
|
||||
const getMonitorsByTeamIdParamValidation = joi.object({
|
||||
teamId: joi.string().required(),
|
||||
});
|
||||
|
||||
const getMonitorsByTeamIdQueryValidation = joi.object({
|
||||
status: joi.boolean(),
|
||||
checkOrder: joi.string().valid("asc", "desc"),
|
||||
limit: joi.number(),
|
||||
normalize: joi.boolean(),
|
||||
type: joi
|
||||
.alternatives()
|
||||
.try(
|
||||
@@ -467,9 +447,7 @@ export {
|
||||
createMonitorBodyValidation,
|
||||
getMonitorByIdParamValidation,
|
||||
getMonitorByIdQueryValidation,
|
||||
getMonitorsSummaryByTeamIdParamValidation,
|
||||
getMonitorsSummaryByTeamIdQueryValidation,
|
||||
getMonitorsByTeamIdValidation,
|
||||
getMonitorsByTeamIdParamValidation,
|
||||
getMonitorsByTeamIdQueryValidation,
|
||||
getMonitorStatsByIdParamValidation,
|
||||
getMonitorStatsByIdQueryValidation,
|
||||
|
||||
Reference in New Issue
Block a user