From 2f1053bd05c10fdb1b237ec1a19f9ce2ae5e4953 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 21 Jun 2024 11:08:31 -0700 Subject: [PATCH 1/5] Iniital commit --- .../src/Components/CurrentMonitors/index.jsx | 2 +- Client/src/Components/HostsTable/index.jsx | 23 +++++++----- Client/src/Features/Monitors/monitorsSlice.js | 15 +++++--- Client/src/Pages/Monitors/CurrentStats.jsx | 35 +------------------ Client/src/Pages/Monitors/index.jsx | 15 ++++++-- 5 files changed, 41 insertions(+), 49 deletions(-) diff --git a/Client/src/Components/CurrentMonitors/index.jsx b/Client/src/Components/CurrentMonitors/index.jsx index 868ce7a99..4f53ebeb5 100644 --- a/Client/src/Components/CurrentMonitors/index.jsx +++ b/Client/src/Components/CurrentMonitors/index.jsx @@ -10,7 +10,7 @@ const CurrentMonitors = ({ monitors }) => {
Current monitors
-
5
+
{monitors.length}
diff --git a/Client/src/Components/HostsTable/index.jsx b/Client/src/Components/HostsTable/index.jsx index 2867ef26a..c5c5faf48 100644 --- a/Client/src/Components/HostsTable/index.jsx +++ b/Client/src/Components/HostsTable/index.jsx @@ -13,14 +13,21 @@ const HostsTable = ({ monitors }) => { - {monitors.map((monitor, index) => ( - - {monitor.host} - {monitor.status} - {monitor.team} - {monitor.actions} - - ))} + {monitors.map((monitor, index) => { + console.log(monitor); + return ( + + {monitor.url} + + {monitor.isActive ? "Active" : "Down"} + + {monitor.team} + + {monitor.actions} + + + ); + })}
diff --git a/Client/src/Features/Monitors/monitorsSlice.js b/Client/src/Features/Monitors/monitorsSlice.js index c025b02c2..8cb5e323f 100644 --- a/Client/src/Features/Monitors/monitorsSlice.js +++ b/Client/src/Features/Monitors/monitorsSlice.js @@ -1,5 +1,6 @@ import axios from "axios"; import { createAsyncThunk, createSlice } from "@reduxjs/toolkit"; +import { jwtDecode } from "jwt-decode"; const initialState = { isLoading: false, @@ -25,9 +26,17 @@ export const getMonitors = createAsyncThunk( export const getMonitorsByUserId = createAsyncThunk( "montiors/getMonitorsByUserId", async (token, thunkApi) => { + const user = jwtDecode(token); try { - // TODO get userId and make request - console.log(token); + const res = await axios.get( + import.meta.env.VITE_APP_API_BASE_URL + "/monitors/user/" + user._id, + { + headers: { + Authorization: `Bearer ${token}`, + }, + } + ); + return res.data; } catch (error) { return thunkApi.rejectWithValue(error.response.data); } @@ -50,7 +59,6 @@ const monitorsSlice = createSlice({ state.isLoading = false; state.success = action.payload.success; state.msg = action.payload.msg; - console.log(action.payload); state.monitors = action.payload.data; }) .addCase(getMonitors.rejected, (state, action) => { @@ -68,7 +76,6 @@ const monitorsSlice = createSlice({ .addCase(getMonitorsByUserId.fulfilled, (state, action) => { state.isLoading = false; state.success = action.payload.msg; - console.log(action.payload); state.monitors = action.payload.data; }) .addCase(getMonitorsByUserId.rejected, (state, action) => { diff --git a/Client/src/Pages/Monitors/CurrentStats.jsx b/Client/src/Pages/Monitors/CurrentStats.jsx index de15d953c..c0cd42c99 100644 --- a/Client/src/Pages/Monitors/CurrentStats.jsx +++ b/Client/src/Pages/Monitors/CurrentStats.jsx @@ -8,40 +8,7 @@ import ServerStatus from "../../Components/Charts/Servers/ServerStatus"; import CurrentMonitors from "../../Components/CurrentMonitors"; import MockData from "../../Mock/sample_data.json"; -const CurrentStats = (mockdata = true) => { - // The hardcoded part is passed as default value for the purpose of demonstration. - // Pass an empty array, [], before fetching the data - const [monitors, setMonitors] = useState([]); - - useEffect(() => { - if (mockdata) { - console.log(MockData.data); - setMonitors( - MockData.data.map((item) => ({ - host: Host(item.name, 100, "var(--env-var-color-17)", item.url), - status: HostStatus( - item.isActive - ? "var(--env-var-color-20)" - : "var(--env-var-color-21)", - item.isActive ? "Up" : "Down", - item.isActive - ? "var(--env-var-color-17)" - : "var(--env-var-color-19)" - ), - team: , - actions: HostActions(), - })) - ); - } - }, [mockdata]); - - // useEffect(() => { - // fetch("API_URL") - // .then((response) => response.json()) - // .then((data) => setMonitors(data)) - // .catch((error) => console.error(error)); - // }, []); - +const CurrentStats = ({ monitors }) => { return (
diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index a1dd61f0c..f863eedc4 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -1,12 +1,23 @@ // import CreateNewMonitor from "./CreateNewMonitor"; import CurrentStats from "./CurrentStats"; import "./index.css"; -import React from "react"; +import { useEffect } from "react"; +import { useSelector } from "react-redux"; +import { useDispatch } from "react-redux"; +import { getMonitorsByUserId } from "../../Features/Monitors/monitorsSlice"; const Monitors = () => { + const monitorState = useSelector((state) => state.monitors); + const authstate = useSelector((state) => state.auth); + const dispatch = useDispatch(); + + useEffect(() => { + dispatch(getMonitorsByUserId(authstate.authToken)); + }, []); + return (
- + {/* */}
); From 171b1b805949f44d077520933d3d550d82d6a574 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 21 Jun 2024 15:11:54 -0700 Subject: [PATCH 2/5] Conneted Monitor page to backend. Added data normailization to Checks. Added JSdocs and proptypes --- Client/src/App.jsx | 2 +- .../Components/Charts/BarChart/BarChart.jsx | 56 ++++++++++++++++--- .../src/Components/CurrentMonitors/index.jsx | 13 ++++- Client/src/Components/HostsTable/index.jsx | 25 +++++++-- Client/src/Layouts/HomeLayout/index.jsx | 12 ---- Client/src/Pages/Monitors/CurrentStats.jsx | 18 ++++-- Client/src/Pages/Monitors/index.jsx | 1 - 7 files changed, 92 insertions(+), 35 deletions(-) diff --git a/Client/src/App.jsx b/Client/src/App.jsx index 367a66758..ec0e4130c 100644 --- a/Client/src/App.jsx +++ b/Client/src/App.jsx @@ -31,7 +31,7 @@ function App() { } /> } /> } /> - } /> + {/* } /> */} } /> } /> } /> diff --git a/Client/src/Components/Charts/BarChart/BarChart.jsx b/Client/src/Components/Charts/BarChart/BarChart.jsx index e424fef3e..a1f3d48b0 100644 --- a/Client/src/Components/Charts/BarChart/BarChart.jsx +++ b/Client/src/Components/Charts/BarChart/BarChart.jsx @@ -1,17 +1,57 @@ -import React from "react"; import "./barChart.css"; import PropTypes from "prop-types"; +const RANGE_MAX = 100; +const RANGE_MIN = 1; + +/** + * Normalizes the response times of a set of checks to a specified range. + * This function calculates the minimum and maximum response times from the input, + * then scales each check's response time linearly between `RANGE_MIN` and `RANGE_MAX`. + * + * @param {Array} checks - An array of check objects. Each check should have a `responseTime` property. + * @returns {Array} An array of check objects with normalized `responseTime` values. + */ +const normalizeData = (checks) => { + const min = checks.reduce((accum, cur) => { + return Math.min(accum, cur.responseTime); + }, Infinity); + + const max = checks.reduce((accum, cur) => { + return Math.max(accum, cur.responseTime); + }, 0); + + const normalizedChecks = checks.map((check) => { + let normailzedResponseTime = + RANGE_MIN + + ((check.responseTime - min) * (RANGE_MAX - RANGE_MIN)) / (max - min); + const normalizedCheck = { ...check, responseTime: normailzedResponseTime }; + return normalizedCheck; + }); + return normalizedChecks; +}; + +/** + * BarChart renders a bar chart visualization for a set of checks. Each bar represents a check's response time, + * with the color indicating the check's status (green for successful checks, red for failed ones). + * + * @component + * @param {Array} checks - An array of check objects to be visualized. Each check object should have an `_id`, + * a `status` indicating success (true) or failure (false), and a `responseTime` representing the height of the bar in percentage. + */ const BarChart = ({ checks = [] }) => { + const normailzedChecks = normalizeData(checks); return (
- {checks.map((value, index) => ( -
- ))} + {normailzedChecks.map((check) => { + return ( +
+ ); + })}
); }; diff --git a/Client/src/Components/CurrentMonitors/index.jsx b/Client/src/Components/CurrentMonitors/index.jsx index 4f53ebeb5..2357c850e 100644 --- a/Client/src/Components/CurrentMonitors/index.jsx +++ b/Client/src/Components/CurrentMonitors/index.jsx @@ -1,8 +1,15 @@ import SearchTextField from "../TextFields/Search/SearchTextField"; import "./index.css"; -import React from "react"; import HostsTable from "../HostsTable"; import Pagination from "../Pagination"; +import PropTypes from "prop-types"; + +/** + * CurrentMonitors displays the current status of monitor + * + * @component + * @param {Array} monitors - An array of monitor objects to be displayed. + */ const CurrentMonitors = ({ monitors }) => { return ( @@ -24,4 +31,8 @@ const CurrentMonitors = ({ monitors }) => { ); }; +CurrentMonitors.propTypes = { + monitors: PropTypes.arrayOf(PropTypes.object).isRequired, +}; + export default CurrentMonitors; diff --git a/Client/src/Components/HostsTable/index.jsx b/Client/src/Components/HostsTable/index.jsx index c5c5faf48..2fb7991ea 100644 --- a/Client/src/Components/HostsTable/index.jsx +++ b/Client/src/Components/HostsTable/index.jsx @@ -1,4 +1,12 @@ -import React from "react"; +import BarChart from "../Charts/BarChart/BarChart"; +import PropTypes from "prop-types"; + +/** + * HostsTable displays the current status of monitor + * + * @component + * @param {Array} monitors - An array of monitor objects to be displayed. + */ const HostsTable = ({ monitors }) => { return ( @@ -8,20 +16,21 @@ const HostsTable = ({ monitors }) => { Host Status - Team + Response Actions - {monitors.map((monitor, index) => { - console.log(monitor); + {monitors.map((monitor) => { return ( - + {monitor.url} {monitor.isActive ? "Active" : "Down"} - {monitor.team} + + + {monitor.actions} @@ -34,4 +43,8 @@ const HostsTable = ({ monitors }) => { ); }; +HostsTable.propTypes = { + monitors: PropTypes.arrayOf(PropTypes.object).isRequired, +}; + export default HostsTable; diff --git a/Client/src/Layouts/HomeLayout/index.jsx b/Client/src/Layouts/HomeLayout/index.jsx index a7f7ce9df..2e4bc5e6b 100644 --- a/Client/src/Layouts/HomeLayout/index.jsx +++ b/Client/src/Layouts/HomeLayout/index.jsx @@ -3,24 +3,12 @@ import { Outlet } from "react-router"; import DashboardSidebar from "../../Components/DashboardSidebar"; import "./index.css"; -// Demo, remove me -import { useSelector } from "react-redux"; -// Emd Demo - const HomeLayout = () => { - const authState = useSelector((state) => state.auth); - const { user, msg } = authState; return (
- {/* Remove me later */} - {user &&

{user.firstname}

} - {user &&

{user.lastname}

} - {user &&

{user.email}

} - {msg &&

Msg: {msg}

} - {/* I am a demo */}
diff --git a/Client/src/Pages/Monitors/CurrentStats.jsx b/Client/src/Pages/Monitors/CurrentStats.jsx index c0cd42c99..b2455936c 100644 --- a/Client/src/Pages/Monitors/CurrentStats.jsx +++ b/Client/src/Pages/Monitors/CurrentStats.jsx @@ -1,12 +1,14 @@ -import React, { useEffect, useState } from "react"; -import Host from "../../Components/Host"; -import HostStatus from "../../Components/HostStatus"; -import HostActions from "../../Components/HostActions"; -import BarChart from "../../Components/Charts/BarChart/BarChart"; import Button from "../../Components/Button"; import ServerStatus from "../../Components/Charts/Servers/ServerStatus"; import CurrentMonitors from "../../Components/CurrentMonitors"; -import MockData from "../../Mock/sample_data.json"; +import PropTypes from "prop-types"; + +/** + * CurrentStats displays the current status of monitor + * + * @component + * @param {Array} monitors - An array of monitor objects to be displayed. + */ const CurrentStats = ({ monitors }) => { return ( @@ -33,4 +35,8 @@ const CurrentStats = ({ monitors }) => { ); }; +CurrentStats.propTypes = { + monitors: PropTypes.arrayOf(PropTypes.object).isRequired, +}; + export default CurrentStats; diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index f863eedc4..875f59443 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -18,7 +18,6 @@ const Monitors = () => { return (
- {/* */}
); }; From 9b8c65173620440a2c1bd04519ef5a9591e23aec Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 21 Jun 2024 15:51:53 -0700 Subject: [PATCH 3/5] Monitor summary now displays correct data --- .../Components/Charts/Servers/ServerStatus.jsx | 4 ++-- Client/src/Pages/Monitors/CurrentStats.jsx | 18 +++++++++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Client/src/Components/Charts/Servers/ServerStatus.jsx b/Client/src/Components/Charts/Servers/ServerStatus.jsx index f910abf27..632971ab1 100644 --- a/Client/src/Components/Charts/Servers/ServerStatus.jsx +++ b/Client/src/Components/Charts/Servers/ServerStatus.jsx @@ -7,7 +7,7 @@ import { useTheme } from "@mui/material"; * @component * @param {Object} props * @param {string} props.title - The title text for the server status (required) - * @param {string} props.value - The value text to be displayed (required) + * @param {number} props.value - The value text to be displayed (required) * @param {string} props.state - The state of the server (e.g., "online", "offline", "warning") (required) * @returns {JSX.Element} - Renders the server status component */ @@ -30,7 +30,7 @@ const ServerStatus = ({ title, value, state }) => { ServerStatus.propTypes = { title: PropTypes.string.isRequired, - value: PropTypes.string.isRequired, + value: PropTypes.number.isRequired, state: PropTypes.string.isRequired, }; diff --git a/Client/src/Pages/Monitors/CurrentStats.jsx b/Client/src/Pages/Monitors/CurrentStats.jsx index b2455936c..ab32b14d6 100644 --- a/Client/src/Pages/Monitors/CurrentStats.jsx +++ b/Client/src/Pages/Monitors/CurrentStats.jsx @@ -11,6 +11,18 @@ import PropTypes from "prop-types"; */ const CurrentStats = ({ monitors }) => { + const up = monitors.reduce((acc, cur) => { + if (cur.checks) { + return cur.checks[cur.checks.length - 1].status === true ? acc + 1 : acc; + } + }, 0); + + const down = monitors.reduce((acc, cur) => { + if (cur.checks) { + return cur.checks[cur.checks.length - 1].status === false ? acc + 1 : acc; + } + }, 0); + return (
@@ -25,9 +37,9 @@ const CurrentStats = ({ monitors }) => {
- - - + + +
From 68cb5b4597edd594037e07466b0e52f4af2fc6d1 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 21 Jun 2024 16:07:30 -0700 Subject: [PATCH 4/5] Added forgotten Host and HostStatus components --- Client/src/Components/HostsTable/index.jsx | 28 ++++++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Client/src/Components/HostsTable/index.jsx b/Client/src/Components/HostsTable/index.jsx index 2fb7991ea..4c3db4d9a 100644 --- a/Client/src/Components/HostsTable/index.jsx +++ b/Client/src/Components/HostsTable/index.jsx @@ -1,6 +1,7 @@ import BarChart from "../Charts/BarChart/BarChart"; import PropTypes from "prop-types"; - +import Host from "../Host/"; +import HostStatus from "../HostStatus"; /** * HostsTable displays the current status of monitor * @@ -22,12 +23,29 @@ const HostsTable = ({ monitors }) => { {monitors.map((monitor) => { + const host = Host( + monitor.name, + 100, + "var(--env-var-color-17)", + monitor.url + ); + + const status = HostStatus( + monitor.checks[monitor.checks.length - 1].status === true + ? "var(--env-var-color-20)" + : "var(--env-var-color-21)", + monitor.checks[monitor.checks.length - 1].status === true + ? "Up" + : "Down", + monitor.checks[monitor.checks.length - 1].status === true + ? "var(--env-var-color-17)" + : "var(--env-var-color-19)" + ); + return ( - {monitor.url} - - {monitor.isActive ? "Active" : "Down"} - + {host} + {status} From d88389838486e043f72c29f1c73259daeadfbfb9 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 24 Jun 2024 10:59:18 -0700 Subject: [PATCH 5/5] Refactored to deal with empty data set --- Client/src/Components/HostsTable/index.jsx | 34 +++++++++++++++------- Client/src/Pages/Monitors/CurrentStats.jsx | 6 ++-- Server/service/networkService.js | 5 ++-- 3 files changed, 30 insertions(+), 15 deletions(-) diff --git a/Client/src/Components/HostsTable/index.jsx b/Client/src/Components/HostsTable/index.jsx index 4c3db4d9a..8e5af7164 100644 --- a/Client/src/Components/HostsTable/index.jsx +++ b/Client/src/Components/HostsTable/index.jsx @@ -9,6 +9,25 @@ import HostStatus from "../HostStatus"; * @param {Array} monitors - An array of monitor objects to be displayed. */ +const getLastCheckStatus = (monitor) => { + if (monitor.checks.length === 0) { + // Default values for when there are no checks + return { + color: "var(--env-var-color-21)", + statusText: "No Data", + dotColor: "var(--env-var-color-19)", + }; + } + + const lastCheck = monitor.checks[monitor.checks.length - 1]; + const isUp = lastCheck.status === true; + return { + color: isUp ? "var(--env-var-color-20)" : "var(--env-var-color-21)", + statusText: isUp ? "Up" : "Down", + dotColor: isUp ? "var(--env-var-color-17)" : "var(--env-var-color-19)", + }; +}; + const HostsTable = ({ monitors }) => { return (
@@ -30,17 +49,10 @@ const HostsTable = ({ monitors }) => { monitor.url ); - const status = HostStatus( - monitor.checks[monitor.checks.length - 1].status === true - ? "var(--env-var-color-20)" - : "var(--env-var-color-21)", - monitor.checks[monitor.checks.length - 1].status === true - ? "Up" - : "Down", - monitor.checks[monitor.checks.length - 1].status === true - ? "var(--env-var-color-17)" - : "var(--env-var-color-19)" - ); + const { color, currentStatus, dotColor } = + getLastCheckStatus(monitor); + + const status = HostStatus(color, currentStatus, dotColor); return ( diff --git a/Client/src/Pages/Monitors/CurrentStats.jsx b/Client/src/Pages/Monitors/CurrentStats.jsx index ab32b14d6..729de36d3 100644 --- a/Client/src/Pages/Monitors/CurrentStats.jsx +++ b/Client/src/Pages/Monitors/CurrentStats.jsx @@ -12,15 +12,17 @@ import PropTypes from "prop-types"; const CurrentStats = ({ monitors }) => { const up = monitors.reduce((acc, cur) => { - if (cur.checks) { + if (cur.checks.length > 0) { return cur.checks[cur.checks.length - 1].status === true ? acc + 1 : acc; } + return 0; }, 0); const down = monitors.reduce((acc, cur) => { - if (cur.checks) { + if (cur.checks.length > 0) { return cur.checks[cur.checks.length - 1].status === false ? acc + 1 : acc; } + return 0; }, 0); return ( diff --git a/Server/service/networkService.js b/Server/service/networkService.js index f3f40999d..2437786fb 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -2,6 +2,7 @@ const axios = require("axios"); const ping = require("ping"); const logger = require("../utils/logger"); const Check = require("../models/Check"); +const { Console } = require("winston/lib/winston/transports"); class NetworkService { constructor(db) { @@ -149,9 +150,9 @@ class NetworkService { const insertedCheck = await check.save(); return insertedCheck.status; } catch (error) { - logger.error(`Error wrtiting check for ${job.id}`, { + logger.error(`Error wrtiting check for ${check.monitorId}`, { service: this.SERVICE_NAME, - jobId: job.id, + monitorId: check.monitorId, error: error, }); return false;