From 3ac85edd536783b16c2b39778efa944474000b45 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 12 Aug 2024 13:49:10 -0700 Subject: [PATCH 01/18] Remove unused imports --- Client/src/Pages/PageSpeed/Details/index.jsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx index 04e08d122..963b0b31f 100644 --- a/Client/src/Pages/PageSpeed/Details/index.jsx +++ b/Client/src/Pages/PageSpeed/Details/index.jsx @@ -6,11 +6,9 @@ import { useTheme } from "@emotion/react"; import { useNavigate, useParams } from "react-router-dom"; import { useSelector } from "react-redux"; import { - formatDate, formatDuration, formatDurationRounded, } from "../../../Utils/timeUtils"; -import { getLastChecked } from "../../../Utils/monitorUtils"; import axiosInstance from "../../../Utils/axiosConfig"; import Button from "../../../Components/Button"; import WestRoundedIcon from "@mui/icons-material/WestRounded"; From e533c9491831d965da367feeb62ad28761f765bc Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 12 Aug 2024 15:15:13 -0700 Subject: [PATCH 02/18] Implemented new stats for Monitor Details --- Client/src/Pages/Monitors/Details/index.jsx | 51 +++++++++++++++++++-- Server/controllers/monitorController.js | 37 +++++++++++++++ Server/models/Check.js | 1 + Server/package-lock.json | 14 ++++-- Server/package.json | 1 + Server/routes/monitorRoute.js | 1 + Server/utils/messages.js | 1 + 7 files changed, 100 insertions(+), 6 deletions(-) diff --git a/Client/src/Pages/Monitors/Details/index.jsx b/Client/src/Pages/Monitors/Details/index.jsx index 918077a91..e2c0eb91b 100644 --- a/Client/src/Pages/Monitors/Details/index.jsx +++ b/Client/src/Pages/Monitors/Details/index.jsx @@ -113,6 +113,7 @@ const DetailsPage = () => { const { monitorId } = useParams(); const { authToken } = useSelector((state) => state.auth); const [dateRange, setDateRange] = useState("day"); + const [certificateExpiry, setCertificateExpiry] = useState("N/A"); const navigate = useNavigate(); const fetchMonitor = useCallback(async () => { @@ -136,10 +137,23 @@ const DetailsPage = () => { fetchMonitor(); }, [fetchMonitor]); + useEffect(() => { + const fetchCertificate = async () => { + const res = await axiosInstance.get( + `/monitors/certificate/${monitorId}`, + { + headers: { + Authorization: `Bearer ${authToken}`, + }, + } + ); + setCertificateExpiry(res.data.data.certificateDate); + }; + fetchCertificate(); + }, [authToken, monitorId]); + const theme = useTheme(); - let loading = Object.keys(monitor).length === 0; - return ( {loading ? ( @@ -222,6 +236,35 @@ const DetailsPage = () => { value={`${formatDuration(monitor?.lastChecked)} ago`} /> + + + + + + + { - + diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index b8aa24c25..28c33caeb 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -7,6 +7,7 @@ const { getMonitorsByUserIdQueryValidation, } = require("../validation/joi"); +const sslChecker = require("ssl-checker"); const SERVICE_NAME = "monitorController"; const { errorMessages, successMessages } = require("../utils/messages"); const { runInNewContext } = require("vm"); @@ -65,6 +66,41 @@ const getMonitorStatsById = async (req, res, next) => { } }; +const getMonitorCertificate = async (req, res, next) => { + try { + //validation + } catch (error) { + error.status = 422; + error.message = + error.details?.[0]?.message || error.message || "Validation Error"; + next(error); + } + + try { + const monitor = await req.db.getMonitorById(req, res); + const monitorUrl = new URL(monitor.url); + const certificate = await sslChecker(monitorUrl.hostname); + if (certificate && certificate.validTo) { + return res.json({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { + certificateDate: new Date(certificate.validTo).toLocaleDateString(), + }, + }); + } else { + return res.json({ + success: true, + msg: successMessages.MONITOR_CERTIFICATE, + data: { certificateDate: "N/A" }, + }); + } + } catch (error) { + error.service = SERVICE_NAME; + next(error); + } +}; + /** * Returns monitor with matching ID * @async @@ -302,6 +338,7 @@ const editMonitor = async (req, res, next) => { module.exports = { getAllMonitors, getMonitorStatsById, + getMonitorCertificate, getMonitorById, getMonitorsByUserId, createMonitor, diff --git a/Server/models/Check.js b/Server/models/Check.js index aeb4f4849..66984d545 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -57,6 +57,7 @@ const CheckSchema = mongoose.Schema( * * @type {Date} */ + expiry: { type: Date, default: Date.now, diff --git a/Server/package-lock.json b/Server/package-lock.json index 916c66648..6f11e3a2e 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -27,6 +27,7 @@ "nodemailer": "^6.9.14", "ping": "0.4.4", "sharp": "0.33.4", + "ssl-checker": "2.0.10", "winston": "^3.13.0" }, "devDependencies": { @@ -983,9 +984,10 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.2", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.2.tgz", - "integrity": "sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==", + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", + "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", + "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -5026,6 +5028,12 @@ "memory-pager": "^1.0.2" } }, + "node_modules/ssl-checker": { + "version": "2.0.10", + "resolved": "https://registry.npmjs.org/ssl-checker/-/ssl-checker-2.0.10.tgz", + "integrity": "sha512-SS6rrZocToJWHM1p6iVNb583ybB3UqT1fymCHSWuEdXDUqKA6O1D5Fb8KJVmhj3XKXE82IEWcr+idJrc4jUzFQ==", + "license": "MIT" + }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", diff --git a/Server/package.json b/Server/package.json index ad0936654..6c383ce73 100644 --- a/Server/package.json +++ b/Server/package.json @@ -29,6 +29,7 @@ "nodemailer": "^6.9.14", "ping": "0.4.4", "sharp": "0.33.4", + "ssl-checker": "2.0.10", "winston": "^3.13.0" }, "devDependencies": { diff --git a/Server/routes/monitorRoute.js b/Server/routes/monitorRoute.js index ba585affb..847b2b25d 100644 --- a/Server/routes/monitorRoute.js +++ b/Server/routes/monitorRoute.js @@ -5,6 +5,7 @@ const Monitor = require("../models/Monitor"); router.get("/", monitorController.getAllMonitors); router.get("/stats/:monitorId", monitorController.getMonitorStatsById); +router.get("/certificate/:monitorId", monitorController.getMonitorCertificate); router.get("/:monitorId", monitorController.getMonitorById); router.get("/user/:userId", monitorController.getMonitorsByUserId); diff --git a/Server/utils/messages.js b/Server/utils/messages.js index 4e56ec5b7..92f568605 100644 --- a/Server/utils/messages.js +++ b/Server/utils/messages.js @@ -77,6 +77,7 @@ const successMessages = { MONITOR_CREATE: "Monitor created successfully", MONITOR_DELETE: "Monitor deleted successfully", MONITOR_EDIT: "Monitor edited successfully", + MONITOR_CERTIFICATE: "Got monitor certificate successfully", //Job Queue JOB_QUEUE_DELETE_JOB: "Job removed successfully", From 9c980c103a05fd34d949fbabd38b6eb689a0412c Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 12 Aug 2024 15:39:48 -0700 Subject: [PATCH 03/18] Fixed spelling error, moved incident calculation to correct location --- Server/db/mongo/modules/monitorModule.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index 9a0ed7463..7dbcb675e 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -219,6 +219,8 @@ const getMonitorStatsById = async (req) => { }) .limit(limit); + const incidents = getIncidents(dateRangeChecks); + // If more than numToDisplay checks, pick every nth check if ( numToDisplay !== undefined && @@ -233,10 +235,9 @@ const getMonitorStatsById = async (req) => { if (normalize !== undefined) { dateRangeChecks = NormalizeData(dateRangeChecks, 1, 100); } - + // Add common stats - const incidents = getIncidents(dateRangeChecks); - monitorStats.uptimeDuration = caluclteUptimeDuration(checksAll); + monitorStats.uptimeDuration = calculateUptimeDuration(checksAll); monitorStats.lastChecked = getLastChecked(checksAll); monitorStats.latestResponseTime = getLatestResponseTime(checksAll); monitorStats.incidents = incidents; From ca9db41205714d0d0ba3b6d72f538324b86edbe0 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Mon, 12 Aug 2024 19:25:07 -0400 Subject: [PATCH 04/18] Added responsive design to authentication pages --- Client/src/Components/Check/Check.jsx | 2 +- Client/src/Components/Check/check.css | 2 +- Client/src/Pages/Auth/CheckEmail.jsx | 17 +++--- Client/src/Pages/Auth/ForgotPassword.jsx | 17 +++--- Client/src/Pages/Auth/Login.jsx | 19 +++--- .../src/Pages/Auth/NewPasswordConfirmed.jsx | 15 +++-- Client/src/Pages/Auth/Register/Register.jsx | 52 +++++++++++----- Client/src/Pages/Auth/SetNewPassword.jsx | 40 +++++++++--- Client/src/Pages/Auth/index.css | 61 +++++++++++++++++-- Client/src/index.css | 8 --- 10 files changed, 163 insertions(+), 70 deletions(-) diff --git a/Client/src/Components/Check/Check.jsx b/Client/src/Components/Check/Check.jsx index 1b246566f..645e0b5e2 100644 --- a/Client/src/Components/Check/Check.jsx +++ b/Client/src/Components/Check/Check.jsx @@ -46,7 +46,7 @@ const Check = ({ text, variant = "info", outlined = false }) => { }; Check.propTypes = { - text: PropTypes.string.isRequired, + text: PropTypes.oneOfType([PropTypes.string, PropTypes.element]).isRequired, variant: PropTypes.oneOf(["info", "error", "success"]), outlined: PropTypes.bool, }; diff --git a/Client/src/Components/Check/check.css b/Client/src/Components/Check/check.css index cb24be466..f1432d2bc 100644 --- a/Client/src/Components/Check/check.css +++ b/Client/src/Components/Check/check.css @@ -1,4 +1,4 @@ -.check span.MuiTypography-root { +.check > span.MuiTypography-root { font-size: var(--env-var-font-size-small-plus); color: var(--env-var-color-2); opacity: 0.8; diff --git a/Client/src/Pages/Auth/CheckEmail.jsx b/Client/src/Pages/Auth/CheckEmail.jsx index 88a4afa1f..0ede66831 100644 --- a/Client/src/Pages/Auth/CheckEmail.jsx +++ b/Client/src/Pages/Auth/CheckEmail.jsx @@ -86,10 +86,9 @@ const CheckEmail = () => { return ( - background pattern { maxWidth={600} flex={1} justifyContent="center" - p={theme.gap.xl} - pb={theme.gap.triplexl} + px={{ xs: theme.gap.large, lg: theme.gap.xl }} + pb={theme.gap.xl} mx="auto" sx={{ "& > .MuiStack-root": { @@ -121,11 +120,15 @@ const CheckEmail = () => { }, }} > - + Check your email - + We sent a password reset link to{" "} {email || "username@email.com"} diff --git a/Client/src/Pages/Auth/ForgotPassword.jsx b/Client/src/Pages/Auth/ForgotPassword.jsx index e1f6ae56e..c51417c91 100644 --- a/Client/src/Pages/Auth/ForgotPassword.jsx +++ b/Client/src/Pages/Auth/ForgotPassword.jsx @@ -88,10 +88,9 @@ const ForgotPassword = () => { return ( - background pattern { maxWidth={600} flex={1} justifyContent="center" - p={theme.gap.xl} - pb={theme.gap.triplexl} + px={{ xs: theme.gap.large, lg: theme.gap.xl }} + pb={theme.gap.xl} mx="auto" sx={{ "& > .MuiStack-root": { @@ -123,7 +122,11 @@ const ForgotPassword = () => { }, }} > - + Forgot password? @@ -131,7 +134,7 @@ const ForgotPassword = () => { No worries, we'll send you reset instructions. - +
{ return ( <> - + Log In We are pleased to see you again! @@ -82,7 +86,7 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => { return ( <> - + Log In Enter your email address @@ -163,7 +167,7 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => { return ( <> - + Log In Enter your password @@ -336,10 +340,9 @@ const Login = () => { return ( - background pattern { maxWidth={600} flex={1} justifyContent="center" - p={theme.gap.xl} - pb={theme.gap.triplexl} + px={{ xs: theme.gap.large, lg: theme.gap.xl }} + pb={theme.gap.xl} mx="auto" sx={{ "& > .MuiStack-root": { diff --git a/Client/src/Pages/Auth/NewPasswordConfirmed.jsx b/Client/src/Pages/Auth/NewPasswordConfirmed.jsx index 12a6995f9..be6aeb9b5 100644 --- a/Client/src/Pages/Auth/NewPasswordConfirmed.jsx +++ b/Client/src/Pages/Auth/NewPasswordConfirmed.jsx @@ -23,10 +23,9 @@ const NewPasswordConfirmed = () => { return ( - background pattern { maxWidth={600} flex={1} justifyContent="center" - p={theme.gap.xl} - pb={theme.gap.triplexl} + px={{ xs: theme.gap.large, lg: theme.gap.xl }} + pb={theme.gap.xl} mx="auto" sx={{ "& > .MuiStack-root": { @@ -58,7 +57,11 @@ const NewPasswordConfirmed = () => { }, }} > - + Password reset diff --git a/Client/src/Pages/Auth/Register/Register.jsx b/Client/src/Pages/Auth/Register/Register.jsx index 855cc3c54..30b439f16 100644 --- a/Client/src/Pages/Auth/Register/Register.jsx +++ b/Client/src/Pages/Auth/Register/Register.jsx @@ -31,7 +31,11 @@ const LandingPage = ({ isAdmin, onSignup }) => { return ( <> - + Sign Up @@ -87,7 +91,7 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => { return ( <> - + Sign Up Enter your personal details @@ -127,7 +131,6 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => { img={} onClick={onBack} sx={{ - mb: theme.gap.medium, px: theme.gap.ml, "& svg.MuiSvgIcon-root": { mr: theme.gap.xs, @@ -171,7 +174,7 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => { return ( <> - + Sign Up Enter your email address @@ -200,7 +203,6 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => { img={} onClick={onBack} sx={{ - mb: theme.gap.medium, px: theme.gap.ml, "& svg.MuiSvgIcon-root": { mr: theme.gap.xs, @@ -244,7 +246,7 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => { return ( <> - + Sign Up Create your password @@ -277,9 +279,17 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => { error={errors.confirm} /> - + + Must be at least 8 + characters long + + } variant={ errors?.password === "Password is required" ? "error" @@ -291,7 +301,12 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => { } /> + Must contain one + special character and a number + + } variant={ errors?.password === "Password is required" ? "error" @@ -305,7 +320,14 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => { } /> + + Must contain at least + {" "} + one upper and lower character + + } variant={ errors?.password === "Password is required" ? "error" @@ -326,7 +348,6 @@ const StepThree = ({ form, errors, onSubmit, onChange, onBack }) => { img={} onClick={onBack} sx={{ - mb: theme.gap.medium, px: theme.gap.ml, "& svg.MuiSvgIcon-root": { mr: theme.gap.xs, @@ -509,10 +530,9 @@ const Register = ({ isAdmin }) => { return ( - background pattern { maxWidth={600} flex={1} justifyContent="center" - p={theme.gap.xl} - pb={theme.gap.triplexl} + px={{ xs: theme.gap.large, lg: theme.gap.xl }} + pb={theme.gap.xl} mx="auto" sx={{ "& > .MuiStack-root": { diff --git a/Client/src/Pages/Auth/SetNewPassword.jsx b/Client/src/Pages/Auth/SetNewPassword.jsx index 0124f035e..fab448ffa 100644 --- a/Client/src/Pages/Auth/SetNewPassword.jsx +++ b/Client/src/Pages/Auth/SetNewPassword.jsx @@ -105,10 +105,9 @@ const SetNewPassword = () => { return ( - background pattern { maxWidth={600} flex={1} justifyContent="center" - p={theme.gap.xl} - pb={theme.gap.triplexl} + px={{ xs: theme.gap.large, lg: theme.gap.xl }} + pb={theme.gap.large} mx="auto" sx={{ "& > .MuiStack-root": { @@ -140,7 +139,11 @@ const SetNewPassword = () => { }, }} > - + Set new password @@ -175,7 +178,12 @@ const SetNewPassword = () => { + Must be at least 8 + characters long + + } variant={ errors?.password === "Password is required" ? "error" @@ -187,7 +195,12 @@ const SetNewPassword = () => { } /> + Must contain one + special character and a number + + } variant={ errors?.password === "Password is required" ? "error" @@ -201,7 +214,14 @@ const SetNewPassword = () => { } /> + + Must contain at least + {" "} + one upper and lower character + + } variant={ errors?.password === "Password is required" ? "error" @@ -220,7 +240,7 @@ const SetNewPassword = () => { onClick={handleSubmit} level="primary" label="Reset password" - sx={{ width: "100%" }} + sx={{ width: "100%", maxWidth: 400 }} /> diff --git a/Client/src/Pages/Auth/index.css b/Client/src/Pages/Auth/index.css index f7f2c211e..979ae191c 100644 --- a/Client/src/Pages/Auth/index.css +++ b/Client/src/Pages/Auth/index.css @@ -8,10 +8,8 @@ color: var(--env-var-color-3); } .auth button:not(.MuiIconButton-root), -.auth .field h3.MuiTypography-root, -.auth .field input, -.auth .field .input-error, .auth p, +.auth .field input, .auth span { font-size: var(--env-var-font-size-medium-plus); } @@ -55,6 +53,8 @@ width: 24px; height: 24px; } +.auth .field h3.MuiTypography-root, +.auth .field .input-error, .auth .check span.MuiTypography-root { font-size: var(--env-var-font-size-medium); } @@ -66,18 +66,67 @@ cursor: default; } -.auth > .MuiStack-root:nth-of-type(1) { +.auth > .MuiStack-root:nth-of-type(2) { height: var(--env-var-nav-bar-height); } .auth .background-pattern-svg { - top: 14%; + position: absolute; + top: 0; + left: 50%; + transform: translate(-50%, -30%); + z-index: -1; + width: 100%; max-width: 800px; + height: 100%; + max-height: 800px; + + background-position: center; + background-size: cover; + background-repeat: no-repeat; } .forgot-password-page h1, .check-email-page h1, .password-confirmed-page h1, .set-new-password-page h1 { - font-size: var(--env-var-font-size-large-plus); + font-size: 22px; +} + +@media (max-width: 800px) { + .auth h1 { + font-size: var(--env-var-font-size-large-plus); + } + .auth button:not(.MuiIconButton-root), + .auth .field input, + .auth p, + .auth span { + font-size: var(--env-var-font-size-medium); + } + .auth button:not(.MuiIconButton-root), + .auth .field .MuiInputBase-root:has(input) { + height: 36px; + } + .auth .check span.MuiTypography-root, + .auth .field .input-error, + .auth .field h3.MuiTypography-root { + font-size: var(--env-var-font-size-small-plus); + } + .auth .check span > span { + display: none; + } + .auth form + form { + margin: var(--env-var-spacing-1-plus) 0; + } + .auth .background-pattern-svg { + max-width: 750px; + max-height: 750px; + } + + .forgot-password-page h1, + .check-email-page h1, + .password-confirmed-page h1, + .set-new-password-page h1 { + font-size: 18px; + } } diff --git a/Client/src/index.css b/Client/src/index.css index 5693ddaa5..9e404d61c 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -155,14 +155,6 @@ button:focus-visible { border-radius: 4px; } -.background-pattern-svg { - position: absolute; - top: -5%; - left: 50%; - transform: translate(-50%, -50%); - z-index: -1; -} - .MuiTouchRipple-root { display: none; } From f80936dd710db9e81e0b971ae969cb024c601fa7 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 12 Aug 2024 19:43:25 -0700 Subject: [PATCH 05/18] Server status update bug fixed, simplified email notification --- Server/models/Check.js | 63 +++++++++++++++++++----------------------- 1 file changed, 29 insertions(+), 34 deletions(-) diff --git a/Server/models/Check.js b/Server/models/Check.js index 66984d545..a9ac7f288 100644 --- a/Server/models/Check.js +++ b/Server/models/Check.js @@ -85,43 +85,38 @@ CheckSchema.pre("save", async function (next) { }); // Check if there are any notifications - if (notifications && notifications.length > 0) { - // Only send email if monitor status has changed - if (monitor.status !== this.status) { - const emailService = new EmailService(); - if (monitor.status === true && this.status === false) { - // Notify users that the monitor is down - for (const notification of notifications) { - if (notification.type === "email") { - await emailService.buildAndSendEmail( - "serverIsDownTemplate", - { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.address, - `Monitor ${monitor.name} is down` - ); - } - } - } + // Only send email if monitor status has changed + if (monitor.status !== this.status) { + const emailService = new EmailService(); - if (monitor.status === false && this.status === true) { - // Notify users that the monitor is up - for (const notification of notifications) { - if (notification.type === "email") { - await emailService.buildAndSendEmail( - "serverIsUpTemplate", - { monitorName: monitor.name, monitorUrl: monitor.url }, - notification.address, - `Monitor ${monitor.name} is back up` - ); - } - } - } - - // Update monitor status - monitor.status = this.status; - await monitor.save(); + let template = ""; + let status = ""; + if (monitor.status === true && this.status === false) { + template = "serverIsDownTemplate"; + status = "down"; } + + if (monitor.status === false && this.status === true) { + // Notify users that the monitor is up + template = "serverIsUpTemplate"; + status = "up"; + } + + for (const notification of notifications) { + if (notification.type === "email") { + await emailService.buildAndSendEmail( + template, + { monitorName: monitor.name, monitorUrl: monitor.url }, + notification.address, + `Monitor ${monitor.name} is ${status}` + ); + } + } + + // Update monitor status + monitor.status = this.status; + await monitor.save(); } } } catch (error) { From 02668aeb086a873e11e45f88bfe4dcd9a0de37f9 Mon Sep 17 00:00:00 2001 From: "Gorkem Cetin (BWL)" <167266851+gorkem-bwl@users.noreply.github.com> Date: Tue, 13 Aug 2024 10:33:28 -0400 Subject: [PATCH 06/18] Fix roadmap --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d73109925..7c4ed8c25 100644 --- a/README.md +++ b/README.md @@ -24,15 +24,15 @@ BlueWave Uptime is an open source server monitoring application used to track th - [x] Incidents at a glance - [x] Page speed monitoring - [ ] Scheduled maintenance (in the works) -- [ ] Status pages (in the works) **Roadmap (short term):** +- [ ] Memory, disk and CPU monitoring - [ ] 3rd party integrations - [ ] DNS monitoring - [ ] SSL monitoring **Roadmap (long term):** -- [ ] Memory, disk and CPU monitoring using OpenTelemetry +- [ ] Status pages ## Tech stack From 457bbda77c46695b3d15f5a2f755808d708f9408 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Tue, 13 Aug 2024 13:24:35 -0400 Subject: [PATCH 07/18] fix: if no response from server reject with proper payload format --- Client/src/Features/Auth/authSlice.js | 36 +++++++++++++++---- .../PageSpeedMonitor/pageSpeedMonitorSlice.js | 30 +++++++++++++--- .../UptimeMonitors/uptimeMonitorsSlice.js | 30 +++++++++++++--- 3 files changed, 80 insertions(+), 16 deletions(-) diff --git a/Client/src/Features/Auth/authSlice.js b/Client/src/Features/Auth/authSlice.js index a30c295d9..6da315fc4 100644 --- a/Client/src/Features/Auth/authSlice.js +++ b/Client/src/Features/Auth/authSlice.js @@ -20,7 +20,11 @@ export const register = createAsyncThunk( if (error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -33,7 +37,11 @@ export const login = createAsyncThunk("auth/login", async (form, thunkApi) => { if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } }); @@ -72,7 +80,11 @@ export const update = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -91,7 +103,11 @@ export const deleteUser = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -106,7 +122,11 @@ export const forgotPassword = createAsyncThunk( if (error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -128,7 +148,11 @@ export const setNewPassword = createAsyncThunk( if (error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); diff --git a/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js b/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js index 4377edb35..6af825a37 100644 --- a/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js +++ b/Client/src/Features/PageSpeedMonitor/pageSpeedMonitorSlice.js @@ -25,7 +25,11 @@ export const createPageSpeed = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -40,7 +44,11 @@ export const getPageSpeedMonitors = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -63,7 +71,11 @@ export const getPageSpeedByUserId = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -94,7 +106,11 @@ export const updatePageSpeed = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -115,7 +131,11 @@ export const deletePageSpeed = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); diff --git a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js index e69e6d41d..27d616e1e 100644 --- a/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js +++ b/Client/src/Features/UptimeMonitors/uptimeMonitorsSlice.js @@ -25,7 +25,11 @@ export const createUptimeMonitor = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -40,7 +44,11 @@ export const getUptimeMonitors = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -63,7 +71,11 @@ export const getUptimeMonitorsByUserId = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -94,7 +106,11 @@ export const updateUptimeMonitor = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); @@ -115,7 +131,11 @@ export const deleteUptimeMonitor = createAsyncThunk( if (error.response && error.response.data) { return thunkApi.rejectWithValue(error.response.data); } - return thunkApi.rejectWithValue(error.message); + const payload = { + status: false, + msg: error.message ? error.message : "Unknown error", + }; + return thunkApi.rejectWithValue(payload); } } ); From 578eb33b911e4986549a7c503a9d4d9613f57b2f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 13 Aug 2024 10:32:29 -0700 Subject: [PATCH 08/18] Refactored getMonitorStats for clarity --- Client/src/Pages/Monitors/Details/index.jsx | 2 +- Server/db/mongo/modules/monitorModule.js | 48 ++++++++++++--------- Server/validation/joi.js | 2 +- 3 files changed, 29 insertions(+), 23 deletions(-) diff --git a/Client/src/Pages/Monitors/Details/index.jsx b/Client/src/Pages/Monitors/Details/index.jsx index e2c0eb91b..5f3ddf9fc 100644 --- a/Client/src/Pages/Monitors/Details/index.jsx +++ b/Client/src/Pages/Monitors/Details/index.jsx @@ -119,7 +119,7 @@ const DetailsPage = () => { const fetchMonitor = useCallback(async () => { try { const res = await axiosInstance.get( - `/monitors/stats/${monitorId}?filter=${dateRange}&numToDisplay=50&normalize=true`, + `/monitors/stats/${monitorId}?dateRange=${dateRange}&numToDisplay=50&normalize=true`, { headers: { Authorization: `Bearer ${authToken}`, diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index 7dbcb675e..1d53db978 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -149,7 +149,7 @@ const getMonitorStatsById = async (req) => { try { const { monitorId } = req.params; - let { status, limit, sortOrder, filter, numToDisplay, normalize } = + let { status, limit, sortOrder, dateRange, numToDisplay, normalize } = req.query; // This effectively removes limit, returning all checks @@ -161,37 +161,44 @@ const getMonitorStatsById = async (req) => { // Get monitor const monitor = await Monitor.findById(monitorId); + // Determine if this is a pagespeed monitor or an http/ping monitor let model = monitor.type === "http" || monitor.type === "ping" ? Check : PageSpeedCheck; + // Build monitor stats object const monitorStats = { ...monitor.toObject(), }; - // Get all checks to calculate stats + + // Start building query const checksQuery = { monitorId: monitor._id }; + // Get all checks const checksAll = await model.find(checksQuery).sort({ createdAt: sortOrder, }); if (monitor.type === "http" || monitor.type === "ping") { - // Get checks in 24 hours - checksQuery.createdAt = { $gte: filterLookup.day }; - const checks24Hours = await model.find(checksQuery).sort({ - createdAt: sortOrder, - }); - // Get checks in 30 days - checksQuery.createdAt = { $gte: filterLookup.month }; - const checks30Days = await model.find(checksQuery).sort({ - createdAt: sortOrder, - }); - // Get checks in 60 mins - checksQuery.createdAt = { $gte: filterLookup.hour }; - const checks60Mins = await model - .find(checksQuery) - .sort({ createdAt: sortOrder }); + const checksQuery24Hours = { + ...checksQuery, + createdAt: { $gte: filterLookup.day }, + }; + const checksQuery30Days = { + ...checksQuery, + createdAt: { $gte: filterLookup.month }, + }; + const checksQuery60Mins = { + ...checksQuery, + createdAt: { $gte: filterLookup.hour }, + }; + + const [checks24Hours, checks30Days, checks60Mins] = await Promise.all([ + model.find(checksQuery24Hours).sort({ createdAt: sortOrder }), + model.find(checksQuery30Days).sort({ createdAt: sortOrder }), + model.find(checksQuery60Mins).sort({ createdAt: sortOrder }), + ]); // HTTP/PING Specific stats monitorStats.avgResponseTime24hours = @@ -202,14 +209,13 @@ const getMonitorStatsById = async (req) => { } //Get checks for dateRange - if (status !== undefined) { checksQuery.status = status; } // Filter checks by "day", "week", or "month" - if (filter !== undefined) { - checksQuery.createdAt = { $gte: filterLookup[filter] }; + if (dateRange !== undefined) { + checksQuery.createdAt = { $gte: filterLookup[dateRange] }; } let dateRangeChecks = await model @@ -236,7 +242,7 @@ const getMonitorStatsById = async (req) => { dateRangeChecks = NormalizeData(dateRangeChecks, 1, 100); } - // Add common stats + // Add common stats and stats that depend on the dateRange monitorStats.uptimeDuration = calculateUptimeDuration(checksAll); monitorStats.lastChecked = getLastChecked(checksAll); monitorStats.latestResponseTime = getLatestResponseTime(checksAll); diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 74bf5ed92..fe7090083 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -126,7 +126,7 @@ const getMonitorByIdQueryValidation = joi.object({ status: joi.boolean(), sortOrder: joi.string().valid("asc", "desc"), limit: joi.number(), - filter: joi.string().valid("day", "week", "month"), + dateRange: joi.string().valid("day", "week", "month"), numToDisplay: joi.number(), normalize: joi.boolean(), }); From fce178f792663c9109710b0c234847f7b8ebef0a Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Tue, 13 Aug 2024 14:06:17 -0400 Subject: [PATCH 09/18] fix: disabled open site option for ping type monitors --- Client/src/Pages/Monitors/index.jsx | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index e56be39b2..c1e987827 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -120,12 +120,11 @@ const Monitors = () => { }; const closeMenu = () => { setAnchorEl(null); - setActions({}); }; const [isOpen, setIsOpen] = useState(false); const openRemove = () => { - setAnchorEl(null); + closeMenu(); setIsOpen(true); }; const handleRemove = async (event) => { @@ -220,7 +219,11 @@ const Monitors = () => { aria-label="monitor actions" onClick={(event) => { event.stopPropagation(); - openMenu(event, monitor._id, monitor.url); + openMenu( + event, + monitor._id, + monitor.type === "ping" ? null : monitor.url + ); }} sx={{ "&:focus": { @@ -326,13 +329,17 @@ const Monitors = () => { open={Boolean(anchorEl)} onClose={closeMenu} > - { - window.open(actions.url, "_blank", "noreferrer"); - }} - > - Open site - + {actions.url !== null ? ( + { + window.open(actions.url, "_blank", "noreferrer"); + }} + > + Open site + + ) : ( + "" + )} navigate(`/monitors/${actions.id}`)}> Details From 5c1c2d4df78ab0320b729057a4018ede8eb19c91 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 13 Aug 2024 13:29:12 -0700 Subject: [PATCH 10/18] Added better quickstart for devs --- README.md | 60 ++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 7c4ed8c25..0934fcdc1 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ ![](https://img.shields.io/github/issues-pr/bluewave-labs/bluewave-uptime) ![](https://img.shields.io/github/issues/bluewave-labs/bluewave-uptime) -

BlueWave Uptime

An open source server monitoring application

@@ -26,29 +25,31 @@ BlueWave Uptime is an open source server monitoring application used to track th - [ ] Scheduled maintenance (in the works) **Roadmap (short term):** + - [ ] Memory, disk and CPU monitoring - [ ] 3rd party integrations - [ ] DNS monitoring - [ ] SSL monitoring **Roadmap (long term):** + - [ ] Status pages ## Tech stack -* [ReactJs](https://react.dev/) -* [MUI (React framework)](https://mui.com/) -* [Node.js](https://nodejs.org/en) -* [MongoDB](https://mongodb.com) +- [ReactJs](https://react.dev/) +- [MUI (React framework)](https://mui.com/) +- [Node.js](https://nodejs.org/en) +- [MongoDB](https://mongodb.com) ## Contributing We love contributors. Here's how you can contribute: -* Check [Contributor's guideline](https://github.com/bluewave-labs/bluewave-uptime/blob/master/CONTRIBUTING.md). -* Have a look at our Figma designs [here](https://www.figma.com/design/RPSfaw66HjzSwzntKcgDUV/Uptime-Genie?node-id=0-1&t=WqOFv9jqNTFGItpL-1). We encourage you to copy to your own Figma page, then work on it as it is read-only. -* Open an issue if you believe you've encountered a bug -* Make a pull request to add new features/make quality-of-life improvements/fix bugs. +- Check [Contributor's guideline](https://github.com/bluewave-labs/bluewave-uptime/blob/master/CONTRIBUTING.md). +- Have a look at our Figma designs [here](https://www.figma.com/design/RPSfaw66HjzSwzntKcgDUV/Uptime-Genie?node-id=0-1&t=WqOFv9jqNTFGItpL-1). We encourage you to copy to your own Figma page, then work on it as it is read-only. +- Open an issue if you believe you've encountered a bug +- Make a pull request to add new features/make quality-of-life improvements/fix bugs. @@ -58,12 +59,12 @@ Made with [contrib.rocks](https://contrib.rocks). ![Alt](https://repobeats.axiom.co/api/embed/c35d999c82dbb31e967427ea4166c14da4172e73.svg "Repobeats analytics image") - ## Getting Started - Clone this repository to your local machine -1. [Docker Quickstart](#docker-quickstart) +1. [Quickstart for Developers](#dev-quickstart) +1. [Docker Compose](#docker-compose) 1. [Installation (Client)](#client) 1. [Configuration(Client)](#config-client) - [Environment](#env-vars-client) @@ -117,9 +118,41 @@ Made with [contrib.rocks](https://contrib.rocks). --- -### Docker Quick Start +#### Quickstart for Developers -#### Docker Quickstart +# PLEASE MAKE SURE YOU CD TO THE SPECIFIED DIRECTORIES + +##### Cloning and Initial Setup + +1. Clone this repository +2. Checkout the `develop` branch `git checkout develop` + +##### Docker Images Setup + +3. CD to the `Docker` directory +4. Run `docker run -d -p 6379:6379 -v $(pwd)/redis/data:/data --name uptime_redis uptime_redis` +5. Run `docker run -d -p 27017:27017 -v $(pwd)/mongo/data:/data/db --name uptime_database_mongo uptime_database_mongo` + +##### Server Setup + +6. CD to `Server` directory, run `npm install` +7. While in `Server` directory, create a `.env` file with the [required environmental variables](#env-vars-server) +8. While in the `Server` directory, run `npm run dev` + +##### Client Setup + +9. CD to `Client` directory `run npm install` +10. While in the `Client` directory, create a `.env` file with the [required environmental variables](#env-vars-client) +11. While in the `Client` cirectory run `npm run dev` + +##### Access Application + +12. Client is running at `localhost:5173` +13. Server is running at `localhost:5000` + +--- + +#### Docker Compose The fastest way to start the application is to use our Dockerfiles and [Docker Compose](https://docs.docker.com/compose/). @@ -2112,4 +2145,3 @@ const myRoute = async(req, res, next) => { ``` Errors should not be handled at the controller level and should be left to the middleware to handle. - From 4fac8377021e670520c14361c13b8dd664ec9604 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 13 Aug 2024 13:46:25 -0700 Subject: [PATCH 11/18] Fix docker-compose instructions --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 0934fcdc1..f62640e93 100644 --- a/README.md +++ b/README.md @@ -120,7 +120,7 @@ Made with [contrib.rocks](https://contrib.rocks). #### Quickstart for Developers -# PLEASE MAKE SURE YOU CD TO THE SPECIFIED DIRECTORIES +MAKE SURE YOU CD TO THE SPECIFIED DIRECTORIES AS PATHS IN COMMANDS ARE RELATIVE ##### Cloning and Initial Setup @@ -176,7 +176,7 @@ SYSTEM_EMAIL_ADDRESS= SYSTEM_EMAIL_PASSWORD= ``` -3. In the `Docker` directory, create a `client.env` file with the [required environtmental variables](#env-vars-client) for the client. Sample file: +3. In the `Client` directory, create a `client.env` file with the [required environtmental variables](#env-vars-client) for the client. Sample file: ``` VITE_APP_API_BASE_URL="http://localhost:5000/api/v1" From 8987f4c72a6fa9489b7f0f4e75c4ec4dc31bec59 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 13 Aug 2024 13:46:59 -0700 Subject: [PATCH 12/18] Fixed port --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f62640e93..2d0db36fc 100644 --- a/README.md +++ b/README.md @@ -184,7 +184,7 @@ VITE_APP_API_BASE_URL="http://localhost:5000/api/v1" 4. In the `Docker` directory run `docker compose up` to run the `docker-compose.yaml` file and start all four images. -That's it, the application is ready to use on port 5173. +That's it, the application is ready to use on port 80.
### Client From f1289c0b6c9adb05bc5f7cd2795be81fb715954f Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Tue, 13 Aug 2024 17:22:21 -0400 Subject: [PATCH 13/18] redesign app layout: replaced css grid with flexbox --- Client/src/Layouts/HomeLayout/index.css | 71 +++++++++---------------- Client/src/Layouts/HomeLayout/index.jsx | 2 - Client/src/Pages/Monitors/index.jsx | 8 ++- Client/src/Utils/Theme.js | 3 +- 4 files changed, 34 insertions(+), 50 deletions(-) diff --git a/Client/src/Layouts/HomeLayout/index.css b/Client/src/Layouts/HomeLayout/index.css index 048700db6..28b4dd0f8 100644 --- a/Client/src/Layouts/HomeLayout/index.css +++ b/Client/src/Layouts/HomeLayout/index.css @@ -1,55 +1,36 @@ -.home-layout { - display: grid; - position: relative; - grid-template-areas: - "aside header" - "aside main"; - grid-template-columns: var(--env-var-side-bar-width) auto; - grid-template-rows: var(--env-var-nav-bar-height) auto; +#root:has(.home-layout) { background-color: var(--env-var-color-30); +} + +.home-layout { + display: flex; + position: relative; + gap: var(--env-var-spacing-2); min-height: 100vh; + max-width: 1400px; + margin: 0 auto; + padding: var(--env-var-spacing-2); } .home-layout aside { - grid-area: aside; - position: fixed; - top: 0; - height: 100%; - width: var(--env-var-side-bar-width); + position: sticky; + top: var(--env-var-spacing-2); + left: 0; + + height: calc(100vh - var(--env-var-spacing-2) * 2); + max-width: var(--env-var-side-bar-width); + flex: 1; + + border: 1px solid var(--color-border-0); + border-radius: var(--env-var-radius-1); background-color: var(--env-var-color-8); - border-right: 1px solid var(--color-border-0); + padding: var(--env-var-spacing-1) var(--env-var-spacing-1-plus); } -.home-layout header { - grid-area: header; - background-color: var(--env-var-color-30); -} + .home-layout > div { - height: 100%; - grid-area: main; - padding: var(--env-var-spacing-2) calc(var(--env-var-spacing-4) * 2); - padding-bottom: var(--env-var-spacing-4); - max-width: 100%; -} -.home-layout > div:not(:has([class*="fallback"])) { - max-width: 1500px; -} - -@media (max-width: 600px) { - .home-layout { - grid-template-areas: - "header header" - "main main"; - grid-template-columns: 1fr; - grid-template-rows: 64px auto; - } - - aside { - display: none; - visibility: hidden; - } - - .home-layout > div { - padding: var(--env-var-spacing-1); - } + min-height: calc(100vh - var(--env-var-spacing-2) * 2); + padding-top: var(--env-var-spacing-4); + width: 100%; + flex: 1; } diff --git a/Client/src/Layouts/HomeLayout/index.jsx b/Client/src/Layouts/HomeLayout/index.jsx index 007fb7d05..81cb2fd9c 100644 --- a/Client/src/Layouts/HomeLayout/index.jsx +++ b/Client/src/Layouts/HomeLayout/index.jsx @@ -1,4 +1,3 @@ -import NavBar from "../../Components/NavBar"; import Sidebar from "../../Components/Sidebar"; import { Outlet } from "react-router"; import { Box } from "@mui/material"; @@ -9,7 +8,6 @@ const HomeLayout = () => { return ( - ); diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index e56be39b2..5a37ecb49 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -250,7 +250,10 @@ const Monitors = () => { justifyContent="space-between" alignItems="center" > - + Hello, {authState.user.firstName} {monitorState.monitors?.length !== 0 && ( @@ -299,7 +302,8 @@ const Monitors = () => {
Date: Tue, 13 Aug 2024 17:39:42 -0400 Subject: [PATCH 14/18] redesign app layout: adjusted padding and margin values --- Client/src/Layouts/HomeLayout/index.css | 2 -- Client/src/Pages/Incidents/index.jsx | 2 +- Client/src/Pages/Integrations/index.jsx | 2 +- Client/src/Pages/Monitors/Configure/index.css | 2 +- Client/src/Pages/Monitors/CreateMonitor/index.css | 2 +- Client/src/Pages/Monitors/index.jsx | 2 +- Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx | 4 +--- Client/src/Pages/PageSpeed/Details/index.jsx | 4 ++-- Client/src/Pages/PageSpeed/index.jsx | 2 +- 9 files changed, 9 insertions(+), 13 deletions(-) diff --git a/Client/src/Layouts/HomeLayout/index.css b/Client/src/Layouts/HomeLayout/index.css index 28b4dd0f8..a62016a60 100644 --- a/Client/src/Layouts/HomeLayout/index.css +++ b/Client/src/Layouts/HomeLayout/index.css @@ -30,7 +30,5 @@ .home-layout > div { min-height: calc(100vh - var(--env-var-spacing-2) * 2); - padding-top: var(--env-var-spacing-4); - width: 100%; flex: 1; } diff --git a/Client/src/Pages/Incidents/index.jsx b/Client/src/Pages/Incidents/index.jsx index 515f8ec0c..f9d53f7c0 100644 --- a/Client/src/Pages/Incidents/index.jsx +++ b/Client/src/Pages/Incidents/index.jsx @@ -78,7 +78,7 @@ const Incidents = () => { }; return ( - + {loading ? ( ) : ( diff --git a/Client/src/Pages/Integrations/index.jsx b/Client/src/Pages/Integrations/index.jsx index f7d693948..278754fea 100644 --- a/Client/src/Pages/Integrations/index.jsx +++ b/Client/src/Pages/Integrations/index.jsx @@ -108,7 +108,7 @@ const Integrations = () => { ]; return ( - + Integrations Connect BlueWave Uptime to your favorite service. diff --git a/Client/src/Pages/Monitors/Configure/index.css b/Client/src/Pages/Monitors/Configure/index.css index a594348ab..ac9cd8a0d 100644 --- a/Client/src/Pages/Monitors/Configure/index.css +++ b/Client/src/Pages/Monitors/Configure/index.css @@ -75,7 +75,7 @@ .configure-monitor-form { display: flex; flex-direction: column; - gap: var(--env-var-spacing-4); + gap: var(--env-var-spacing-2); } body:has(.configure-monitor) .select-dropdown .MuiMenuItem-root, diff --git a/Client/src/Pages/Monitors/CreateMonitor/index.css b/Client/src/Pages/Monitors/CreateMonitor/index.css index 1eb542a44..3fa38f14a 100644 --- a/Client/src/Pages/Monitors/CreateMonitor/index.css +++ b/Client/src/Pages/Monitors/CreateMonitor/index.css @@ -59,7 +59,7 @@ .create-monitor-form { display: flex; flex-direction: column; - gap: var(--env-var-spacing-4); + gap: var(--env-var-spacing-2); } .create-monitor .MuiStack-root .MuiButtonGroup-root button { diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index 5a37ecb49..95e93755f 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -240,7 +240,7 @@ const Monitors = () => { let loading = monitorState.isLoading && monitorState.monitors.length === 0; return ( - + {loading ? ( ) : ( diff --git a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx index cb82310c4..e9b271326 100644 --- a/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx +++ b/Client/src/Pages/PageSpeed/CreatePageSpeed/index.jsx @@ -106,7 +106,7 @@ const CreatePageSpeed = () => { onClick={() => navigate("/pagespeed")} sx={{ backgroundColor: theme.palette.otherColors.fillGray, - mb: theme.gap.large, + mb: theme.gap.medium, px: theme.gap.ml, "& svg.MuiSvgIcon-root": { mr: theme.gap.small, @@ -122,8 +122,6 @@ const CreatePageSpeed = () => { display: "flex", flexDirection: "column", gap: theme.gap.large, - // TODO - maxWidth: "1000px", }} > Create a page speed monitor diff --git a/Client/src/Pages/PageSpeed/Details/index.jsx b/Client/src/Pages/PageSpeed/Details/index.jsx index 963b0b31f..ed8c942af 100644 --- a/Client/src/Pages/PageSpeed/Details/index.jsx +++ b/Client/src/Pages/PageSpeed/Details/index.jsx @@ -363,7 +363,7 @@ const PageSpeedDetails = () => { {
Performance report - + { let isActuallyLoading = isLoading && monitors.length === 0; return ( - + {isActuallyLoading ? ( ) : monitors?.length !== 0 ? ( From baeacd15a23109e451466f48e0137c65253dd5d4 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Tue, 13 Aug 2024 20:17:18 -0400 Subject: [PATCH 15/18] redesign app layout: removed navbar and added profile menu to sidebar --- Client/src/Components/Avatar/index.jsx | 7 +- Client/src/Components/NavBar/index.css | 34 ---- Client/src/Components/NavBar/index.jsx | 203 ------------------------ Client/src/Components/Sidebar/index.css | 39 ++++- Client/src/Components/Sidebar/index.jsx | 141 +++++++++++++++- Client/src/Pages/Account/index.jsx | 2 +- Client/src/assets/icons/down-arrow.svg | 3 + 7 files changed, 182 insertions(+), 247 deletions(-) delete mode 100644 Client/src/Components/NavBar/index.css delete mode 100644 Client/src/Components/NavBar/index.jsx create mode 100644 Client/src/assets/icons/down-arrow.svg diff --git a/Client/src/Components/Avatar/index.jsx b/Client/src/Components/Avatar/index.jsx index 4f4351508..6c4bf80c7 100644 --- a/Client/src/Components/Avatar/index.jsx +++ b/Client/src/Components/Avatar/index.jsx @@ -18,7 +18,7 @@ import { useEffect, useState } from "react"; const Avatar = ({ src, small, sx }) => { const { user } = useSelector((state) => state.auth); - const style = small ? { width: 25, height: 25 } : { width: 64, height: 64 }; + const style = small ? { width: 32, height: 32 } : { width: 64, height: 64 }; const border = small ? 1 : 3; const [image, setImage] = useState(); @@ -35,7 +35,7 @@ const Avatar = ({ src, small, sx }) => { src ? src : user?.avatarImage ? image : "/static/images/avatar/2.jpg" } sx={{ - fontSize: small ? "13px" : "22px", + fontSize: small ? "16px" : "22px", fontWeight: 400, display: "inline-flex", "&::before": { @@ -52,7 +52,8 @@ const Avatar = ({ src, small, sx }) => { ...sx, }} > - {user.firstName?.charAt(0)}{user.lastName?.charAt(0)} + {user.firstName?.charAt(0)} + {!small && user.lastName?.charAt(0)} ); }; diff --git a/Client/src/Components/NavBar/index.css b/Client/src/Components/NavBar/index.css deleted file mode 100644 index fcedc53a2..000000000 --- a/Client/src/Components/NavBar/index.css +++ /dev/null @@ -1,34 +0,0 @@ -/* NavBar Component Styles*/ -.MuiToolbar-root { - min-height: var(--env-var-nav-bar-height) !important; -} - -.icon-button-toggle-title { - font-size: var(--env-var-font-size-medium); - color: var(--env-var-color-5); -} - -.icon-button-toggle-pic { - width: 10px; - height: 5px; -} - -#icon-button { - -webkit-transition: none; - transition: none; - outline: none; - box-shadow: none; - background-color: transparent; -} - -#menu-appbar svg { - width: 16px; - height: 16px; - color: var(--env-var-color-25); -} - -#bw-uptime-logo-dashboard { - width: fit-content; - height: 16px; - margin-left: 15px; -} diff --git a/Client/src/Components/NavBar/index.jsx b/Client/src/Components/NavBar/index.jsx deleted file mode 100644 index 95123d975..000000000 --- a/Client/src/Components/NavBar/index.jsx +++ /dev/null @@ -1,203 +0,0 @@ -import "./index.css"; -import { cloneElement, useState } from "react"; -import AppBar from "@mui/material/AppBar"; -import Box from "@mui/material/Box"; -import Toolbar from "@mui/material/Toolbar"; -import IconButton from "@mui/material/IconButton"; -import Typography from "@mui/material/Typography"; -import Menu from "@mui/material/Menu"; -import Avatar from "../Avatar"; -import Tooltip from "@mui/material/Tooltip"; -import MenuItem from "@mui/material/MenuItem"; -import { useTheme } from "@mui/material/styles"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import { clearAuthState } from "../../Features/Auth/authSlice"; -import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; - -import { useDispatch, useSelector } from "react-redux"; -import { useNavigate } from "react-router-dom"; -import LockSvg from "../../assets/icons/lock.svg?react"; -import UserSvg from "../../assets/icons/user.svg?react"; -import TeamSvg from "../../assets/icons/user-two.svg?react"; -import LogoutSvg from "../../assets/icons/logout.svg?react"; -import { Stack, useScrollTrigger } from "@mui/material"; -import axiosIntance from "../../Utils/axiosConfig"; -import axios from "axios"; - -const icons = { - Profile: , - Team: , - Password: , - Logout: , -}; - -function AddBorderOnScroll(props) { - const { children, window } = props; - const trigger = useScrollTrigger({ - target: window ? window() : undefined, - disableHysteresis: true, - threshold: 0, - }); - - return ( - - {children} - - ); -} - -/** - * NavBar component - * - * A responsive navigation bar component with a user menu. - * - * @component - * @example - * return ( - * - * ) - */ -function NavBar() { - const theme = useTheme(); - const [anchorElUser, setAnchorElUser] = useState(null); - const dispatch = useDispatch(); - const navigate = useNavigate(); - const authState = useSelector((state) => state.auth); - - // Initialize settings and update based on user role - let settings = ["Profile", "Password", "Team", "Logout"]; - if (authState.user?.role && !authState.user.role.includes("admin")) { - settings = ["Profile", "Password", "Logout"]; - } - - /** - * Handles opening the user menu. - * - * @param {React.MouseEvent} event - The event triggered by clicking the user menu button. - */ - const handleOpenUserMenu = (event) => { - setAnchorElUser(event.currentTarget); - }; - - /** - * Handles logging out the user - * - */ - const logout = async () => { - // Clear auth state - dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); - // Make request to BE to remove JWT from user - await axiosIntance.post( - "/auth/logout", - { email: authState.user.email }, - { - headers: { - Authorization: `Bearer ${authState.authToken}`, - "Content-Type": "application/json", - }, - } - ); - navigate("/login"); - }; - - /** - * Handles closing the user menu. - */ - const handleCloseUserMenu = (setting) => { - setAnchorElUser(null); - switch (setting) { - case "Profile": - navigate("/account/profile"); - break; - case "Team": - navigate("/account/team"); - break; - case "Password": - navigate("/account/password"); - break; - case "Logout": - logout(); - break; - default: - break; - } - }; - - return ( - - - - - - - - {authState.user?.firstName} {authState.user?.lastName} - - - - - - - {settings.map((setting) => ( - handleCloseUserMenu(setting)} - sx={{ width: "150px" }} - > - {icons[setting]} - - {setting} - - - ))} - - - - ); -} - -export default NavBar; diff --git a/Client/src/Components/Sidebar/index.css b/Client/src/Components/Sidebar/index.css index 6fa9d2134..4840043c3 100644 --- a/Client/src/Components/Sidebar/index.css +++ b/Client/src/Components/Sidebar/index.css @@ -18,11 +18,14 @@ aside span.MuiTypography-root { color: var(--env-var-color-5); line-height: 1; } -aside .MuiStack-root:nth-last-child(2) { +aside .MuiStack-root:nth-last-child(3) { margin-top: auto; +} +aside .MuiStack-root:last-child, +aside .MuiStack-root:nth-last-child(3) { position: relative; } -aside .MuiStack-root:nth-last-child(2):before { +aside .MuiStack-root:last-child:before { content: ""; position: absolute; top: -10px; @@ -31,5 +34,35 @@ aside .MuiStack-root:nth-last-child(2):before { border-top: solid 1px var(--env-var-color-6); } aside .MuiStack-root:last-child { - margin-bottom: 10px; + margin-top: 15px; +} + +.sidebar-menu { + margin-top: -20px; +} +.sidebar-menu .MuiPaper-root { + box-shadow: var(--env-var-shadow-1); + border: solid 1px var(--env-var-color-6); + border-radius: var(--env-var-radius-1); + gap: 1px; +} +.sidebar-menu .MuiList-root { + min-width: 100px; + width: 150px; + padding-bottom: 0; +} +.sidebar-menu li.MuiButtonBase-root:last-child { + border-top: solid 1px var(--env-var-color-6); + padding: 12px 16px; +} +.sidebar-menu li.MuiButtonBase-root { + min-height: fit-content; +} +.sidebar-menu .MuiList-root svg { + width: 16px; + height: 16px; +} +.sidebar-menu span { + font-size: var(--env-var-font-size-medium); + color: var(--env-var-color-2); } diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index 3b2a32394..804704e07 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -1,7 +1,15 @@ -import { Stack, Typography } from "@mui/material"; +import { useState } from "react"; +import { Box, Menu, MenuItem, Stack, Tooltip, Typography } from "@mui/material"; import { useLocation, useNavigate } from "react-router"; import { useTheme } from "@emotion/react"; - +import { useDispatch, useSelector } from "react-redux"; +import { clearAuthState } from "../../Features/Auth/authSlice"; +import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import Avatar from "../Avatar"; +import LockSvg from "../../assets/icons/lock.svg?react"; +import UserSvg from "../../assets/icons/user.svg?react"; +import TeamSvg from "../../assets/icons/user-two.svg?react"; +import LogoutSvg from "../../assets/icons/logout.svg?react"; import BWULogo from "../../assets/Images/bwl-logo.svg?react"; import Support from "../../assets/icons/support.svg?react"; import StatusPages from "../../assets/icons/status-pages.svg?react"; @@ -11,6 +19,7 @@ import Incidents from "../../assets/icons/incidents.svg?react"; import Integrations from "../../assets/icons/integrations.svg?react"; import PageSpeed from "../../assets/icons/page-speed.svg?react"; import Settings from "../../assets/icons/settings.svg?react"; +import Arrow from "../../assets/icons/down-arrow.svg?react"; import "./index.css"; @@ -32,10 +41,80 @@ const menu = [ { name: "Settings", path: "settings", icon: }, ]; +const icons = { + Profile: , + Team: , + Password: , + Logout: , +}; + function Sidebar() { const theme = useTheme(); const navigate = useNavigate(); const location = useLocation(); + const dispatch = useDispatch(); + const [anchorElUser, setAnchorElUser] = useState(null); + const authState = useSelector((state) => state.auth); + + // Initialize settings and update based on user role + let settings = ["Profile", "Password", "Team", "Logout"]; + if (authState.user?.role && !authState.user.role.includes("admin")) { + settings = ["Profile", "Password", "Logout"]; + } + + /** + * Handles opening the user menu. + * + * @param {React.MouseEvent} event - The event triggered by clicking the user menu button. + */ + const handleOpenUserMenu = (event) => { + setAnchorElUser(event.currentTarget); + }; + + /** + * Handles logging out the user + * + */ + const logout = async () => { + // Clear auth state + dispatch(clearAuthState()); + dispatch(clearUptimeMonitorState()); + // Make request to BE to remove JWT from user + await axiosIntance.post( + "/auth/logout", + { email: authState.user.email }, + { + headers: { + Authorization: `Bearer ${authState.authToken}`, + "Content-Type": "application/json", + }, + } + ); + navigate("/login"); + }; + + /** + * Handles closing the user menu. + */ + const handleCloseUserMenu = (setting) => { + setAnchorElUser(null); + switch (setting) { + case "Profile": + navigate("/account/profile"); + break; + case "Team": + navigate("/account/team"); + break; + case "Password": + navigate("/account/password"); + break; + case "Logout": + logout(); + break; + default: + break; + } + }; return ( @@ -48,6 +127,8 @@ function Sidebar() { key={item.path} direction="row" alignItems="center" + py={theme.gap.small} + px={theme.gap.medium} gap={theme.gap.small} borderRadius={`${theme.shape.borderRadius}px`} onClick={() => @@ -59,12 +140,66 @@ function Sidebar() { ) : navigate(`/${item.path}`) } - sx={{ p: `${theme.gap.small} ${theme.gap.medium}` }} > {item.icon} {item.name} ))} + + + + + {authState.user?.firstName} {authState.user?.lastName} + + + + + + {settings.map((setting) => ( + handleCloseUserMenu(setting)}> + {icons[setting]} + + {setting} + + + ))} + ); } diff --git a/Client/src/Pages/Account/index.jsx b/Client/src/Pages/Account/index.jsx index db08ec3a2..82f37a969 100644 --- a/Client/src/Pages/Account/index.jsx +++ b/Client/src/Pages/Account/index.jsx @@ -29,7 +29,7 @@ const Account = ({ open = "profile" }) => { if (!user.role.includes("admin")) tabList = ["Profile", "Password"]; return ( - + + + From 826779dcd0b3f0032b1b0512ff4d745833fb42dd Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Tue, 13 Aug 2024 20:24:29 -0400 Subject: [PATCH 16/18] redesign app layout: set different background colors to avatars depending on user's name --- Client/src/Components/Avatar/index.jsx | 24 +++++++++++++++++++++++- Client/src/Components/Sidebar/index.jsx | 16 ++++++++-------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/Client/src/Components/Avatar/index.jsx b/Client/src/Components/Avatar/index.jsx index 6c4bf80c7..2a9f70a08 100644 --- a/Client/src/Components/Avatar/index.jsx +++ b/Client/src/Components/Avatar/index.jsx @@ -3,6 +3,27 @@ import PropTypes from "prop-types"; import { useSelector } from "react-redux"; import { useEffect, useState } from "react"; +/** + * Generates a color based on the input string. + * @param {string} string - The input string to generate the color from. + * @returns {string} + */ +const stringToColor = (string) => { + let hash = 0; + let i; + for (i = 0; i < string.length; i += 1) { + hash = string.charCodeAt(i) + ((hash << 5) - hash); + } + + let color = "#"; + for (i = 0; i < 3; i += 1) { + const value = (hash >> (i * 8)) & 0xff; + color += `00${value.toString(16)}`.slice(-2); + } + + return color; +}; + /** * @component * @param {Object} props @@ -37,6 +58,7 @@ const Avatar = ({ src, small, sx }) => { sx={{ fontSize: small ? "16px" : "22px", fontWeight: 400, + backgroundColor: stringToColor(`${user?.firstName} ${user?.lastName}`), display: "inline-flex", "&::before": { content: `""`, @@ -53,7 +75,7 @@ const Avatar = ({ src, small, sx }) => { }} > {user.firstName?.charAt(0)} - {!small && user.lastName?.charAt(0)} + {user.lastName?.charAt(0)} ); }; diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index 804704e07..6b1a6aa09 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -23,13 +23,6 @@ import Arrow from "../../assets/icons/down-arrow.svg?react"; import "./index.css"; -/** - * @component - * Sidebar component serves as a sidebar containing a menu. - * - * @returns {JSX.Element} The JSX element representing the Sidebar component. - */ - const menu = [ { name: "Monitors", path: "monitors", icon: }, { name: "Incidents", path: "incidents", icon: }, @@ -48,6 +41,13 @@ const icons = { Logout: , }; +/** + * @component + * Sidebar component serves as a sidebar containing a menu. + * + * @returns {JSX.Element} The JSX element representing the Sidebar component. + */ + function Sidebar() { const theme = useTheme(); const navigate = useNavigate(); @@ -173,7 +173,7 @@ function Sidebar() { {authState.user?.firstName} {authState.user?.lastName} - + Date: Wed, 14 Aug 2024 02:50:50 +0000 Subject: [PATCH 17/18] Bump axios from 1.7.3 to 1.7.4 in /Server Bumps [axios](https://github.com/axios/axios) from 1.7.3 to 1.7.4. - [Release notes](https://github.com/axios/axios/releases) - [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md) - [Commits](https://github.com/axios/axios/compare/v1.7.3...v1.7.4) --- updated-dependencies: - dependency-name: axios dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- Server/package-lock.json | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Server/package-lock.json b/Server/package-lock.json index 6f11e3a2e..d2ad88cff 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -984,10 +984,9 @@ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.3.tgz", - "integrity": "sha512-Ar7ND9pU99eJ9GpoGQKhKf58GpUOgnzuaB7ueNQ5BMi0p+LZ5oaEnfF999fAArcTIBwXTCHAmGcHOZJaWPq9Nw==", - "license": "MIT", + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", + "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", From 2b803ee7c26876028827c2457b81f3bfe73d8566 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Wed, 14 Aug 2024 00:47:33 -0400 Subject: [PATCH 18/18] fix: added missing import and try catch block --- Client/src/Components/Sidebar/index.jsx | 41 +++++++++++++++---------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index 6b1a6aa09..5a5b1d980 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -1,10 +1,12 @@ import { useState } from "react"; -import { Box, Menu, MenuItem, Stack, Tooltip, Typography } from "@mui/material"; +import { Menu, MenuItem, Stack, Tooltip, Typography } from "@mui/material"; import { useLocation, useNavigate } from "react-router"; import { useTheme } from "@emotion/react"; import { useDispatch, useSelector } from "react-redux"; import { clearAuthState } from "../../Features/Auth/authSlice"; import { clearUptimeMonitorState } from "../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import { createToast } from "../../Utils/toastUtils"; +import axiosInstance from "../../Utils/axiosConfig"; import Avatar from "../Avatar"; import LockSvg from "../../assets/icons/lock.svg?react"; import UserSvg from "../../assets/icons/user.svg?react"; @@ -76,21 +78,28 @@ function Sidebar() { * */ const logout = async () => { - // Clear auth state - dispatch(clearAuthState()); - dispatch(clearUptimeMonitorState()); - // Make request to BE to remove JWT from user - await axiosIntance.post( - "/auth/logout", - { email: authState.user.email }, - { - headers: { - Authorization: `Bearer ${authState.authToken}`, - "Content-Type": "application/json", - }, - } - ); - navigate("/login"); + try { + // Make request to BE to remove JWT from user + await axiosInstance.post( + "/auth/logout", + { email: authState.user.email }, + { + headers: { + Authorization: `Bearer ${authState.authToken}`, + "Content-Type": "application/json", + }, + } + ); + + // Clear auth state + dispatch(clearAuthState()); + dispatch(clearUptimeMonitorState()); + navigate("/login"); + } catch (error) { + createToast({ + body: error.message, + }); + } }; /**