diff --git a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx index 39aa8dcbf..567de0b68 100644 --- a/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx +++ b/Client/src/Components/Charts/MonitorDetailsAreaChart/index.jsx @@ -1,6 +1,5 @@ import PropTypes from "prop-types"; import { AreaChart, Area, XAxis, Tooltip, ResponsiveContainer } from "recharts"; -import { NormalizeData } from "../ChartUtils"; import "./index.css"; const CustomToolTip = ({ active, payload, label }) => { @@ -28,7 +27,7 @@ const CustomToolTip = ({ active, payload, label }) => { return null; }; -const MonitorDetailsAreaChart = ({ checks, filter }) => { +const MonitorDetailsAreaChart = ({ checks }) => { // SQUASH ERROR, NOT PERMANENT SOLUTION const error = console.error; console.error = (...args) => { @@ -47,45 +46,12 @@ const MonitorDetailsAreaChart = ({ checks, filter }) => { }); }; - const filterChecks = (checks, filter, numToDisplay) => { - const limits = { - day: 24 * 60 * 60 * 1000, - week: 24 * 60 * 60 * 1000 * 7, - month: 24 * 60 * 60 * 1000 * 30, //TODO better monthly calculations - }; - - const now = new Date().getTime(); - let result = []; - for (let i = 0; i < checks.length; i++) { - const checkTime = new Date(checks[i].createdAt).getTime(); - if (now - checkTime < limits[filter]) { - result.push(checks[i]); - } - } - - // If more than numToDisplay checks, pick every nth check - if (result.length > numToDisplay) { - const n = Math.ceil(result.length / numToDisplay); - result = result.filter((_, idx) => { - return idx % n === 0; - }); - } - - return result; - }; - - let normalizedChecks = []; - if (checks && checks.length > 0) { - const filteredChecks = filterChecks(checks, filter, 75); - normalizedChecks = NormalizeData(filteredChecks, 10, 100); - } - return ( { - const normalizedChecks = NormalizeData(checks, 1, 100); return (
- {normalizedChecks.map((check, index) => ( + {checks.map((check, index) => ( { const { monitorId } = useParams(); const { authToken } = useSelector((state) => state.auth); const [filter, setFilter] = useState("day"); + const navigate = useNavigate(); + + const fetchDataForTable = useCallback(async () => { + try { + const limit = 0; + + const res = await axiosInstance.get( + `/monitors/${monitorId}?sortOrder=asc&limit=${limit}`, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + } + ); + const data = { + cols: [ + { id: 1, name: "Status" }, + { id: 2, name: "Date & Time" }, + { id: 3, name: "Message" }, + ], + rows: res.data.data.checks.map((check, idx) => { + const status = check.status === true ? "up" : "down"; + + return { + id: check._id, + data: [ + { + id: idx, + data: ( + + ), + }, + { + id: idx + 1, + data: new Date(check.createdAt).toLocaleString(), + }, + { id: idx + 2, data: check.statusCode }, + ], + }; + }), + }; + + setData(data); + } catch (error) { + console.log(error); + } + }, [authToken, monitorId]); + + const fetchMonitor = useCallback(async () => { + try { + const res = await axiosInstance.get( + `/monitors/${monitorId}?sortOrder=asc&filter=${filter}&numToDisplay=50&normalize=true`, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + } + ); + setMonitor(res.data.data); + } catch (error) { + console.error("Error fetching monitor of id: " + monitorId); + navigate("/not-found"); + } + }, [authToken, monitorId, navigate, filter]); const fetchDataForChart = async () => { try { @@ -185,14 +253,17 @@ const DetailsPage = () => { useEffect(() => { fetchMonitor(); - }, [monitorId, authToken]); + }, [fetchMonitor]); + + useEffect(() => { + fetchDataForTable(); + }, [fetchDataForTable]); useEffect(() => { fetchDataForChart(); }, [filter]); const theme = useTheme(); - const navigate = useNavigate(); /** * Function to calculate uptime duration based on the most recent check. diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index d7f70e300..e56be39b2 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -145,7 +145,7 @@ const Monitors = () => { useEffect(() => { dispatch(getUptimeMonitorsByUserId(authState.authToken)); - }, []); + }, [authState.authToken, dispatch]); const up = monitorState.monitors.reduce((acc, cur) => { return cur.status === true ? acc + 1 : acc; diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index 9bca432c2..15f431673 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -3,6 +3,7 @@ const Check = require("../../../models/Check"); const PageSpeedCheck = require("../../../models/PageSpeedCheck"); const { errorMessages } = require("../../../utils/messages"); const Notification = require("../../../models/Notification"); +const { NormalizeData } = require("../../../utils/dataUtils"); /** * Get all monitors @@ -32,7 +33,14 @@ const getAllMonitors = async (req, res) => { const getMonitorById = async (req, res) => { try { const { monitorId } = req.params; - let { status, limit, sortOrder } = req.query; + let { status, limit, sortOrder, filter, numToDisplay, normalize } = + req.query; + + const filterLookup = { + day: new Date(new Date().setDate(new Date().getDate() - 1)), + week: new Date(new Date().setDate(new Date().getDate() - 7)), + month: new Date(new Date().setMonth(new Date().getMonth() - 1)), + }; // This effectively removes limit, returning all checks if (limit === undefined) limit = 0; @@ -52,18 +60,35 @@ const getMonitorById = async (req, res) => { checksQuery.status = status; } + // Filter checks by "day", "week", or "month" + if (filter !== undefined) { + checksQuery.createdAt = { $gte: filterLookup[filter] }; + } + // Determine model type let model = monitor.type === "http" || monitor.type === "ping" ? Check : PageSpeedCheck; - const checks = await model + let checks = await model .find(checksQuery) .sort({ createdAt: sortOrder, }) .limit(limit); + + // If more than numToDisplay checks, pick every nth check + if (numToDisplay !== undefined && checks && checks.length > numToDisplay) { + const n = Math.ceil(checks.length / numToDisplay); + checks = checks.filter((_, index) => index % n === 0); + } + + // Normalize checks if requested + if (normalize) { + checks = NormalizeData(checks, 10, 100); + } + const notifications = await Notification.find({ monitorId: monitor._id }); const monitorWithChecks = { ...monitor.toObject(), checks, notifications }; return monitorWithChecks; @@ -82,7 +107,7 @@ const getMonitorById = async (req, res) => { */ const getMonitorsByUserId = async (req, res) => { try { - let { limit, type, status, sortOrder } = req.query; + let { limit, type, status, sortOrder, normalize } = req.query; const monitorQuery = { userId: req.params.userId }; if (type !== undefined) { @@ -115,13 +140,18 @@ const getMonitorsByUserId = async (req, res) => { : PageSpeedCheck; // Checks are order newest -> oldest - const checks = await model + let checks = await model .find(checksQuery) .sort({ createdAt: sortOrder, }) .limit(limit); + //Normalize checks if requested + if (normalize === true) { + checks = NormalizeData(checks, 10, 100); + } + // Get notifications const notifications = await Notification.find({ monitorId: monitor._id, diff --git a/Client/src/Components/Charts/ChartUtils.js b/Server/utils/dataUtils.js similarity index 89% rename from Client/src/Components/Charts/ChartUtils.js rename to Server/utils/dataUtils.js index 77d9d4c2d..3495cc3d6 100644 --- a/Client/src/Components/Charts/ChartUtils.js +++ b/Server/utils/dataUtils.js @@ -11,7 +11,7 @@ const calculatePercentile = (arr, percentile) => { ); }; -export const NormalizeData = (checks, rangeMin, rangeMax) => { +const NormalizeData = (checks, rangeMin, rangeMax) => { if (checks.length > 1) { // Get the 5th and 95th percentile const min = calculatePercentile(checks, 0); @@ -19,6 +19,7 @@ export const NormalizeData = (checks, rangeMin, rangeMax) => { const normalizedChecks = checks.map((check) => { const originalResponseTime = check.responseTime; + console.log(originalResponseTime); // Normalize the response time between 1 and 100 let normalizedResponseTime = rangeMin + @@ -31,7 +32,7 @@ export const NormalizeData = (checks, rangeMin, rangeMax) => { Math.min(rangeMax, normalizedResponseTime) ); return { - ...check, + ...check._doc, responseTime: normalizedResponseTime, originalResponseTime: originalResponseTime, }; @@ -44,3 +45,7 @@ export const NormalizeData = (checks, rangeMin, rangeMax) => { }); } }; + +module.exports = { + NormalizeData, +}; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index ccf16f257..8e3dbcfec 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -126,6 +126,9 @@ const getMonitorByIdQueryValidation = joi.object({ status: joi.boolean(), sortOrder: joi.string().valid("asc", "desc"), limit: joi.number(), + filter: joi.string().valid("day", "week", "month"), + numToDisplay: joi.number(), + normalize: joi.boolean(), }); const getMonitorsByUserIdValidation = joi.object({ @@ -136,6 +139,7 @@ const getMonitorsByUserIdQueryValidation = joi.object({ status: joi.boolean(), sortOrder: joi.string().valid("asc", "desc"), limit: joi.number(), + normalize: joi.boolean(), type: joi .alternatives() .try(