From 1bd8676d942253f2e063347fb9e0dc24a712dd88 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 21 Aug 2024 09:51:00 -0700 Subject: [PATCH 01/32] Add safe accesss default for rowsPerPage, add proptypes --- Client/src/Components/BasicTable/index.jsx | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/Client/src/Components/BasicTable/index.jsx b/Client/src/Components/BasicTable/index.jsx index 8b6cafd91..17ba781f2 100644 --- a/Client/src/Components/BasicTable/index.jsx +++ b/Client/src/Components/BasicTable/index.jsx @@ -15,7 +15,7 @@ import { Stack, } from "@mui/material"; import { useDispatch, useSelector } from "react-redux"; -import { useLocation, useParams } from "react-router-dom"; +import { useLocation } from "react-router-dom"; import { setRowsPerPage } from "../../Features/UI/uiSlice"; import LeftArrowDouble from "../../assets/icons/left-arrow-double.svg?react"; import RightArrowDouble from "../../assets/icons/right-arrow-double.svg?react"; @@ -90,6 +90,13 @@ const TablePaginationActions = (props) => { ); }; +TablePaginationActions.propTypes = { + count: PropTypes.number.isRequired, + page: PropTypes.number.isRequired, + rowsPerPage: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, +}; + /** * BasicTable Component * Renders a table with optional pagination. @@ -144,12 +151,13 @@ const TablePaginationActions = (props) => { */ const BasicTable = ({ data, paginated, reversed }) => { + const DEFAULT_ROWS_PER_PAGE = 5; const theme = useTheme(); const location = useLocation(); const dispatch = useDispatch(); const uiState = useSelector((state) => state.ui); let table = location.pathname.split("/").pop(); - let rowsPerPage = uiState[table].rowsPerPage; + let rowsPerPage = uiState?.[table]?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE; const [page, setPage] = useState(0); useEffect(() => { From 33b12e578151a94313a107d380ae585cd47a1a09 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 21 Aug 2024 10:58:40 -0700 Subject: [PATCH 02/32] Add TOS and Privacy Policy links --- Client/src/Pages/Auth/Login.jsx | 22 +++++++++++++++++++-- Client/src/Pages/Auth/Register/Register.jsx | 22 +++++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/Client/src/Pages/Auth/Login.jsx b/Client/src/Pages/Auth/Login.jsx index 00de688f6..765cb7a76 100644 --- a/Client/src/Pages/Auth/Login.jsx +++ b/Client/src/Pages/Auth/Login.jsx @@ -55,8 +55,26 @@ const LandingPage = ({ onContinue }) => { By continuing, you agree to our{" "} - Terms of Service and{" "} - Privacy Policy. + { + window.open( + "https://bluewavelabs.ca/terms-of-service-open-source", + "_blank" + ); + }} + > + Terms of Service + {" "} + and{" "} + { + window.open("https://bluewavelabs.ca/privacy-policy", "_blank"); + }} + > + Privacy Policy. + diff --git a/Client/src/Pages/Auth/Register/Register.jsx b/Client/src/Pages/Auth/Register/Register.jsx index 046aad64b..958a61b22 100644 --- a/Client/src/Pages/Auth/Register/Register.jsx +++ b/Client/src/Pages/Auth/Register/Register.jsx @@ -60,8 +60,26 @@ const LandingPage = ({ isAdmin, onSignup }) => { By signing up, you agree to our{" "} - Terms of Service and{" "} - Privacy Policy. + { + window.open( + "https://bluewavelabs.ca/terms-of-service-open-source", + "_blank" + ); + }} + > + Terms of Service + {" "} + and{" "} + { + window.open("https://bluewavelabs.ca/privacy-policy", "_blank"); + }} + > + Privacy Policy. + From 4e430547ab0c7b02bfd2d9ecd9d31fa343d26a5f Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 21 Aug 2024 11:41:47 -0700 Subject: [PATCH 03/32] Add error handling and logging to email service --- Client/src/Utils/NetworkService.js | 6 +++ Server/controllers/authController.js | 4 +- Server/service/emailService.js | 77 ++++++++++++++++++++-------- 3 files changed, 64 insertions(+), 23 deletions(-) diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index 32fcb6aa8..de4508cff 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -231,6 +231,12 @@ class NetworkService { }); } + async deleteUser(authToken, userId) { + return this.axiosInstance.delete(`/auth/user/${userId}`, { + headers: { Authorization: `Bearer ${authToken}` }, + }); + } + /** * ************************************ * Forgot password request diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 4b5f51121..6fbcdb669 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -90,7 +90,7 @@ const registerController = async (req, res, next) => { const token = issueToken(userForToken); - req.emailService.buildAndSendEmail( + await req.emailService.buildAndSendEmail( "welcomeEmailTemplate", { name: newUser.firstName }, newUser.email, @@ -240,7 +240,7 @@ const inviteController = async (req, res, next) => { } const inviteToken = await req.db.requestInviteToken(req, res); - req.emailService.buildAndSendEmail( + await req.emailService.buildAndSendEmail( "employeeActivationTemplate", { name: firstname, diff --git a/Server/service/emailService.js b/Server/service/emailService.js index 79d6213c6..27518484c 100644 --- a/Server/service/emailService.js +++ b/Server/service/emailService.js @@ -3,6 +3,8 @@ const path = require("path"); const nodemailer = require("nodemailer"); const { compile } = require("handlebars"); const mjml2html = require("mjml"); +const SERVICE_NAME = "EmailService"; +const logger = require("../utils/logger"); /** * Represents an email service that can load templates, build, and send emails. @@ -19,12 +21,19 @@ class EmailService { * @returns {Function} A compiled template function that can be used to generate HTML email content. */ this.loadTemplate = (templateName) => { - const templatePath = path.join( - __dirname, - `../templates/${templateName}.mjml` - ); - const templateContent = fs.readFileSync(templatePath, "utf8"); - return compile(templateContent); + try { + const templatePath = path.join( + __dirname, + `../templates/${templateName}.mjml` + ); + const templateContent = fs.readFileSync(templatePath, "utf8"); + return compile(templateContent); + } catch (error) { + logger.error("Error loading Email templates", { + error, + service: this.SERVICE_NAME, + }); + } }; /** @@ -67,25 +76,51 @@ class EmailService { */ buildAndSendEmail = async (template, context, to, subject) => { const buildHtml = async (template, context) => { - const mjml = this.templateLookup[template](context); - const html = await mjml2html(mjml); - return html.html; + try { + const mjml = this.templateLookup[template](context); + const html = await mjml2html(mjml); + return html.html; + } catch (error) { + logger.error("Error building Email HTML", { + error, + service: SERVICE_NAME, + }); + } }; const sendEmail = async (to, subject, html) => { - const info = await this.transporter.sendMail({ - to: to, - subject: subject, - html: html, - }); - return info; + try { + const info = await this.transporter.sendMail({ + to: to, + subject: subject, + html: html, + }); + return info; + } catch (error) { + logger.error("Error sending Email", { + error, + service: SERVICE_NAME, + }); + } }; - const info = await sendEmail( - to, - subject, - await buildHtml(template, context) - ); - return info.messageId; + + try { + const info = await sendEmail( + to, + subject, + await buildHtml(template, context) + ); + return info.messageId; + } catch (error) { + error.service = SERVICE_NAME; + if (error.method === undefined) { + error.method = "buildAndSendEmail"; + } + logger.error("Error building and sending Email", { + error, + service: SERVICE_NAME, + }); + } }; } From 6ce9e394252c560cecfbbd3a87ca564ddb306ffa Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Wed, 21 Aug 2024 11:50:26 -0700 Subject: [PATCH 04/32] Add validation for roles array --- Server/validation/joi.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Server/validation/joi.js b/Server/validation/joi.js index fe7090083..26e62a6db 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -44,7 +44,11 @@ const registerValidation = joi.object({ /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*()])[A-Za-z0-9!@#$%^&*()]+$/ ), profileImage: joi.any(), - role: joi.array().required(), + role: joi + .array() + .items(joi.string().valid("admin", "user")) + .min(1) + .required(), }); const editUserParamValidation = joi.object({ From 122d9d8f568ecfd82e9a58158bbb467da7997f5c Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 22 Aug 2024 12:07:36 -0400 Subject: [PATCH 05/32] Removed toast bounce animation and added a pulse effect to monitor status dot --- Client/src/Pages/Monitors/Details/index.jsx | 30 +++++++++++++++++++-- Client/src/Utils/toastUtils.jsx | 3 ++- Client/src/index.css | 11 ++++++++ 3 files changed, 41 insertions(+), 3 deletions(-) diff --git a/Client/src/Pages/Monitors/Details/index.jsx b/Client/src/Pages/Monitors/Details/index.jsx index b636444bd..4c0807ecb 100644 --- a/Client/src/Pages/Monitors/Details/index.jsx +++ b/Client/src/Pages/Monitors/Details/index.jsx @@ -171,8 +171,34 @@ const DetailsPage = () => { ]} /> - - {monitor?.status ? : } + + + + {monitor.url?.replace(/^https?:\/\//, "") || "..."} diff --git a/Client/src/Utils/toastUtils.jsx b/Client/src/Utils/toastUtils.jsx index 0f272a74e..ff3f341f8 100644 --- a/Client/src/Utils/toastUtils.jsx +++ b/Client/src/Utils/toastUtils.jsx @@ -1,5 +1,5 @@ import PropTypes from "prop-types"; -import { toast } from "react-toastify"; +import { toast, Slide } from "react-toastify"; import Alert from "../Components/Alert"; /** @@ -23,6 +23,7 @@ export const createToast = ({ autoClose: 3000, hideProgressBar: true, closeButton: false, + transition: Slide, ...config, }; diff --git a/Client/src/index.css b/Client/src/index.css index 1c80b4dab..f924671bb 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -168,6 +168,17 @@ body .MuiSkeleton-root { background-color: var(--env-var-color-15); } +@keyframes ripple { + from { + opacity: 1; + transform: scale(0); + } + to { + opacity: 0; + transform: scale(2.3); + } +} + @media (prefers-color-scheme: light) { :root { color: #213547; From 35ae18086593a04e6ca64b3e44183eb5a8d21b19 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 22 Aug 2024 12:11:56 -0400 Subject: [PATCH 06/32] Added pulse dot effect to the rest of monitor pages --- Client/src/Pages/Monitors/Configure/index.jsx | 32 ++++++++++++++--- Client/src/Pages/Monitors/Details/index.jsx | 2 -- .../src/Pages/PageSpeed/Configure/index.jsx | 30 ++++++++++++++-- Client/src/Pages/PageSpeed/Details/index.jsx | 36 +++++++++++++++---- 4 files changed, 86 insertions(+), 14 deletions(-) diff --git a/Client/src/Pages/Monitors/Configure/index.jsx b/Client/src/Pages/Monitors/Configure/index.jsx index 6b809433d..baebca18d 100644 --- a/Client/src/Pages/Monitors/Configure/index.jsx +++ b/Client/src/Pages/Monitors/Configure/index.jsx @@ -5,8 +5,6 @@ import { useEffect, useState } from "react"; import Button from "../../../Components/Button"; import Field from "../../../Components/Inputs/Field"; import { Box, Modal, Skeleton, Stack, Typography } from "@mui/material"; -import GreenCheck from "../../../assets/icons/checkbox-green.svg?react"; -import RedCheck from "../../../assets/icons/checkbox-red.svg?react"; import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline"; import { monitorValidation } from "../../../Validation/validation"; import Select from "../../../Components/Inputs/Select"; @@ -229,8 +227,34 @@ const Configure = () => { gap={theme.gap.large} flex={1} > - - {monitor?.status ? : } + + + + {parsedUrl?.host ? ( diff --git a/Client/src/Pages/Monitors/Details/index.jsx b/Client/src/Pages/Monitors/Details/index.jsx index 4c0807ecb..cdbacb521 100644 --- a/Client/src/Pages/Monitors/Details/index.jsx +++ b/Client/src/Pages/Monitors/Details/index.jsx @@ -7,8 +7,6 @@ import { networkService } from "../../../main"; import MonitorDetailsAreaChart from "../../../Components/Charts/MonitorDetailsAreaChart"; import ButtonGroup from "@mui/material/ButtonGroup"; import Button from "../../../Components/Button"; -import GreenCheck from "../../../assets/icons/checkbox-green.svg?react"; -import RedCheck from "../../../assets/icons/checkbox-red.svg?react"; import SettingsIcon from "../../../assets/icons/settings-bold.svg?react"; import PaginationTable from "./PaginationTable"; import { diff --git a/Client/src/Pages/PageSpeed/Configure/index.jsx b/Client/src/Pages/PageSpeed/Configure/index.jsx index bc759db17..ad1212e65 100644 --- a/Client/src/Pages/PageSpeed/Configure/index.jsx +++ b/Client/src/Pages/PageSpeed/Configure/index.jsx @@ -168,8 +168,34 @@ const PageSpeedConfigure = () => { flex={1} gap={theme.gap.large} > - - {monitor?.status ? : } + + + + { const theme = useTheme(); @@ -325,8 +323,34 @@ const PageSpeedDetails = () => { { name: "details", path: `/pagespeed/${monitorId}` }, ]} /> - - {monitor?.status ? : } + + + + Date: Thu, 22 Aug 2024 12:23:52 -0400 Subject: [PATCH 07/32] Refactored status dot to its own component --- Client/src/Components/Animated/PulseDot.jsx | 33 +++++++++++++ Client/src/Pages/Monitors/Configure/index.jsx | 45 +++++------------- Client/src/Pages/Monitors/Details/index.jsx | 47 ++++++------------- .../src/Pages/PageSpeed/Configure/index.jsx | 39 ++++----------- Client/src/Pages/PageSpeed/Details/index.jsx | 35 ++++---------- 5 files changed, 77 insertions(+), 122 deletions(-) create mode 100644 Client/src/Components/Animated/PulseDot.jsx diff --git a/Client/src/Components/Animated/PulseDot.jsx b/Client/src/Components/Animated/PulseDot.jsx new file mode 100644 index 000000000..46ba960f3 --- /dev/null +++ b/Client/src/Components/Animated/PulseDot.jsx @@ -0,0 +1,33 @@ +import { Box, Stack } from "@mui/material"; + +const PulseDot = ({ color }) => { + return ( + + + + ); +}; + +export default PulseDot; diff --git a/Client/src/Pages/Monitors/Configure/index.jsx b/Client/src/Pages/Monitors/Configure/index.jsx index baebca18d..bd2a26df0 100644 --- a/Client/src/Pages/Monitors/Configure/index.jsx +++ b/Client/src/Pages/Monitors/Configure/index.jsx @@ -2,22 +2,23 @@ import { useNavigate, useParams } from "react-router"; import { useTheme } from "@emotion/react"; import { useDispatch, useSelector } from "react-redux"; import { useEffect, useState } from "react"; -import Button from "../../../Components/Button"; -import Field from "../../../Components/Inputs/Field"; import { Box, Modal, Skeleton, Stack, Typography } from "@mui/material"; -import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline"; import { monitorValidation } from "../../../Validation/validation"; -import Select from "../../../Components/Inputs/Select"; import { createToast } from "../../../Utils/toastUtils"; +import { logger } from "../../../Utils/Logger"; import { updateUptimeMonitor, getUptimeMonitorsByUserId, deleteUptimeMonitor, } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice"; +import Button from "../../../Components/Button"; +import Field from "../../../Components/Inputs/Field"; +import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline"; +import Select from "../../../Components/Inputs/Select"; import Checkbox from "../../../Components/Inputs/Checkbox"; import Breadcrumbs from "../../../Components/Breadcrumbs"; +import PulseDot from "../../../Components/Animated/PulseDot"; import "./index.css"; -import { logger } from "../../../Utils/Logger"; /** * Parses a URL string and returns a URL object. @@ -228,33 +229,13 @@ const Configure = () => { flex={1} > - - - + {parsedUrl?.host ? ( diff --git a/Client/src/Pages/Monitors/Details/index.jsx b/Client/src/Pages/Monitors/Details/index.jsx index cdbacb521..ddc594602 100644 --- a/Client/src/Pages/Monitors/Details/index.jsx +++ b/Client/src/Pages/Monitors/Details/index.jsx @@ -4,18 +4,19 @@ import { Box, Skeleton, Stack, Typography, useTheme } from "@mui/material"; import { useSelector } from "react-redux"; import { useNavigate, useParams } from "react-router-dom"; import { networkService } from "../../../main"; +import { logger } from "../../../Utils/Logger"; +import { + formatDuration, + formatDurationRounded, +} from "../../../Utils/timeUtils"; import MonitorDetailsAreaChart from "../../../Components/Charts/MonitorDetailsAreaChart"; import ButtonGroup from "@mui/material/ButtonGroup"; import Button from "../../../Components/Button"; import SettingsIcon from "../../../assets/icons/settings-bold.svg?react"; import PaginationTable from "./PaginationTable"; -import { - formatDuration, - formatDurationRounded, -} from "../../../Utils/timeUtils"; -import "./index.css"; import Breadcrumbs from "../../../Components/Breadcrumbs"; -import { logger } from "../../../Utils/Logger"; +import PulseDot from "../../../Components/Animated/PulseDot"; +import "./index.css"; const StatBox = ({ title, value }) => { return ( @@ -170,33 +171,13 @@ const DetailsPage = () => { /> - - - + {monitor.url?.replace(/^https?:\/\//, "") || "..."} diff --git a/Client/src/Pages/PageSpeed/Configure/index.jsx b/Client/src/Pages/PageSpeed/Configure/index.jsx index ad1212e65..83484de7e 100644 --- a/Client/src/Pages/PageSpeed/Configure/index.jsx +++ b/Client/src/Pages/PageSpeed/Configure/index.jsx @@ -10,15 +10,14 @@ import { } from "../../../Features/PageSpeedMonitor/pageSpeedMonitorSlice"; import { monitorValidation } from "../../../Validation/validation"; import { createToast } from "../../../Utils/toastUtils"; +import { logger } from "../../../Utils/Logger"; import Button from "../../../Components/Button"; import Field from "../../../Components/Inputs/Field"; import Select from "../../../Components/Inputs/Select"; import Checkbox from "../../../Components/Inputs/Checkbox"; import PauseCircleOutlineIcon from "@mui/icons-material/PauseCircleOutline"; -import GreenCheck from "../../../assets/icons/checkbox-green.svg?react"; -import RedCheck from "../../../assets/icons/checkbox-red.svg?react"; import Breadcrumbs from "../../../Components/Breadcrumbs"; -import { logger } from "../../../Utils/Logger"; +import PulseDot from "../../../Components/Animated/PulseDot"; import "./index.css"; @@ -169,33 +168,13 @@ const PageSpeedConfigure = () => { gap={theme.gap.large} > - - - + { @@ -324,33 +325,13 @@ const PageSpeedDetails = () => { ]} /> - - - + Date: Thu, 22 Aug 2024 12:24:56 -0400 Subject: [PATCH 08/32] Added docs and proptypes --- Client/src/Components/Animated/PulseDot.jsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Client/src/Components/Animated/PulseDot.jsx b/Client/src/Components/Animated/PulseDot.jsx index 46ba960f3..4ec04c226 100644 --- a/Client/src/Components/Animated/PulseDot.jsx +++ b/Client/src/Components/Animated/PulseDot.jsx @@ -1,5 +1,19 @@ +import PropTypes from "prop-types"; import { Box, Stack } from "@mui/material"; +/** + * A component that renders a pulsating dot with a specified color. + * + * @component + * @example + * // Example usage: + * + * + * @param {Object} props + * @param {string} props.color - The color of the dot. + * @returns {JSX.Element} The PulseDot component. + */ + const PulseDot = ({ color }) => { return ( { ); }; +PulseDot.propTypes = { + color: PropTypes.string.isRequired, +}; + export default PulseDot; From ca3c1c984aa7e10a4b24b2ce896cdfc08742214d Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 22 Aug 2024 12:35:38 -0400 Subject: [PATCH 09/32] Updated tos link --- Client/src/Pages/Auth/Login.jsx | 9 +++++++-- Client/src/Pages/Auth/Register/Register.jsx | 9 +++++++-- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/Client/src/Pages/Auth/Login.jsx b/Client/src/Pages/Auth/Login.jsx index 765cb7a76..ecdd27060 100644 --- a/Client/src/Pages/Auth/Login.jsx +++ b/Client/src/Pages/Auth/Login.jsx @@ -60,7 +60,8 @@ const LandingPage = ({ onContinue }) => { onClick={() => { window.open( "https://bluewavelabs.ca/terms-of-service-open-source", - "_blank" + "_blank", + "noreferrer" ); }} > @@ -70,7 +71,11 @@ const LandingPage = ({ onContinue }) => { { - window.open("https://bluewavelabs.ca/privacy-policy", "_blank"); + window.open( + "https://bluewavelabs.ca/privacy-policy-open-source", + "_blank", + "noreferrer" + ); }} > Privacy Policy. diff --git a/Client/src/Pages/Auth/Register/Register.jsx b/Client/src/Pages/Auth/Register/Register.jsx index 958a61b22..a06075b5a 100644 --- a/Client/src/Pages/Auth/Register/Register.jsx +++ b/Client/src/Pages/Auth/Register/Register.jsx @@ -65,7 +65,8 @@ const LandingPage = ({ isAdmin, onSignup }) => { onClick={() => { window.open( "https://bluewavelabs.ca/terms-of-service-open-source", - "_blank" + "_blank", + "noreferrer" ); }} > @@ -75,7 +76,11 @@ const LandingPage = ({ isAdmin, onSignup }) => { { - window.open("https://bluewavelabs.ca/privacy-policy", "_blank"); + window.open( + "https://bluewavelabs.ca/privacy-policy-open-source", + "_blank", + "noreferrer" + ); }} > Privacy Policy. From 0183e135b73b015a08e92506e4e6d510acbd89c5 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 22 Aug 2024 14:21:19 -0400 Subject: [PATCH 10/32] Added white inner circle --- Client/src/Components/Animated/PulseDot.jsx | 15 +++++++++++++-- Client/src/index.css | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Client/src/Components/Animated/PulseDot.jsx b/Client/src/Components/Animated/PulseDot.jsx index 4ec04c226..1b196426a 100644 --- a/Client/src/Components/Animated/PulseDot.jsx +++ b/Client/src/Components/Animated/PulseDot.jsx @@ -23,8 +23,8 @@ const PulseDot = ({ color }) => { justifyContent="center" > { borderRadius: "50%", animation: "ripple 1.8s ease-out infinite", }, + "&::after": { + content: `""`, + position: "absolute", + width: "6px", + height: "6px", + borderRadius: "50%", + backgroundColor: "white", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + }, }} /> diff --git a/Client/src/index.css b/Client/src/index.css index f924671bb..d62f4b48f 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -175,7 +175,7 @@ body .MuiSkeleton-root { } to { opacity: 0; - transform: scale(2.3); + transform: scale(2); } } From a5a2674f1f2c0ec72ad733ad11d8f615a9b45c8b Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 22 Aug 2024 15:00:44 -0400 Subject: [PATCH 11/32] Adjusted padding measurements --- Client/src/Components/Sidebar/index.css | 19 +++++++++----- Client/src/Components/Sidebar/index.jsx | 35 +++++++++++-------------- Client/src/Pages/Monitors/index.jsx | 1 + 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/Client/src/Components/Sidebar/index.css b/Client/src/Components/Sidebar/index.css index 10cdfc77d..1535b6143 100644 --- a/Client/src/Components/Sidebar/index.css +++ b/Client/src/Components/Sidebar/index.css @@ -76,14 +76,21 @@ aside .MuiCollapse-wrapperInner .MuiList-root > .MuiListItemButton-root::after { content: ""; position: absolute; left: -8px; - top: 50%; - transform: translateY(-50%); + top: 45%; height: 3px; width: 3px; border-radius: 50%; background-color: #d6d9dd; } +aside + .MuiCollapse-wrapperInner + .MuiList-root + > .selected-path.MuiListItemButton-root::after { + background-color: var(--env-var-color-2); + transform: scale(1.2); +} + aside { transition: flex 300ms ease-out; } @@ -104,20 +111,20 @@ aside { padding: 5px 8px; border-radius: var(--env-var-radius-1); } -.sidebar-popup li.MuiButtonBase-root:hover{ +.sidebar-popup li.MuiButtonBase-root:hover { background-color: var(--env-var-color-13); } -.sidebar-popup li.MuiButtonBase-root:has(.MuiBox-root):hover{ +.sidebar-popup li.MuiButtonBase-root:has(.MuiBox-root):hover { background-color: white; } -.sidebar-popup li.MuiButtonBase-root:has(.MuiBox-root){ +.sidebar-popup li.MuiButtonBase-root:has(.MuiBox-root) { padding-bottom: 0; } .sidebar-popup .MuiModal-root p.MuiTypography-root { font-size: var(--env-var-font-size-medium); color: var(--env-var-color-2); } -.sidebar-popup svg{ +.sidebar-popup svg { width: 16px; height: 16px; opacity: 0.9; diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index 69b44c125..ddcbf8ade 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -128,11 +128,7 @@ function Sidebar() { gap={theme.gap.medium} sx={{ flex: collapsed ? 0 : 1 }} > - + {collapsed ? ( ) : ( @@ -175,14 +171,14 @@ function Sidebar() { id="nested-menu-subheader" sx={{ pt: theme.gap.small, - px: collapsed ? theme.gap.xs : theme.gap.ml, + px: collapsed ? 0 : theme.gap.small, textAlign: collapsed ? "center" : "left", }} > Menu } - sx={{ px: collapsed ? theme.gap.xs : theme.gap.ml }} + sx={{ px: theme.gap.medium }} > {menu.map((item) => item.path ? ( @@ -210,9 +206,9 @@ function Sidebar() { } onClick={() => navigate(`/${item.path}`)} sx={{ - gap: theme.gap.medium, + gap: theme.gap.small, borderRadius: `${theme.shape.borderRadius}px`, - justifyContent: collapsed ? "center" : "flex-start", + px: theme.gap.small, }} > {item.icon} @@ -247,9 +243,9 @@ function Sidebar() { onClick={(event) => openPopup(event, item.name)} sx={{ position: "relative", - gap: theme.gap.medium, + gap: theme.gap.small, borderRadius: `${theme.shape.borderRadius}px`, - justifyContent: collapsed ? "center" : "flex-start", + px: theme.gap.small, }} > {item.icon} @@ -311,9 +307,9 @@ function Sidebar() { })) } sx={{ - gap: theme.gap.medium, + gap: theme.gap.small, borderRadius: `${theme.shape.borderRadius}px`, - justifyContent: collapsed ? "center" : "flex-start", + px: theme.gap.small, }} > {item.icon} @@ -324,7 +320,7 @@ function Sidebar() { {item.nested.map((child) => { if ( @@ -374,14 +370,14 @@ function Sidebar() { id="nested-other-subheader" sx={{ pt: theme.gap.small, - px: collapsed ? theme.gap.xs : theme.gap.ml, + px: collapsed ? 0 : theme.gap.small, textAlign: collapsed ? "center" : "left", }} > Other } - sx={{ px: collapsed ? theme.gap.xs : theme.gap.ml }} + sx={{ px: theme.gap.medium }} > {other.map((item) => ( {item.icon} @@ -432,10 +428,9 @@ function Sidebar() { diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index dc293ef49..15c98ce24 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -437,6 +437,7 @@ const Monitors = () => { Date: Thu, 22 Aug 2024 15:51:22 -0400 Subject: [PATCH 12/32] Added bwu icon --- Client/src/Components/Sidebar/index.jsx | 43 ++++++++++++++++----- Client/src/assets/icons/bwu-abbreviated.svg | 4 -- 2 files changed, 34 insertions(+), 13 deletions(-) delete mode 100644 Client/src/assets/icons/bwu-abbreviated.svg diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index ddcbf8ade..bdf1fcf65 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -26,8 +26,6 @@ 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 BWULogoAbbreviated from "../../assets/icons/bwu-abbreviated.svg?react"; import Support from "../../assets/icons/support.svg?react"; import Dashboard from "../../assets/icons/dashboard.svg?react"; import Account from "../../assets/icons/user-edit.svg?react"; @@ -128,17 +126,44 @@ function Sidebar() { gap={theme.gap.medium} sx={{ flex: collapsed ? 0 : 1 }} > - - {collapsed ? ( - - ) : ( - - )} + + + + BU + + {!collapsed && ( + + BlueWave Uptime + + )} + - - - From 710fad0ec81709f4c8b3af67fd3cbf5de2066bf3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 22 Aug 2024 13:54:21 -0700 Subject: [PATCH 13/32] Add table prop to BasicTable to specify table name --- Client/src/Components/BasicTable/index.jsx | 6 ++---- Client/src/Components/TabPanels/Account/TeamPanel.jsx | 7 ++++++- Client/src/Pages/Monitors/Details/index.jsx | 2 -- Client/src/Pages/Monitors/index.jsx | 2 +- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Client/src/Components/BasicTable/index.jsx b/Client/src/Components/BasicTable/index.jsx index 17ba781f2..bef5bd651 100644 --- a/Client/src/Components/BasicTable/index.jsx +++ b/Client/src/Components/BasicTable/index.jsx @@ -15,7 +15,6 @@ import { Stack, } from "@mui/material"; import { useDispatch, useSelector } from "react-redux"; -import { useLocation } from "react-router-dom"; import { setRowsPerPage } from "../../Features/UI/uiSlice"; import LeftArrowDouble from "../../assets/icons/left-arrow-double.svg?react"; import RightArrowDouble from "../../assets/icons/right-arrow-double.svg?react"; @@ -150,13 +149,11 @@ TablePaginationActions.propTypes = { * */ -const BasicTable = ({ data, paginated, reversed }) => { +const BasicTable = ({ data, paginated, reversed, table }) => { const DEFAULT_ROWS_PER_PAGE = 5; const theme = useTheme(); - const location = useLocation(); const dispatch = useDispatch(); const uiState = useSelector((state) => state.ui); - let table = location.pathname.split("/").pop(); let rowsPerPage = uiState?.[table]?.rowsPerPage ?? DEFAULT_ROWS_PER_PAGE; const [page, setPage] = useState(0); @@ -293,6 +290,7 @@ BasicTable.propTypes = { paginated: PropTypes.bool, reversed: PropTypes.bool, rowPerPage: PropTypes.number, + table: PropTypes.string, }; export default BasicTable; diff --git a/Client/src/Components/TabPanels/Account/TeamPanel.jsx b/Client/src/Components/TabPanels/Account/TeamPanel.jsx index a6c378a60..06b0d9d0a 100644 --- a/Client/src/Components/TabPanels/Account/TeamPanel.jsx +++ b/Client/src/Components/TabPanels/Account/TeamPanel.jsx @@ -294,7 +294,12 @@ const TeamPanel = () => { onClick={() => setIsOpen(true)} /> - + { History - {/* TODO New Table */} - {/* */} diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index dc293ef49..926f50c8a 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -452,7 +452,7 @@ const Monitors = () => { {/* TODO - add search bar */} - + )} From c112fb45b62862e1fac22b28ba99fa58ed9dfcd5 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 22 Aug 2024 17:13:29 -0400 Subject: [PATCH 14/32] Fixed wonky animation --- Client/src/Components/Sidebar/index.css | 50 ++++++++++++++++++++++--- Client/src/Components/Sidebar/index.jsx | 43 ++++++++++----------- 2 files changed, 65 insertions(+), 28 deletions(-) diff --git a/Client/src/Components/Sidebar/index.css b/Client/src/Components/Sidebar/index.css index 1535b6143..8ce0089bc 100644 --- a/Client/src/Components/Sidebar/index.css +++ b/Client/src/Components/Sidebar/index.css @@ -91,10 +91,6 @@ aside transform: scale(1.2); } -aside { - transition: flex 300ms ease-out; -} - .sidebar-popup .MuiPaper-root { box-shadow: var(--env-var-shadow-1); border: solid 1px var(--env-var-color-6); @@ -102,7 +98,7 @@ aside { gap: 1px; } .sidebar-popup .MuiList-root { - padding: 6px 6px; + padding: 3px; min-width: 100px; } .sidebar-popup li.MuiButtonBase-root { @@ -129,3 +125,47 @@ aside { height: 16px; opacity: 0.9; } + +/* TRANSITIONS */ +aside { + flex: 1; + transition: max-width 650ms cubic-bezier(0.36, -0.01, 0, 0.77); +} +.home-layout aside.collapsed { + max-width: 64px; +} + +aside.expanded .MuiTypography-root, +aside.expanded p.MuiTypography-root, +aside.expanded .MuiListItemText-root + svg, +aside.expanded .MuiAvatar-root + .MuiBox-root + .MuiIconButton-root { + visibility: visible; + animation: fadeIn 1s ease; +} + +aside.collapsed .MuiTypography-root, +aside.collapsed p.MuiTypography-root, +aside.collapsed .MuiListItemText-root + svg, +aside.collapsed .MuiAvatar-root + .MuiBox-root + .MuiIconButton-root { + opacity: 0; + visibility: hidden; +} + +aside .MuiListSubheader-root { + transition: all 200ms ease; +} + +@keyframes fadeIn { + 0% { + opacity: 0; + visibility: hidden; + } + 30% { + opacity: 0; + visibility: hidden; + } + 100% { + opacity: 0.9; + visibility: visible; + } +} diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index bdf1fcf65..f1bfcb719 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -123,14 +123,10 @@ function Sidebar() { return ( - + BU - {!collapsed && ( - - BlueWave Uptime - - )} + + BlueWave Uptime + dispatch(toggleSidebar())} + onClick={() => { + setOpen({ Dashboard: false, Account: false }); + dispatch(toggleSidebar()); + }} > {collapsed ? : } @@ -196,8 +193,7 @@ function Sidebar() { id="nested-menu-subheader" sx={{ pt: theme.gap.small, - px: collapsed ? 0 : theme.gap.small, - textAlign: collapsed ? "center" : "left", + px: collapsed ? theme.gap.xs : theme.gap.small, }} > Menu @@ -231,13 +227,14 @@ function Sidebar() { } onClick={() => navigate(`/${item.path}`)} sx={{ + height: "37px", gap: theme.gap.small, borderRadius: `${theme.shape.borderRadius}px`, px: theme.gap.small, }} > {item.icon} - {!collapsed && {item.name}} + {item.name} ) : collapsed ? ( @@ -274,7 +271,7 @@ function Sidebar() { }} > {item.icon} - {!collapsed && {item.name}} + {item.name} {item.nested.map((child) => { if ( @@ -313,6 +310,7 @@ function Sidebar() { gap: theme.gap.small, borderRadius: `${theme.shape.borderRadius}px`, pl: theme.gap.small, + mb:"1px" }} > {child.icon} @@ -396,7 +394,6 @@ function Sidebar() { sx={{ pt: theme.gap.small, px: collapsed ? 0 : theme.gap.small, - textAlign: collapsed ? "center" : "left", }} > Other @@ -443,7 +440,7 @@ function Sidebar() { }} > {item.icon} - {!collapsed && {item.name}} + {item.name} ))} From c740416691167a1989f42f5cde0512c963e7a4dd Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 22 Aug 2024 14:17:33 -0700 Subject: [PATCH 15/32] Add nginx config --- Docker/client.Dockerfile | 1 + Docker/nginx/default.conf | 45 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+) create mode 100644 Docker/nginx/default.conf diff --git a/Docker/client.Dockerfile b/Docker/client.Dockerfile index 5b43527f2..aaea43c06 100644 --- a/Docker/client.Dockerfile +++ b/Docker/client.Dockerfile @@ -16,6 +16,7 @@ RUN npm run build FROM nginx:1.27.1-alpine +COPY ./Docker/nginx/default.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/dist /usr/share/nginx/html CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/Docker/nginx/default.conf b/Docker/nginx/default.conf new file mode 100644 index 000000000..0c3122df3 --- /dev/null +++ b/Docker/nginx/default.conf @@ -0,0 +1,45 @@ +server { + listen 80; + listen [::]:80; + server_name localhost; + + #access_log /var/log/nginx/host.access.log main; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } + + #error_page 404 /404.html; + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } + + # proxy the PHP scripts to Apache listening on 127.0.0.1:80 + # + #location ~ \.php$ { + # proxy_pass http://127.0.0.1; + #} + + # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 + # + #location ~ \.php$ { + # root html; + # fastcgi_pass 127.0.0.1:9000; + # fastcgi_index index.php; + # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; + # include fastcgi_params; + #} + + # deny access to .htaccess files, if Apache's document root + # concurs with nginx's one + # + #location ~ /\.ht { + # deny all; + #} +} From c3f964a2e2e5b0ad51b617c841fde627a40b5ee8 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 22 Aug 2024 19:36:48 -0400 Subject: [PATCH 16/32] Implemented basic bar chart --- Client/src/Components/Animated/PulseDot.jsx | 4 +- Client/src/Components/BasicTable/index.css | 1 + .../src/Components/Charts/BarChart/index.jsx | 40 +++++++++++++++++++ Client/src/Components/Sidebar/index.jsx | 2 +- Client/src/Pages/Monitors/index.jsx | 3 +- Client/src/index.css | 2 +- 6 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 Client/src/Components/Charts/BarChart/index.jsx diff --git a/Client/src/Components/Animated/PulseDot.jsx b/Client/src/Components/Animated/PulseDot.jsx index 1b196426a..38e956b50 100644 --- a/Client/src/Components/Animated/PulseDot.jsx +++ b/Client/src/Components/Animated/PulseDot.jsx @@ -23,8 +23,8 @@ const PulseDot = ({ color }) => { justifyContent="center" > { + const theme = useTheme(); + + return ( + + {checks.map((check) => ( + + + + ))} + + ); +}; + +export default BarChart; diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index f1bfcb719..1033ec0e6 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -310,7 +310,7 @@ function Sidebar() { gap: theme.gap.small, borderRadius: `${theme.shape.borderRadius}px`, pl: theme.gap.small, - mb:"1px" + mb: "1px", }} > {child.icon} diff --git a/Client/src/Pages/Monitors/index.jsx b/Client/src/Pages/Monitors/index.jsx index 15c98ce24..0d559da13 100644 --- a/Client/src/Pages/Monitors/index.jsx +++ b/Client/src/Pages/Monitors/index.jsx @@ -28,6 +28,7 @@ import { import Settings from "../../assets/icons/settings-bold.svg?react"; import PropTypes from "prop-types"; +import BarChart from "../../Components/Charts/BarChart"; const ActionsMenu = ({ monitor }) => { const [anchorEl, setAnchorEl] = useState(null); @@ -357,7 +358,7 @@ const Monitors = () => { /> ), }, - { id: idx + 2, data: }, + { id: idx + 2, data: }, { id: idx + 3, data: ( diff --git a/Client/src/index.css b/Client/src/index.css index d62f4b48f..887d7da07 100644 --- a/Client/src/index.css +++ b/Client/src/index.css @@ -67,7 +67,7 @@ --env-var-width-2: 360px; --env-var-height-1: 100vh; - --env-var-height-2: 36px; + --env-var-height-2: 34px; --env-var-nav-bar-height: 70px; --env-var-side-bar-width: 250px; From ec79bcd500c7b1a6d920956cb2f0c8b78997ee4b Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Thu, 22 Aug 2024 20:15:49 -0400 Subject: [PATCH 17/32] Added tooltips to each individual bar --- .../src/Components/Charts/BarChart/index.css | 19 ++++ .../src/Components/Charts/BarChart/index.jsx | 93 +++++++++++++++---- Client/src/Utils/timeUtils.js | 3 +- 3 files changed, 97 insertions(+), 18 deletions(-) create mode 100644 Client/src/Components/Charts/BarChart/index.css diff --git a/Client/src/Components/Charts/BarChart/index.css b/Client/src/Components/Charts/BarChart/index.css new file mode 100644 index 000000000..e386aef1c --- /dev/null +++ b/Client/src/Components/Charts/BarChart/index.css @@ -0,0 +1,19 @@ +.bar-tooltip .MuiTooltip-tooltip { + background-color: white; + border: solid 1px black; + box-shadow: var(--env-var-shadow-1); + border: solid 1px var(--env-var-color-6); + border-radius: var(--env-var-radius-1); + padding: 4px 8px; +} + +.bar-tooltip .MuiTooltip-tooltip p { + font-size: var(--env-var-font-size-small-plus); + color: var(--env-var-color-2); + font-weight: 500; +} +.bar-tooltip .MuiTooltip-tooltip span { + font-size: var(--env-var-font-size-small); + color: var(--env-var-color-2); + font-weight: 600; +} diff --git a/Client/src/Components/Charts/BarChart/index.jsx b/Client/src/Components/Charts/BarChart/index.jsx index 6af3180a9..8ab54e8a8 100644 --- a/Client/src/Components/Charts/BarChart/index.jsx +++ b/Client/src/Components/Charts/BarChart/index.jsx @@ -1,5 +1,7 @@ import { useTheme } from "@emotion/react"; -import { Box, Stack } from "@mui/material"; +import { Box, Stack, Tooltip, Typography } from "@mui/material"; +import { formatDate } from "../../../Utils/timeUtils"; +import "./index.css"; const BarChart = ({ checks = [] }) => { const theme = useTheme(); @@ -7,31 +9,88 @@ const BarChart = ({ checks = [] }) => { return ( {checks.map((check) => ( - + + {formatDate(new Date(check.createdAt), { year: undefined })} + + + + + + Response Time + + + {check.originalResponseTime} + + {" "} + ms + + + + + + } + placement="top" key={`check-${check?._id}`} - position="relative" - width="8px" - height="100%" - backgroundColor="#f4f4f4" - sx={{ - borderRadius: theme.gap.xs, + slotProps={{ + popper: { + className: "bar-tooltip", + modifiers: [ + { + name: "offset", + options: { + offset: [0, -12], + }, + }, + ], + }, }} > - + > + + + ))} ); diff --git a/Client/src/Utils/timeUtils.js b/Client/src/Utils/timeUtils.js index f7742dbaf..2d1a2f180 100644 --- a/Client/src/Utils/timeUtils.js +++ b/Client/src/Utils/timeUtils.js @@ -43,7 +43,7 @@ export const formatDurationRounded = (ms) => { return time; }; -export const formatDate = (date) => { +export const formatDate = (date, customOptions) => { const options = { year: "numeric", month: "long", @@ -51,6 +51,7 @@ export const formatDate = (date) => { hour: "numeric", minute: "numeric", hour12: true, + ...customOptions }; // Return the date using the specified options From aa5c0f81d3a1023a4c6859e9f92fb04a64a2a99b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 09:46:45 -0700 Subject: [PATCH 18/32] Docker config --- Docker/.gitignore | 2 ++ Docker/client.Dockerfile | 2 +- Docker/docker-compose.yaml | 12 ++++++++- Docker/nginx/conf.d/default.conf | 33 +++++++++++++++++++++++ Docker/nginx/default.conf | 45 -------------------------------- 5 files changed, 47 insertions(+), 47 deletions(-) create mode 100644 Docker/nginx/conf.d/default.conf delete mode 100644 Docker/nginx/default.conf diff --git a/Docker/.gitignore b/Docker/.gitignore index e1b726025..80c983e6f 100644 --- a/Docker/.gitignore +++ b/Docker/.gitignore @@ -2,4 +2,6 @@ !build_images.sh mongo/data/* redis/data/* +nginx/* +certbot/* *.env \ No newline at end of file diff --git a/Docker/client.Dockerfile b/Docker/client.Dockerfile index aaea43c06..0f7935cd1 100644 --- a/Docker/client.Dockerfile +++ b/Docker/client.Dockerfile @@ -16,7 +16,7 @@ RUN npm run build FROM nginx:1.27.1-alpine -COPY ./Docker/nginx/default.conf /etc/nginx/conf.d/default.conf +# COPY ./Docker/nginx/default.conf /etc/nginx/conf.d/default.conf COPY --from=build /app/dist /usr/share/nginx/html CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file diff --git a/Docker/docker-compose.yaml b/Docker/docker-compose.yaml index 1abbce9d4..c9fa3431f 100644 --- a/Docker/docker-compose.yaml +++ b/Docker/docker-compose.yaml @@ -1,11 +1,21 @@ -version: "3" services: client: image: uptime_client:latest ports: - "80:80" + - "443:443" depends_on: - server + volumes: + - ./nginx/conf.d:/etc/nginx/conf.d/:ro + - ./certbot/www:/var/www/certbot/:ro + - ./certbot/conf/:/etc/nginx/ssl/:ro + + certbot: + image: certbot/certbot:latest + volumes: + - ./certbot/www/:/var/www/certbot/:rw + - ./certbot/conf/:/etc/letsencrypt/:rw server: image: uptime_server:latest ports: diff --git a/Docker/nginx/conf.d/default.conf b/Docker/nginx/conf.d/default.conf new file mode 100644 index 000000000..eb06e2746 --- /dev/null +++ b/Docker/nginx/conf.d/default.conf @@ -0,0 +1,33 @@ +server { + listen 80; + listen [::]:80; + + server_name uptime-demo.bluewavelabs.ca; + server_tokens off; + + location /.well-known/acme-challenge/ { + root /var/www/certbot; + } + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } +} + +server { + listen 443 default_server ssl http2; + listen [::]:443 ssl http2; + + server_name uptime-demo.bluewavelabs.ca; + + ssl_certificate /etc/nginx/ssl/live/uptime-demo.bluewavelabs.ca/fullchain.pem; + ssl_certificate_key /etc/nginx/ssl/live/uptime-demo.bluewavelabs.ca/privkey.pem; + + location / { + root /usr/share/nginx/html; + index index.html index.htm; + try_files $uri $uri/ /index.html; + } +} diff --git a/Docker/nginx/default.conf b/Docker/nginx/default.conf deleted file mode 100644 index 0c3122df3..000000000 --- a/Docker/nginx/default.conf +++ /dev/null @@ -1,45 +0,0 @@ -server { - listen 80; - listen [::]:80; - server_name localhost; - - #access_log /var/log/nginx/host.access.log main; - - location / { - root /usr/share/nginx/html; - index index.html index.htm; - try_files $uri $uri/ /index.html; - } - - #error_page 404 /404.html; - - # redirect server error pages to the static page /50x.html - # - error_page 500 502 503 504 /50x.html; - location = /50x.html { - root /usr/share/nginx/html; - } - - # proxy the PHP scripts to Apache listening on 127.0.0.1:80 - # - #location ~ \.php$ { - # proxy_pass http://127.0.0.1; - #} - - # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 - # - #location ~ \.php$ { - # root html; - # fastcgi_pass 127.0.0.1:9000; - # fastcgi_index index.php; - # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; - # include fastcgi_params; - #} - - # deny access to .htaccess files, if Apache's document root - # concurs with nginx's one - # - #location ~ /\.ht { - # deny all; - #} -} From ab0e3c4d0a96df8cf5f1eb341ba6dbd554fcf450 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 09:58:46 -0700 Subject: [PATCH 19/32] update nginx conf --- Docker/nginx/conf.d/default.conf | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Docker/nginx/conf.d/default.conf b/Docker/nginx/conf.d/default.conf index eb06e2746..8fdc625ee 100644 --- a/Docker/nginx/conf.d/default.conf +++ b/Docker/nginx/conf.d/default.conf @@ -14,6 +14,15 @@ server { index index.html index.htm; try_files $uri $uri/ /index.html; } + + location /api/ { + proxy_pass http://server:5000/api; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } } server { @@ -30,4 +39,13 @@ server { index index.html index.htm; try_files $uri $uri/ /index.html; } + + location /api/ { + proxy_pass http://server:5000/api; + proxy_http_version 1.1; + proxy_set_header Upgrade $http_upgrade; + proxy_set_header Connection 'upgrade'; + proxy_set_header Host $host; + proxy_cache_bypass $http_upgrade; + } } From 5d734d477c4e2394c9c95f683a20d8421cb520dd Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 10:10:03 -0700 Subject: [PATCH 20/32] Update nginx conf --- Docker/nginx/conf.d/default.conf | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Docker/nginx/conf.d/default.conf b/Docker/nginx/conf.d/default.conf index 8fdc625ee..027416590 100644 --- a/Docker/nginx/conf.d/default.conf +++ b/Docker/nginx/conf.d/default.conf @@ -18,10 +18,10 @@ server { location /api/ { proxy_pass http://server:5000/api; proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; } } @@ -43,9 +43,9 @@ server { location /api/ { proxy_pass http://server:5000/api; proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection 'upgrade'; proxy_set_header Host $host; - proxy_cache_bypass $http_upgrade; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; } } From e32f725e29d622e0a628a3b9931369260faf20f7 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 10:12:23 -0700 Subject: [PATCH 21/32] add trailing slash --- Docker/nginx/conf.d/default.conf | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Docker/nginx/conf.d/default.conf b/Docker/nginx/conf.d/default.conf index 027416590..a04d32c87 100644 --- a/Docker/nginx/conf.d/default.conf +++ b/Docker/nginx/conf.d/default.conf @@ -16,7 +16,7 @@ server { } location /api/ { - proxy_pass http://server:5000/api; + proxy_pass http://server:5000/api/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; @@ -41,7 +41,7 @@ server { } location /api/ { - proxy_pass http://server:5000/api; + proxy_pass http://server:5000/api/; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; From 19f666fe82b69dc03827ee255acc6c0791e42421 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 10:27:09 -0700 Subject: [PATCH 22/32] expose onInput in component, force lowercase for email input --- Client/src/Components/Inputs/Field/index.jsx | 5 +++++ Client/src/Pages/Auth/Register/Register.jsx | 1 + 2 files changed, 6 insertions(+) diff --git a/Client/src/Components/Inputs/Field/index.jsx b/Client/src/Components/Inputs/Field/index.jsx index 0b2fe56fe..335ed178f 100644 --- a/Client/src/Components/Inputs/Field/index.jsx +++ b/Client/src/Components/Inputs/Field/index.jsx @@ -48,6 +48,7 @@ const Field = forwardRef( placeholder, value, onChange, + onInput, error, disabled, }, @@ -87,6 +88,7 @@ const Field = forwardRef( multiline={type === "description"} rows={type === "description" ? 4 : 1} value={value} + onInput={onInput} onChange={onChange} disabled={disabled} inputRef={ref} @@ -176,6 +178,8 @@ const Field = forwardRef( } ); +Field.displayName = "Field"; + Field.propTypes = { type: PropTypes.oneOf(["text", "password", "url", "email", "description"]), id: PropTypes.string.isRequired, @@ -189,6 +193,7 @@ Field.propTypes = { placeholder: PropTypes.string, value: PropTypes.string.isRequired, onChange: PropTypes.func, + onInput: PropTypes.func, error: PropTypes.string, disabled: PropTypes.bool, }; diff --git a/Client/src/Pages/Auth/Register/Register.jsx b/Client/src/Pages/Auth/Register/Register.jsx index a06075b5a..4680c1821 100644 --- a/Client/src/Pages/Auth/Register/Register.jsx +++ b/Client/src/Pages/Auth/Register/Register.jsx @@ -226,6 +226,7 @@ const StepTwo = ({ form, errors, onSubmit, onChange, onBack }) => { placeholder="jordan.ellis@domain.com" autoComplete="email" value={form.email} + onInput={(e) => (e.target.value = e.target.value.toLowerCase())} onChange={onChange} error={errors.email} ref={inputRef} From ee22216bf904ea9e5cfba79d236b3c6df9943e48 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 10:30:27 -0700 Subject: [PATCH 23/32] Add custom validator to check for lowercase --- Client/src/Validation/validation.js | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/Client/src/Validation/validation.js b/Client/src/Validation/validation.js index ab2501199..35d47d6b1 100644 --- a/Client/src/Validation/validation.js +++ b/Client/src/Validation/validation.js @@ -49,6 +49,13 @@ const credentials = joi.object({ .string() .trim() .email({ tlds: { allow: false } }) + .custom((value, helpers) => { + const lowercasedValue = value.toLowerCase(); + if (value !== lowercasedValue) { + return helpers.message("Email must be in lowercase"); + } + return lowercasedValue; + }) .messages({ "string.empty": "Email is required", "string.email": "Must be a valid email address", @@ -72,14 +79,10 @@ const credentials = joi.object({ }); const monitorValidation = joi.object({ - url: joi - .string() - .uri({ allowRelative: true }) - .trim() - .messages({ - "string.empty": "This field is required.", - "string.uri": "The URL you provided is not valid.", - }), + url: joi.string().uri({ allowRelative: true }).trim().messages({ + "string.empty": "This field is required.", + "string.uri": "The URL you provided is not valid.", + }), name: joi.string().trim().max(50).allow("").messages({ "string.max": "This field should not exceed the 50 characters limit.", }), From 73136545cc8f124fd6803eb9dec5a82fdec02376 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 10:33:37 -0700 Subject: [PATCH 24/32] Add backend validation to check for lowercase --- Docker/.gitignore | 3 ++- Server/validation/joi.js | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Docker/.gitignore b/Docker/.gitignore index e1b726025..89aa6bfac 100644 --- a/Docker/.gitignore +++ b/Docker/.gitignore @@ -2,4 +2,5 @@ !build_images.sh mongo/data/* redis/data/* -*.env \ No newline at end of file +*.env +certbot/* \ No newline at end of file diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 26e62a6db..775aee7b5 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -35,7 +35,17 @@ const registerValidation = joi.object({ .string() .required() .pattern(/^[A-Za-z]+$/), - email: joi.string().email().required(), + email: joi + .string() + .email() + .required() + .custom((value, helpers) => { + const lowercasedValue = value.toLowerCase(); + if (value !== lowercasedValue) { + return helpers.message("Email must be in lowercase"); + } + return lowercasedValue; + }), password: joi .string() .min(8) From 9149f3e103ed933cdb6d9b0f35344e704ea71ab3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 10:35:08 -0700 Subject: [PATCH 25/32] add validation for backend login --- Server/validation/joi.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 775aee7b5..4f8042e12 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -16,7 +16,17 @@ const roleValidatior = (role) => (value, helpers) => { //**************************************** const loginValidation = joi.object({ - email: joi.string().email().required(), + email: joi + .string() + .email() + .required() + .custom((value, helpers) => { + const lowercasedValue = value.toLowerCase(); + if (value !== lowercasedValue) { + return helpers.message("Email must be in lowercase"); + } + return lowercasedValue; + }), password: joi .string() .min(8) From d9781f05add0043102f0a3a0cdf3a29492855f68 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 10:36:02 -0700 Subject: [PATCH 26/32] force input to lowercase for login --- Client/src/Pages/Auth/Login.jsx | 1 + 1 file changed, 1 insertion(+) diff --git a/Client/src/Pages/Auth/Login.jsx b/Client/src/Pages/Auth/Login.jsx index ecdd27060..b962fbb9a 100644 --- a/Client/src/Pages/Auth/Login.jsx +++ b/Client/src/Pages/Auth/Login.jsx @@ -129,6 +129,7 @@ const StepOne = ({ form, errors, onSubmit, onChange, onBack }) => { placeholder="jordan.ellis@domain.com" autoComplete="email" value={form.email} + onInput={(e) => (e.target.value = e.target.value.toLowerCase())} onChange={onChange} error={errors.email} ref={inputRef} From 8d75c52a720934df892c4773e4521f9bdda5be0f Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 23 Aug 2024 13:41:06 -0400 Subject: [PATCH 27/32] Stop event propagation --- .../src/Components/Charts/BarChart/index.jsx | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/Client/src/Components/Charts/BarChart/index.jsx b/Client/src/Components/Charts/BarChart/index.jsx index 8ab54e8a8..e685232d0 100644 --- a/Client/src/Components/Charts/BarChart/index.jsx +++ b/Client/src/Components/Charts/BarChart/index.jsx @@ -7,7 +7,17 @@ const BarChart = ({ checks = [] }) => { const theme = useTheme(); return ( - + event.stopPropagation()} + sx={{ + cursor: "default", + }} + > {checks.map((check) => ( { { name: "offset", options: { - offset: [0, -12], + offset: [0, -10], }, }, ], @@ -66,13 +76,13 @@ const BarChart = ({ checks = [] }) => { > { : theme.label.down.dotColor } sx={{ - borderRadius: theme.gap.xs, + borderRadius: "3px", }} /> From 931d15c4510d8cec3315a6fc4cc216ad47a2179f Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 23 Aug 2024 14:25:55 -0400 Subject: [PATCH 28/32] Added placeholder checks --- .../src/Components/Charts/BarChart/index.jsx | 166 ++++++++++-------- 1 file changed, 96 insertions(+), 70 deletions(-) diff --git a/Client/src/Components/Charts/BarChart/index.jsx b/Client/src/Components/Charts/BarChart/index.jsx index e685232d0..f4aac9abc 100644 --- a/Client/src/Components/Charts/BarChart/index.jsx +++ b/Client/src/Components/Charts/BarChart/index.jsx @@ -6,6 +6,16 @@ import "./index.css"; const BarChart = ({ checks = [] }) => { const theme = useTheme(); + // set responseTime to average if there's only one check + if (checks.length === 1) { + checks[0] = { ...checks[0], responseTime: 50 }; + } + + if (checks.length !== 25) { + const placeholders = Array(25 - checks.length).fill("placeholder"); + checks = [...checks, ...placeholders]; + } + return ( { cursor: "default", }} > - {checks.map((check) => ( - - - {formatDate(new Date(check.createdAt), { year: undefined })} - - - - - - Response Time - - - {check.originalResponseTime} - - {" "} - ms - - - - - - } - placement="top" - key={`check-${check?._id}`} - slotProps={{ - popper: { - className: "bar-tooltip", - modifiers: [ - { - name: "offset", - options: { - offset: [0, -10], - }, - }, - ], - }, - }} - > + {checks.map((check, index) => + check === "placeholder" ? ( + ) : ( + + + {formatDate(new Date(check.createdAt), { year: undefined })} + + + + + + Response Time + + + {check.originalResponseTime} + + {" "} + ms + + + + + + } + placement="top" + key={`check-${check?._id}`} + slotProps={{ + popper: { + className: "bar-tooltip", + modifiers: [ + { + name: "offset", + options: { + offset: [0, -10], + }, + }, + ], + }, + }} > .MuiBox-root": { + filter: "brightness(0.8)", + }, }} - /> - - - ))} + > + + + + ) + )} ); }; From 6caea8aef54b32b838bed123b248085f581ff8e3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 23 Aug 2024 11:29:28 -0700 Subject: [PATCH 29/32] Comment out unused features --- Client/src/Components/Sidebar/index.jsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index f1bfcb719..120973d1e 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -53,9 +53,9 @@ const menu = [ ], }, { name: "Incidents", path: "incidents", icon: }, - { name: "Status pages", path: "status", icon: }, + // { name: "Status pages", path: "status", icon: }, { name: "Maintenance", path: "maintenance", icon: }, - { name: "Integrations", path: "integrations", icon: }, + // { name: "Integrations", path: "integrations", icon: }, { name: "Account", icon: , @@ -310,7 +310,7 @@ function Sidebar() { gap: theme.gap.small, borderRadius: `${theme.shape.borderRadius}px`, pl: theme.gap.small, - mb:"1px" + mb: "1px", }} > {child.icon} From 8778fa888f8163f46923ad1802ac1917c13400b6 Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 23 Aug 2024 14:34:26 -0400 Subject: [PATCH 30/32] Added bars transition --- Client/src/Components/Charts/BarChart/index.jsx | 13 ++++++++++--- Client/src/Components/Sidebar/index.jsx | 2 +- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/Client/src/Components/Charts/BarChart/index.jsx b/Client/src/Components/Charts/BarChart/index.jsx index f4aac9abc..9238374c2 100644 --- a/Client/src/Components/Charts/BarChart/index.jsx +++ b/Client/src/Components/Charts/BarChart/index.jsx @@ -1,10 +1,16 @@ import { useTheme } from "@emotion/react"; import { Box, Stack, Tooltip, Typography } from "@mui/material"; import { formatDate } from "../../../Utils/timeUtils"; +import { useEffect, useState } from "react"; import "./index.css"; const BarChart = ({ checks = [] }) => { const theme = useTheme(); + const [animate, setAnimate] = useState(false); + + useEffect(() => { + setAnimate(true); + }); // set responseTime to average if there's only one check if (checks.length === 1) { @@ -106,15 +112,15 @@ const BarChart = ({ checks = [] }) => { sx={{ borderRadius: "3px", "&:hover > .MuiBox-root": { - filter: "brightness(0.8)", - }, + filter: "brightness(0.8)", + }, }} > { } sx={{ borderRadius: "3px", + transition: "height 600ms cubic-bezier(0.4, 0, 0.2, 1)", }} /> diff --git a/Client/src/Components/Sidebar/index.jsx b/Client/src/Components/Sidebar/index.jsx index 1033ec0e6..8fe527346 100644 --- a/Client/src/Components/Sidebar/index.jsx +++ b/Client/src/Components/Sidebar/index.jsx @@ -427,7 +427,7 @@ function Sidebar() { onClick={() => item.path === "support" ? window.open( - "https://github.com/bluewave-labs/bluewave-uptime", + "https://github.com/bluewave-labs/bluewave-uptime/issues", "_blank", "noreferrer" ) From 85ae1826f51654c41dd37985247dbe214bc9330d Mon Sep 17 00:00:00 2001 From: Daniel Cojocea Date: Fri, 23 Aug 2024 15:02:01 -0400 Subject: [PATCH 31/32] Comment out additional unused features --- .../TabPanels/Account/TeamPanel.jsx | 41 ++++++++++--------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/Client/src/Components/TabPanels/Account/TeamPanel.jsx b/Client/src/Components/TabPanels/Account/TeamPanel.jsx index 06b0d9d0a..a0393977d 100644 --- a/Client/src/Components/TabPanels/Account/TeamPanel.jsx +++ b/Client/src/Components/TabPanels/Account/TeamPanel.jsx @@ -74,7 +74,8 @@ const TeamPanel = () => { { id: 1, name: "NAME" }, { id: 2, name: "EMAIL" }, { id: 3, name: "ROLE" }, - { id: 4, name: "ACTION" }, + // FEATURE STILL TO BE IMPLEMENTED + // { id: 4, name: "ACTION" }, ], rows: team?.map((member, idx) => { return { @@ -101,22 +102,23 @@ const TeamPanel = () => { id: idx + 2, data: member.role.includes("admin") ? "Administrator" : "Member", }, - { - // TODO - Add delete onClick - id: idx + 3, - data: ( - - - - ), - }, + // FEATURE STILL TO BE IMPLEMENTED + // { + // // TODO - Add delete onClick + // id: idx + 3, + // data: ( + // + // + // + // ), + // }, ], }; }), @@ -197,7 +199,8 @@ const TeamPanel = () => { return ( - + {/* FEATURE STILL TO BE IMPLEMENTED */} + {/* Organization name @@ -242,7 +245,7 @@ const TeamPanel = () => { /> -