diff --git a/Client/src/Pages/Uptime/Configure/index.jsx b/Client/src/Pages/Uptime/Configure/index.jsx index 76c030776..6cef97e0f 100644 --- a/Client/src/Pages/Uptime/Configure/index.jsx +++ b/Client/src/Pages/Uptime/Configure/index.jsx @@ -11,7 +11,6 @@ import { updateUptimeMonitor, pauseUptimeMonitor, getUptimeMonitorById, - getUptimeSummaryByTeamId, deleteUptimeMonitor, } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; import TextInput from "../../../Components/Inputs/TextInput"; @@ -157,7 +156,6 @@ const Configure = () => { const action = await dispatch(updateUptimeMonitor({ authToken, monitor: monitor })); if (action.meta.requestStatus === "fulfilled") { createToast({ body: "Monitor updated successfully!" }); - dispatch(getUptimeSummaryByTeamId(authToken)); } else { createToast({ body: "Failed to update monitor." }); } diff --git a/Client/src/Pages/Uptime/Home/index.jsx b/Client/src/Pages/Uptime/Home/index.jsx index 50ab2572b..7e1f056ca 100644 --- a/Client/src/Pages/Uptime/Home/index.jsx +++ b/Client/src/Pages/Uptime/Home/index.jsx @@ -14,7 +14,6 @@ import { getUptimeSummaryByTeamId, getUptimeMonitorsByTeamId, } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; -import { setRowsPerPage } from "../../../Features/UI/uiSlice"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; import { useSelector, useDispatch } from "react-redux"; import { useNavigate } from "react-router-dom"; @@ -26,15 +25,13 @@ const BREADCRUMBS = [{ name: `Uptime`, path: "/uptime" }]; const UptimeMonitors = () => { // Redux state - const { isLoading, monitorsSummary } = useSelector((state) => state.uptimeMonitors); - const authState = useSelector((state) => state.auth); - const { rowsPerPage } = useSelector((state) => state.ui.monitors); // Local state const [monitors, setMonitors] = useState([]); const [sort, setSort] = useState({}); const [search, setSearch] = useState(""); const [page, setPage] = useState(0); + const [rowsPerPage, setRowsPerPage] = useState(10); const [isSearching, setIsSearching] = useState(false); const [monitorUpdateTrigger, setMonitorUpdateTrigger] = useState(false); @@ -46,9 +43,6 @@ const UptimeMonitors = () => { const isAdmin = useIsAdmin(); const { isLoading, monitorsSummary } = useSelector((state) => state.uptimeMonitors); const authState = useSelector((state) => state.auth); - const dispatch = useDispatch({}); - const [monitorUpdateTrigger, setMonitorUpdateTrigger] = useState(false); - const fetchParams = useMemo( () => ({ diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index c296de34b..aa4cafbb7 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -12,6 +12,7 @@ import { buildUptimeDetailsPipeline, buildHardwareDetailsPipeline, } from "./monitorModuleQueries.js"; +import { ObjectId } from "mongodb"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -570,6 +571,7 @@ const getMonitorsByTeamId = async (req, res) => { } = req.query; const monitorQuery = { teamId: req.params.teamId }; + const monitorCount = await Monitor.countDocuments(monitorQuery); if (type !== undefined) { monitorQuery.type = Array.isArray(type) ? { $in: type } : type; @@ -582,46 +584,118 @@ const getMonitorsByTeamId = async (req, res) => { { url: { $regex: filter, $options: "i" } }, ]; } - const monitorCount = await Monitor.countDocuments(monitorQuery); // Pagination const skip = page && rowsPerPage ? page * rowsPerPage : 0; // Build Sort option - const sort = field ? { [field]: order === "asc" ? 1 : -1 } : {}; + const sort = { [field]: order === "asc" ? 1 : -1 }; - const monitors = await Monitor.find(monitorQuery) - .skip(skip) - .limit(rowsPerPage) - .sort(sort); - - // Early return if limit is set to -1, indicating we don't want any checks - if (limit === "-1") { - return { monitors, monitorCount }; + 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" } }, + ]; } - // Map each monitor to include its associated checks - const monitorsWithChecks = await Promise.all( - monitors.map(async (monitor) => { - let model = CHECK_MODEL_LOOKUP[monitor.type]; - // Checks are order newest -> oldest - let checks = await model - .find({ - monitorId: monitor._id, - ...(status && { status }), - }) - .sort({ createdAt: checkOrder === "asc" ? 1 : -1 }) - - .limit(limit || 0); - - //Normalize checks if requested - if (normalize !== undefined) { - checks = NormalizeData(checks, 10, 100); - } - return { ...monitor.toObject(), checks }; - }) - ); - return { monitors: monitorsWithChecks, monitorCount }; + 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", + }, + { + case: { $eq: ["$type", "pagespeed"] }, + then: "$pagespeedchecks", + }, + { + case: { $eq: ["$type", "hardware"] }, + then: "$hardwarechecks", + }, + ], + default: [], + }, + }, + }, + }, + { + $project: { + standardchecks: 0, + pagespeedchecks: 0, + hardwarechecks: 0, + }, + }, + ]); + 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"; diff --git a/Server/utils/dataUtils.js b/Server/utils/dataUtils.js index fd10a4257..ca6ca66df 100644 --- a/Server/utils/dataUtils.js +++ b/Server/utils/dataUtils.js @@ -38,7 +38,7 @@ const NormalizeData = (checks, rangeMin, rangeMax) => { Math.min(rangeMax, normalizedResponseTime) ); return { - ...check._doc, + ...check, responseTime: normalizedResponseTime, originalResponseTime: originalResponseTime, }; @@ -47,7 +47,7 @@ const NormalizeData = (checks, rangeMin, rangeMax) => { return normalizedChecks; } else { return checks.map((check) => { - return { ...check._doc, originalResponseTime: check.responseTime }; + return { ...check, originalResponseTime: check.responseTime }; }); } };