From 888f644c7cef07f64269f4b8736027fa6238b2b0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 26 Sep 2024 10:28:48 +0800 Subject: [PATCH 01/25] user -> User --- Server/db/FakeDb.js | 2 +- Server/db/mongo/MongoDB.js | 2 +- Server/db/mongo/modules/checkModule.js | 2 +- Server/db/mongo/modules/recoveryModule.js | 2 +- Server/db/mongo/modules/userModule.js | 2 +- Server/middleware/verifyJWT.js | 2 +- Server/models/{user.js => User.js} | 0 Server/routes/authRoute.js | 2 +- 8 files changed, 7 insertions(+), 7 deletions(-) rename Server/models/{user.js => User.js} (100%) diff --git a/Server/db/FakeDb.js b/Server/db/FakeDb.js index 6a65bf502..e4f36d001 100644 --- a/Server/db/FakeDb.js +++ b/Server/db/FakeDb.js @@ -21,7 +21,7 @@ // ************************** const Monitor = require("../models/Monitor"); -const UserModel = require("../models/user"); +const UserModel = require("../models/User"); const bcrypt = require("bcrypt"); let FAKE_MONITOR_DATA = []; diff --git a/Server/db/mongo/MongoDB.js b/Server/db/mongo/MongoDB.js index 0cc2d5b57..35120c966 100644 --- a/Server/db/mongo/MongoDB.js +++ b/Server/db/mongo/MongoDB.js @@ -1,5 +1,5 @@ const mongoose = require("mongoose"); -const UserModel = require("../../models/user"); +const UserModel = require("../../models/User"); //**************************************** // DB Connection diff --git a/Server/db/mongo/modules/checkModule.js b/Server/db/mongo/modules/checkModule.js index 6ab1fd51b..c18491021 100644 --- a/Server/db/mongo/modules/checkModule.js +++ b/Server/db/mongo/modules/checkModule.js @@ -1,6 +1,6 @@ const Check = require("../../../models/Check"); const Monitor = require("../../../models/Monitor"); -const User = require("../../../models/user"); +const User = require("../../../models/User"); const logger = require("../../../utils/logger"); const SERVICE_NAME = "checkModule"; const dateRangeLookup = { diff --git a/Server/db/mongo/modules/recoveryModule.js b/Server/db/mongo/modules/recoveryModule.js index 3f5c14819..469954cef 100644 --- a/Server/db/mongo/modules/recoveryModule.js +++ b/Server/db/mongo/modules/recoveryModule.js @@ -1,4 +1,4 @@ -const UserModel = require("../../../models/user"); +const UserModel = require("../../../models/User"); const RecoveryToken = require("../../../models/RecoveryToken"); const crypto = require("crypto"); const { errorMessages } = require("../../../utils/messages"); diff --git a/Server/db/mongo/modules/userModule.js b/Server/db/mongo/modules/userModule.js index 37446623e..e602e84b5 100644 --- a/Server/db/mongo/modules/userModule.js +++ b/Server/db/mongo/modules/userModule.js @@ -1,4 +1,4 @@ -const UserModel = require("../../../models/user"); +const UserModel = require("../../../models/User"); const TeamModel = require("../../../models/Team"); const { errorMessages } = require("../../../utils/messages"); const { GenerateAvatarImage } = require("../../../utils/imageProcessing"); diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js index d79c2eddc..9d50fe7ea 100644 --- a/Server/middleware/verifyJWT.js +++ b/Server/middleware/verifyJWT.js @@ -4,7 +4,7 @@ const SERVICE_NAME = "verifyJWT"; const TOKEN_PREFIX = "Bearer "; const { errorMessages } = require("../utils/messages"); const { parse } = require("path"); -const User = require("../models/user"); +const User = require("../models/User"); /** * Verifies the JWT token * @function diff --git a/Server/models/user.js b/Server/models/User.js similarity index 100% rename from Server/models/user.js rename to Server/models/User.js diff --git a/Server/routes/authRoute.js b/Server/routes/authRoute.js index 743a55c19..362f6a5a2 100644 --- a/Server/routes/authRoute.js +++ b/Server/routes/authRoute.js @@ -4,7 +4,7 @@ const { verifyOwnership } = require("../middleware/verifyOwnership"); const { isAllowed } = require("../middleware/isAllowed"); const multer = require("multer"); const upload = multer(); -const User = require("../models/user"); +const User = require("../models/User"); const { registerController, From 09fb5595abe0b19f68aff7031874e1a667e8f68d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 26 Sep 2024 10:42:03 +0800 Subject: [PATCH 02/25] Fix proptype errors, remove old alert reference --- Client/src/Pages/Monitors/Home/index.jsx | 2 +- Client/src/Pages/PageSpeed/Details/index.jsx | 2 +- Server/controllers/monitorController.js | 26 +++++++++++--------- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Client/src/Pages/Monitors/Home/index.jsx b/Client/src/Pages/Monitors/Home/index.jsx index c2a532bb9..a03959076 100644 --- a/Client/src/Pages/Monitors/Home/index.jsx +++ b/Client/src/Pages/Monitors/Home/index.jsx @@ -134,7 +134,7 @@ const Monitors = ({ isAdmin }) => { { normalize: null, }); setMonitor(res?.data?.data ?? {}); - setAudits(res?.data?.data?.checks?.[0]?.audits ?? []); + setAudits(res?.data?.data?.checks?.[0]?.audits ?? {}); } catch (error) { logger.error(logger); navigate("/not-found", { replace: true }); diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index e534e0f60..a8b90c084 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -20,6 +20,7 @@ const SERVICE_NAME = "monitorController"; const { errorMessages, successMessages } = require("../utils/messages"); const jwt = require("jsonwebtoken"); const { getTokenFromHeaders } = require("../utils/utils"); +const logger = require("../utils/logger"); /** * Returns all monitors @@ -315,18 +316,21 @@ const deleteMonitor = async (req, res, next) => { try { const monitor = await req.db.deleteMonitor(req, res, next); // Delete associated checks,alerts,and notifications - await req.jobQueue.deleteJob(monitor); - await req.db.deleteChecks(monitor._id); - await req.db.deleteAlertByMonitorId(monitor._id); - await req.db.deletePageSpeedChecksByMonitorId(monitor._id); - await req.db.deleteNotificationsByMonitorId(monitor._id); + try { + await req.jobQueue.deleteJob(monitor); + await req.db.deleteChecks(monitor._id); + await req.db.deletePageSpeedChecksByMonitorId(monitor._id); + await req.db.deleteNotificationsByMonitorId(monitor._id); + } catch (error) { + logger.error( + `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`, + { + service: SERVICE_NAME, + error: error.message, + } + ); + } - /** - * TODO - * We should remove all checks and alerts associated with this monitor - * when it is deleted so there is no orphaned data - * We also need to make sure to stop all running services for this monitor - */ return res .status(200) .json({ success: true, msg: successMessages.MONITOR_DELETE }); From 38ee162f619d7217452d95b1d2594210b2967836 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Thu, 26 Sep 2024 11:01:38 +0800 Subject: [PATCH 03/25] improve error handling in network service, fix username form errors --- Client/src/Components/Inputs/Field/index.jsx | 11 ++++++++++- .../Components/TabPanels/Account/PasswordPanel.jsx | 10 +++++++++- Server/controllers/monitorController.js | 1 + Server/service/networkService.js | 11 +++++++++-- 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/Client/src/Components/Inputs/Field/index.jsx b/Client/src/Components/Inputs/Field/index.jsx index e7326ec41..1843a411b 100644 --- a/Client/src/Components/Inputs/Field/index.jsx +++ b/Client/src/Components/Inputs/Field/index.jsx @@ -16,6 +16,7 @@ import "./index.css"; * @param {Object} props * @param {string} [props.type] - Type of input field (e.g., 'text', 'password'). * @param {string} props.id - ID of the input field. + * @param {string} props.name - Name of the input field. * @param {string} [props.label] - Label for the input field. * @param {boolean} [props.https] - Indicates if it should display http or https. * @param {boolean} [props.isRequired] - Indicates if the field is required, will display a red asterisk. @@ -27,6 +28,7 @@ import "./index.css"; * @param {function} props.onChange - Function called on input change. * @param {string} [props.error] - Error message to display for the input field. * @param {boolean} [props.disabled] - Indicates if the input field is disabled. + * @param {boolean} [props.hidden] - Indicates if the input field is hidden. * @param {React.Ref} [ref] - Ref forwarded to the underlying `TextField` component. Allows for direct interactions such as focusing. */ @@ -35,6 +37,7 @@ const Field = forwardRef( { type = "text", id, + name, label, https, isRequired, @@ -47,6 +50,7 @@ const Field = forwardRef( onInput, error, disabled, + hidden, }, ref ) => { @@ -107,6 +111,7 @@ const Field = forwardRef( )} ); diff --git a/Client/src/Utils/Logger.js b/Client/src/Utils/Logger.js index 02fffd6be..79b2ed4f9 100644 --- a/Client/src/Utils/Logger.js +++ b/Client/src/Utils/Logger.js @@ -1,6 +1,16 @@ -const LOG_LEVEL = import.meta.env.VITE_APP_LOG_LEVEL; +import store from "../store"; +const LOG_LEVEL = import.meta.env.VITE_APP_LOG_LEVEL || "debug"; class Logger { - constructor(logLevel) { + constructor() { + let logLevel = LOG_LEVEL; + this.unsubscribe = store.subscribe(() => { + const state = store.getState(); + logLevel = state.settings.logLevel || "debug"; + this.updateLogLevel(logLevel); + }); + } + + updateLogLevel(logLevel) { const NO_OP = () => {}; if (logLevel === "none") { @@ -25,6 +35,12 @@ class Logger { } this.log = console.log.bind(console); } + + cleanup() { + if (this.unsubscribe) { + this.unsubscribe(); + } + } } -export const logger = new Logger(LOG_LEVEL); +export const logger = new Logger(); diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index 9545fb247..00b52f7f7 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -1,10 +1,18 @@ import axios from "axios"; -const BASE_URL = import.meta.env.VITE_APP_API_BASE_URL; +const BASE_URL = + import.meta.env.VITE_APP_API_BASE_URL || "http://localhost:5000/api/v1"; import { logger } from "./Logger"; class NetworkService { constructor(store) { this.store = store; - this.axiosInstance = axios.create({ baseURL: BASE_URL }); + let baseURL = BASE_URL; + this.axiosInstance = axios.create(); + this.setBaseUrl(baseURL); + this.unsubscribe = store.subscribe(() => { + const state = store.getState(); + baseURL = state.settings.apiBaseUrl || BASE_URL; + this.setBaseUrl(baseURL); + }); this.axiosInstance.interceptors.response.use( (response) => response, (error) => { @@ -17,6 +25,16 @@ class NetworkService { ); } + setBaseUrl = (url) => { + this.axiosInstance.defaults.baseURL = url; + }; + + cleanup() { + if (this.unsubscribe) { + this.unsubscribe(); + } + } + /** * * ************************************ @@ -640,6 +658,27 @@ class NetworkService { }, }); } + + /** + * + * ************************************ + * Create a new monitor + * ************************************ + * + * @async + * @param {Object} config - The configuration object. + * @param {string} config.authToken - The authorization token to be used in the request header. + * @param {Object} config.settings - The monitor object to be sent in the request body. + * @returns {Promise} The response from the axios POST request. + */ + async updateAppSettings(config) { + return this.axiosInstance.put(`/settings`, config.settings, { + headers: { + Authorization: `Bearer ${config.authToken}`, + "Content-Type": "application/json", + }, + }); + } } export default NetworkService; diff --git a/Client/src/store.js b/Client/src/store.js index 10c296392..644da5e62 100644 --- a/Client/src/store.js +++ b/Client/src/store.js @@ -51,3 +51,4 @@ export const store = configureStore({ }); export const persistor = persistStore(store); +export default store; From d418e2f1f860b1a0428bc5b5e6188ba683595762 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 27 Sep 2024 12:22:50 +0800 Subject: [PATCH 14/25] Add remainder of settings --- Client/src/Pages/AdvancedSettings/index.jsx | 78 ++++++++++++++++++--- 1 file changed, 68 insertions(+), 10 deletions(-) diff --git a/Client/src/Pages/AdvancedSettings/index.jsx b/Client/src/Pages/AdvancedSettings/index.jsx index 61cdd86a9..59e677138 100644 --- a/Client/src/Pages/AdvancedSettings/index.jsx +++ b/Client/src/Pages/AdvancedSettings/index.jsx @@ -1,8 +1,7 @@ import { useTheme } from "@emotion/react"; -import { Box, Stack, Typography, Button } from "@mui/material"; +import { Box, Stack, Typography } from "@mui/material"; import Field from "../../Components/Inputs/Field"; import Link from "../../Components/Link"; -import { logger } from "../../Utils/Logger"; import "./index.css"; import { useDispatch, useSelector } from "react-redux"; import { createToast } from "../../Utils/toastUtils"; @@ -10,18 +9,19 @@ import PropTypes from "prop-types"; import LoadingButton from "@mui/lab/LoadingButton"; import { ConfigBox } from "../Settings/styled"; import { useNavigate } from "react-router"; -import { - getAppSettings, - updateAppSettings, -} from "../../Features/Settings/settingsSlice"; -import { useEffect, useState } from "react"; +import { updateAppSettings } from "../../Features/Settings/settingsSlice"; +import { useState } from "react"; import Select from "../../Components/Inputs/Select"; const AdvancedSettings = ({ isAdmin }) => { - const theme = useTheme(); - const { user, authToken } = useSelector((state) => state.auth); - const dispatch = useDispatch(); const navigate = useNavigate(); + if (!isAdmin) { + navigate("/"); + } + + const theme = useTheme(); + const { authToken } = useSelector((state) => state.auth); + const dispatch = useDispatch(); const settings = useSelector((state) => state.settings); const [localSettings, setLocalSettings] = useState(settings); const logItems = [ @@ -143,6 +143,64 @@ const AdvancedSettings = ({ isAdmin }) => { /> + + + Server Settings + + Modify server settings here + + + + + + + + + + + About From c3edbb779a690f09ab16c647a5998090cf12f8bd Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 27 Sep 2024 13:02:01 +0800 Subject: [PATCH 15/25] Modify settingsService to load from env by default --- Server/service/settingsService.js | 52 ++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 12 deletions(-) diff --git a/Server/service/settingsService.js b/Server/service/settingsService.js index a5e7b2734..ca2509fe0 100644 --- a/Server/service/settingsService.js +++ b/Server/service/settingsService.js @@ -1,24 +1,52 @@ const AppSettings = require("../models/AppSettings"); const SERVICE_NAME = "SettingsService"; +CLIENT_HOST = process.env.CLIENT_HOST; +JWT_SECRET = process.env.JWT_SECRET; +DB_TYPE = process.env.DB_TYPE; +DB_CONNECTION_STRING = process.env.DB_CONNECTION_STRING; +REDIS_HOST = process.env.REDIS_HOST; +REDIS_PORT = process.env.REDIS_PORT; +TOKEN_TTL = process.env.TOKEN_TTL; +PAGESPEED_API_KEY = process.env.PAGESPEED_API_KEY; +SYSTEM_EMAIL_HOST = process.env.SYSTEM_EMAIL_HOST; +SYSTEM_EMAIL_PORT = process.env.SYSTEM_EMAIL_PORT; +SYSTEM_EMAIL_ADDRESS = process.env.SYSTEM_EMAIL_ADDRESS; +SYSTEM_EMAIL_PASSWORD = process.env.SYSTEM_EMAIL_PASSWORD; class SettingsService { constructor() { - this.settings = null; + this.settings = { + clientHost: CLIENT_HOST, + jwtSecret: JWT_SECRET, + dbType: DB_TYPE, + dbConnectionString: DB_CONNECTION_STRING, + redisHost: REDIS_HOST, + redisPort: REDIS_PORT, + tokenTTL: TOKEN_TTL, + pagespeedApiKey: PAGESPEED_API_KEY, + systemEmailHost: SYSTEM_EMAIL_HOST, + systemEmailPort: SYSTEM_EMAIL_PORT, + systemEmailAddress: SYSTEM_EMAIL_ADDRESS, + systemEmailPassword: SYSTEM_EMAIL_PASSWORD, + }; } async loadSettings() { - if (!this.settings) { - try { - this.settings = await AppSettings.findOne(); - if (!this.settings) { - throw new Error("Settings not found"); - } - return this.settings; - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "loadSettings") : null; - throw error; + try { + const dbSettings = await AppSettings.findOne(); + if (!this.settings) { + throw new Error("Settings not found"); } + for (const key in this.settings) { + if (this.settings[key] === undefined && dbSettings[key] !== undefined) { + this.settings[key] = dbSettings[key]; + } + } + return this.settings; + } catch (error) { + error.service === undefined ? (error.service = SERVICE_NAME) : null; + error.method === undefined ? (error.method = "loadSettings") : null; + throw error; } } From f4bbbc5fa9f299e7b6db172d1f67ee0609a7b25d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 27 Sep 2024 13:10:29 +0800 Subject: [PATCH 16/25] fix conditional loadingof settings --- Server/service/settingsService.js | 55 ++++++++++++++----------------- 1 file changed, 25 insertions(+), 30 deletions(-) diff --git a/Server/service/settingsService.js b/Server/service/settingsService.js index ca2509fe0..b1027a17c 100644 --- a/Server/service/settingsService.js +++ b/Server/service/settingsService.js @@ -1,34 +1,23 @@ +const { env } = require("process"); const AppSettings = require("../models/AppSettings"); const SERVICE_NAME = "SettingsService"; - -CLIENT_HOST = process.env.CLIENT_HOST; -JWT_SECRET = process.env.JWT_SECRET; -DB_TYPE = process.env.DB_TYPE; -DB_CONNECTION_STRING = process.env.DB_CONNECTION_STRING; -REDIS_HOST = process.env.REDIS_HOST; -REDIS_PORT = process.env.REDIS_PORT; -TOKEN_TTL = process.env.TOKEN_TTL; -PAGESPEED_API_KEY = process.env.PAGESPEED_API_KEY; -SYSTEM_EMAIL_HOST = process.env.SYSTEM_EMAIL_HOST; -SYSTEM_EMAIL_PORT = process.env.SYSTEM_EMAIL_PORT; -SYSTEM_EMAIL_ADDRESS = process.env.SYSTEM_EMAIL_ADDRESS; -SYSTEM_EMAIL_PASSWORD = process.env.SYSTEM_EMAIL_PASSWORD; +const envConfig = { + clientHost: process.env.CLIENT_HOST, + jwtSecret: process.env.JWT_SECRET, + dbType: process.env.DB_TYPE, + dbConnectionString: process.env.DB_CONNECTION_STRING, + redisHost: process.env.REDIS_HOST, + redisPort: process.env.REDIS_PORT, + tokenTTL: process.env.TOKEN_TTL, + pagespeedApiKey: process.env.PAGESPEED_API_KEY, + systemEmailHost: process.env.SYSTEM_EMAIL_HOST, + systemEmailPort: process.env.SYSTEM_EMAIL_PORT, + systemEmailAddress: process.env.SYSTEM_EMAIL_ADDRESS, + systemEmailPassword: process.env.SYSTEM_EMAIL_PASSWORD, +}; class SettingsService { constructor() { - this.settings = { - clientHost: CLIENT_HOST, - jwtSecret: JWT_SECRET, - dbType: DB_TYPE, - dbConnectionString: DB_CONNECTION_STRING, - redisHost: REDIS_HOST, - redisPort: REDIS_PORT, - tokenTTL: TOKEN_TTL, - pagespeedApiKey: PAGESPEED_API_KEY, - systemEmailHost: SYSTEM_EMAIL_HOST, - systemEmailPort: SYSTEM_EMAIL_PORT, - systemEmailAddress: SYSTEM_EMAIL_ADDRESS, - systemEmailPassword: SYSTEM_EMAIL_PASSWORD, - }; + this.settings = envConfig; } async loadSettings() { @@ -37,8 +26,8 @@ class SettingsService { if (!this.settings) { throw new Error("Settings not found"); } - for (const key in this.settings) { - if (this.settings[key] === undefined && dbSettings[key] !== undefined) { + for (const key in envConfig) { + if (envConfig[key] === undefined && dbSettings[key] !== undefined) { this.settings[key] = dbSettings[key]; } } @@ -52,7 +41,13 @@ class SettingsService { async reloadSettings() { try { - this.settings = await AppSettings.findOne(); + const dbSettings = await AppSettings.findOne(); + + for (const key in envConfig) { + if (envConfig[key] === undefined && dbSettings[key] !== undefined) { + this.settings[key] = dbSettings[key]; + } + } if (!this.settings) { throw new Error("Settings not found"); } From 43a56e1de14e95e5e0a227d4a954b1636aea0a50 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 27 Sep 2024 13:17:28 +0800 Subject: [PATCH 17/25] Fix setting name --- Server/service/settingsService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/service/settingsService.js b/Server/service/settingsService.js index b1027a17c..abec31146 100644 --- a/Server/service/settingsService.js +++ b/Server/service/settingsService.js @@ -8,7 +8,7 @@ const envConfig = { dbConnectionString: process.env.DB_CONNECTION_STRING, redisHost: process.env.REDIS_HOST, redisPort: process.env.REDIS_PORT, - tokenTTL: process.env.TOKEN_TTL, + jwtTTL: process.env.TOKEN_TTL, pagespeedApiKey: process.env.PAGESPEED_API_KEY, systemEmailHost: process.env.SYSTEM_EMAIL_HOST, systemEmailPort: process.env.SYSTEM_EMAIL_PORT, From b8984c01603aeae8171a2416cf2abd2bd87087ea Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 27 Sep 2024 13:38:57 +0800 Subject: [PATCH 18/25] Restrict access to advanced settings page --- Client/src/Pages/AdvancedSettings/index.jsx | 12 ++++--- Client/src/Pages/Settings/index.jsx | 38 +++++++++++---------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/Client/src/Pages/AdvancedSettings/index.jsx b/Client/src/Pages/AdvancedSettings/index.jsx index 59e677138..29a9fe53c 100644 --- a/Client/src/Pages/AdvancedSettings/index.jsx +++ b/Client/src/Pages/AdvancedSettings/index.jsx @@ -10,14 +10,17 @@ import LoadingButton from "@mui/lab/LoadingButton"; import { ConfigBox } from "../Settings/styled"; import { useNavigate } from "react-router"; import { updateAppSettings } from "../../Features/Settings/settingsSlice"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import Select from "../../Components/Inputs/Select"; const AdvancedSettings = ({ isAdmin }) => { const navigate = useNavigate(); - if (!isAdmin) { - navigate("/"); - } + + useEffect(() => { + if (!isAdmin) { + navigate("/"); + } + }, [navigate, isAdmin]); const theme = useTheme(); const { authToken } = useSelector((state) => state.auth); @@ -75,7 +78,6 @@ const AdvancedSettings = ({ isAdmin }) => { noValidate spellCheck="false" > - \{" "} Client Settings diff --git a/Client/src/Pages/Settings/index.jsx b/Client/src/Pages/Settings/index.jsx index 63199f9f4..bf8cfcc79 100644 --- a/Client/src/Pages/Settings/index.jsx +++ b/Client/src/Pages/Settings/index.jsx @@ -251,26 +251,28 @@ const Settings = ({ isAdmin }) => { )} - - - Advanced Settings - - Click here to modify advanced settings - - - + {isAdmin && ( + - + Advanced Settings + + Click here to modify advanced settings + - - + + + + + + + )} About From 453c8f6b12e474eac14b1118af27f2502486ad02 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Fri, 27 Sep 2024 13:46:50 +0800 Subject: [PATCH 19/25] bump FE dependencies --- Client/package-lock.json | 170 ++++++++++++++++++++------------------- 1 file changed, 87 insertions(+), 83 deletions(-) diff --git a/Client/package-lock.json b/Client/package-lock.json index 2f1543bba..4077ce2bf 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -1759,9 +1759,9 @@ } }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.18.1.tgz", - "integrity": "sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.22.4.tgz", + "integrity": "sha512-Fxamp4aEZnfPOcGA8KSNEohV8hX7zVHOemC8jVBoBUHu5zpJK/Eu3uJwt6BMgy9fkvzxDaurgj96F/NiLukF2w==", "cpu": [ "arm" ], @@ -1772,9 +1772,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.18.1.tgz", - "integrity": "sha512-F/tkdw0WSs4ojqz5Ovrw5r9odqzFjb5LIgHdHZG65dFI1lWTWRVy32KDJLKRISHgJvqUeUhdIvy43fX41znyDg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.22.4.tgz", + "integrity": "sha512-VXoK5UMrgECLYaMuGuVTOx5kcuap1Jm8g/M83RnCHBKOqvPPmROFJGQaZhGccnsFtfXQ3XYa4/jMCJvZnbJBdA==", "cpu": [ "arm64" ], @@ -1785,9 +1785,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.18.1.tgz", - "integrity": "sha512-vk+ma8iC1ebje/ahpxpnrfVQJibTMyHdWpOGZ3JpQ7Mgn/3QNHmPq7YwjZbIE7km73dH5M1e6MRRsnEBW7v5CQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.22.4.tgz", + "integrity": "sha512-xMM9ORBqu81jyMKCDP+SZDhnX2QEVQzTcC6G18KlTQEzWK8r/oNZtKuZaCcHhnsa6fEeOBionoyl5JsAbE/36Q==", "cpu": [ "arm64" ], @@ -1798,9 +1798,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.18.1.tgz", - "integrity": "sha512-IgpzXKauRe1Tafcej9STjSSuG0Ghu/xGYH+qG6JwsAUxXrnkvNHcq/NL6nz1+jzvWAnQkuAJ4uIwGB48K9OCGA==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.22.4.tgz", + "integrity": "sha512-aJJyYKQwbHuhTUrjWjxEvGnNNBCnmpHDvrb8JFDbeSH3m2XdHcxDd3jthAzvmoI8w/kSjd2y0udT+4okADsZIw==", "cpu": [ "x64" ], @@ -1811,9 +1811,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.18.1.tgz", - "integrity": "sha512-P9bSiAUnSSM7EmyRK+e5wgpqai86QOSv8BwvkGjLwYuOpaeomiZWifEos517CwbG+aZl1T4clSE1YqqH2JRs+g==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.22.4.tgz", + "integrity": "sha512-j63YtCIRAzbO+gC2L9dWXRh5BFetsv0j0va0Wi9epXDgU/XUi5dJKo4USTttVyK7fGw2nPWK0PbAvyliz50SCQ==", "cpu": [ "arm" ], @@ -1824,9 +1824,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.18.1.tgz", - "integrity": "sha512-5RnjpACoxtS+aWOI1dURKno11d7krfpGDEn19jI8BuWmSBbUC4ytIADfROM1FZrFhQPSoP+KEa3NlEScznBTyQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.22.4.tgz", + "integrity": "sha512-dJnWUgwWBX1YBRsuKKMOlXCzh2Wu1mlHzv20TpqEsfdZLb3WoJW2kIEsGwLkroYf24IrPAvOT/ZQ2OYMV6vlrg==", "cpu": [ "arm" ], @@ -1837,9 +1837,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.18.1.tgz", - "integrity": "sha512-8mwmGD668m8WaGbthrEYZ9CBmPug2QPGWxhJxh/vCgBjro5o96gL04WLlg5BA233OCWLqERy4YUzX3bJGXaJgQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.22.4.tgz", + "integrity": "sha512-AdPRoNi3NKVLolCN/Sp4F4N1d98c4SBnHMKoLuiG6RXgoZ4sllseuGioszumnPGmPM2O7qaAX/IJdeDU8f26Aw==", "cpu": [ "arm64" ], @@ -1850,9 +1850,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.18.1.tgz", - "integrity": "sha512-dJX9u4r4bqInMGOAQoGYdwDP8lQiisWb9et+T84l2WXk41yEej8v2iGKodmdKimT8cTAYt0jFb+UEBxnPkbXEQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.22.4.tgz", + "integrity": "sha512-Gl0AxBtDg8uoAn5CCqQDMqAx22Wx22pjDOjBdmG0VIWX3qUBHzYmOKh8KXHL4UpogfJ14G4wk16EQogF+v8hmA==", "cpu": [ "arm64" ], @@ -1863,9 +1863,9 @@ ] }, "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.18.1.tgz", - "integrity": "sha512-V72cXdTl4EI0x6FNmho4D502sy7ed+LuVW6Ym8aI6DRQ9hQZdp5sj0a2usYOlqvFBNKQnLQGwmYnujo2HvjCxQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.22.4.tgz", + "integrity": "sha512-3aVCK9xfWW1oGQpTsYJJPF6bfpWfhbRnhdlyhak2ZiyFLDaayz0EP5j9V1RVLAAxlmWKTDfS9wyRyY3hvhPoOg==", "cpu": [ "ppc64" ], @@ -1876,9 +1876,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.18.1.tgz", - "integrity": "sha512-f+pJih7sxoKmbjghrM2RkWo2WHUW8UbfxIQiWo5yeCaCM0TveMEuAzKJte4QskBp1TIinpnRcxkquY+4WuY/tg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.22.4.tgz", + "integrity": "sha512-ePYIir6VYnhgv2C5Xe9u+ico4t8sZWXschR6fMgoPUK31yQu7hTEJb7bCqivHECwIClJfKgE7zYsh1qTP3WHUA==", "cpu": [ "riscv64" ], @@ -1889,9 +1889,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.18.1.tgz", - "integrity": "sha512-qb1hMMT3Fr/Qz1OKovCuUM11MUNLUuHeBC2DPPAWUYYUAOFWaxInaTwTQmc7Fl5La7DShTEpmYwgdt2hG+4TEg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.22.4.tgz", + "integrity": "sha512-GqFJ9wLlbB9daxhVlrTe61vJtEY99/xB3C8e4ULVsVfflcpmR6c8UZXjtkMA6FhNONhj2eA5Tk9uAVw5orEs4Q==", "cpu": [ "s390x" ], @@ -1902,9 +1902,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.18.1.tgz", - "integrity": "sha512-7O5u/p6oKUFYjRbZkL2FLbwsyoJAjyeXHCU3O4ndvzg2OFO2GinFPSJFGbiwFDaCFc+k7gs9CF243PwdPQFh5g==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.22.4.tgz", + "integrity": "sha512-87v0ol2sH9GE3cLQLNEy0K/R0pz1nvg76o8M5nhMR0+Q+BBGLnb35P0fVz4CQxHYXaAOhE8HhlkaZfsdUOlHwg==", "cpu": [ "x64" ], @@ -1915,9 +1915,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.18.1.tgz", - "integrity": "sha512-pDLkYITdYrH/9Cv/Vlj8HppDuLMDUBmgsM0+N+xLtFd18aXgM9Nyqupb/Uw+HeidhfYg2lD6CXvz6CjoVOaKjQ==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.22.4.tgz", + "integrity": "sha512-UV6FZMUgePDZrFjrNGIWzDo/vABebuXBhJEqrHxrGiU6HikPy0Z3LfdtciIttEUQfuDdCn8fqh7wiFJjCNwO+g==", "cpu": [ "x64" ], @@ -1928,9 +1928,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.18.1.tgz", - "integrity": "sha512-W2ZNI323O/8pJdBGil1oCauuCzmVd9lDmWBBqxYZcOqWD6aWqJtVBQ1dFrF4dYpZPks6F+xCZHfzG5hYlSHZ6g==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.22.4.tgz", + "integrity": "sha512-BjI+NVVEGAXjGWYHz/vv0pBqfGoUH0IGZ0cICTn7kB9PyjrATSkX+8WkguNjWoj2qSr1im/+tTGRaY+4/PdcQw==", "cpu": [ "arm64" ], @@ -1941,9 +1941,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.18.1.tgz", - "integrity": "sha512-ELfEX1/+eGZYMaCIbK4jqLxO1gyTSOIlZr6pbC4SRYFaSIDVKOnZNMdoZ+ON0mrFDp4+H5MhwNC1H/AhE3zQLg==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.22.4.tgz", + "integrity": "sha512-SiWG/1TuUdPvYmzmYnmd3IEifzR61Tragkbx9D3+R8mzQqDBz8v+BvZNDlkiTtI9T15KYZhP0ehn3Dld4n9J5g==", "cpu": [ "ia32" ], @@ -1954,9 +1954,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.18.1.tgz", - "integrity": "sha512-yjk2MAkQmoaPYCSu35RLJ62+dz358nE83VfTePJRp8CG7aMg25mEJYpXFiD+NcevhX8LxD5OP5tktPXnXN7GDw==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.22.4.tgz", + "integrity": "sha512-j8pPKp53/lq9lMXN57S8cFz0MynJk8OWNuUnXct/9KCpKU7DgU3bYMJhwWmcqC0UU29p8Lr0/7KEVcaM6bf47Q==", "cpu": [ "x64" ], @@ -5249,9 +5249,9 @@ } }, "node_modules/picocolors": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz", - "integrity": "sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", + "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", "license": "ISC" }, "node_modules/picomatch": { @@ -5277,9 +5277,9 @@ } }, "node_modules/postcss": { - "version": "8.4.39", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.39.tgz", - "integrity": "sha512-0vzE+lAiG7hZl1/9I8yzKLx3aR9Xbof3fBHKunvMfOCYAtMhrsnccJY2iTURb9EZd5+pLuiNV9/c/GZJOHsgIw==", + "version": "8.4.47", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.47.tgz", + "integrity": "sha512-56rxCq7G/XfB4EkXq9Egn5GCqugWvDFjafDOThIdMBsI15iqPqR5r15TfSr1YPYeEI19YeaXMCbY6u88Y76GLQ==", "funding": [ { "type": "opencollective", @@ -5297,8 +5297,8 @@ "license": "MIT", "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.1", - "source-map-js": "^1.2.0" + "picocolors": "^1.1.0", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -5678,9 +5678,9 @@ "license": "Unlicense" }, "node_modules/rollup": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.18.1.tgz", - "integrity": "sha512-Elx2UT8lzxxOXMpy5HWQGZqkrQOtrVDDa/bm9l10+U4rQnVzbL/LgZ4NOM1MPIDyHk69W4InuYDF5dzRh4Kw1A==", + "version": "4.22.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.22.4.tgz", + "integrity": "sha512-vD8HJ5raRcWOyymsR6Z3o6+RzfEPCnVLMFJ6vRslO1jt4LO6dUo5Qnpg7y4RkZFM2DMe3WUirkI5c16onjrc6A==", "license": "MIT", "dependencies": { "@types/estree": "1.0.5" @@ -5693,22 +5693,22 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.18.1", - "@rollup/rollup-android-arm64": "4.18.1", - "@rollup/rollup-darwin-arm64": "4.18.1", - "@rollup/rollup-darwin-x64": "4.18.1", - "@rollup/rollup-linux-arm-gnueabihf": "4.18.1", - "@rollup/rollup-linux-arm-musleabihf": "4.18.1", - "@rollup/rollup-linux-arm64-gnu": "4.18.1", - "@rollup/rollup-linux-arm64-musl": "4.18.1", - "@rollup/rollup-linux-powerpc64le-gnu": "4.18.1", - "@rollup/rollup-linux-riscv64-gnu": "4.18.1", - "@rollup/rollup-linux-s390x-gnu": "4.18.1", - "@rollup/rollup-linux-x64-gnu": "4.18.1", - "@rollup/rollup-linux-x64-musl": "4.18.1", - "@rollup/rollup-win32-arm64-msvc": "4.18.1", - "@rollup/rollup-win32-ia32-msvc": "4.18.1", - "@rollup/rollup-win32-x64-msvc": "4.18.1", + "@rollup/rollup-android-arm-eabi": "4.22.4", + "@rollup/rollup-android-arm64": "4.22.4", + "@rollup/rollup-darwin-arm64": "4.22.4", + "@rollup/rollup-darwin-x64": "4.22.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.22.4", + "@rollup/rollup-linux-arm-musleabihf": "4.22.4", + "@rollup/rollup-linux-arm64-gnu": "4.22.4", + "@rollup/rollup-linux-arm64-musl": "4.22.4", + "@rollup/rollup-linux-powerpc64le-gnu": "4.22.4", + "@rollup/rollup-linux-riscv64-gnu": "4.22.4", + "@rollup/rollup-linux-s390x-gnu": "4.22.4", + "@rollup/rollup-linux-x64-gnu": "4.22.4", + "@rollup/rollup-linux-x64-musl": "4.22.4", + "@rollup/rollup-win32-arm64-msvc": "4.22.4", + "@rollup/rollup-win32-ia32-msvc": "4.22.4", + "@rollup/rollup-win32-x64-msvc": "4.22.4", "fsevents": "~2.3.2" } }, @@ -5887,9 +5887,9 @@ } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" @@ -6266,14 +6266,14 @@ } }, "node_modules/vite": { - "version": "5.3.4", - "resolved": "https://registry.npmjs.org/vite/-/vite-5.3.4.tgz", - "integrity": "sha512-Cw+7zL3ZG9/NZBB8C+8QbQZmR54GwqIz+WMI4b3JgdYJvX+ny9AjJXqkGQlDXSXRP9rP0B4tbciRMOVEKulVOA==", + "version": "5.4.8", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.8.tgz", + "integrity": "sha512-FqrItQ4DT1NC4zCUqMB4c4AZORMKIa0m8/URVCZ77OZ/QSNeJ54bU1vrFADbDsuwfIPcgknRkmqakQcgnL4GiQ==", "license": "MIT", "dependencies": { "esbuild": "^0.21.3", - "postcss": "^8.4.39", - "rollup": "^4.13.0" + "postcss": "^8.4.43", + "rollup": "^4.20.0" }, "bin": { "vite": "bin/vite.js" @@ -6292,6 +6292,7 @@ "less": "*", "lightningcss": "^1.21.0", "sass": "*", + "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" @@ -6309,6 +6310,9 @@ "sass": { "optional": true }, + "sass-embedded": { + "optional": true + }, "stylus": { "optional": true }, From 114f4ffb0514a6e14aa8a69ce93d243fec613299 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 30 Sep 2024 09:52:47 +0800 Subject: [PATCH 20/25] refactor reloadSettings --- Server/controllers/settingsController.js | 4 ++-- Server/service/settingsService.js | 25 +++++++++--------------- 2 files changed, 11 insertions(+), 18 deletions(-) diff --git a/Server/controllers/settingsController.js b/Server/controllers/settingsController.js index c44363bfa..77895caea 100644 --- a/Server/controllers/settingsController.js +++ b/Server/controllers/settingsController.js @@ -31,11 +31,11 @@ const updateAppSettings = async (req, res, next) => { try { const settings = await req.db.updateAppSettings(req.body); - await req.settingsService.reloadSettings(); + const updatedSettings = await req.settingsService.reloadSettings(); return res.status(200).json({ success: true, msg: successMessages.UPDATE_APP_SETTINGS, - data: settings, + data: updatedSettings, }); } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; diff --git a/Server/service/settingsService.js b/Server/service/settingsService.js index b1027a17c..7cf76f7cc 100644 --- a/Server/service/settingsService.js +++ b/Server/service/settingsService.js @@ -15,6 +15,7 @@ const envConfig = { systemEmailAddress: process.env.SYSTEM_EMAIL_ADDRESS, systemEmailPassword: process.env.SYSTEM_EMAIL_PASSWORD, }; + class SettingsService { constructor() { this.settings = envConfig; @@ -26,11 +27,18 @@ class SettingsService { if (!this.settings) { throw new Error("Settings not found"); } + + // Try to load settings from env first, if not found, load from db for (const key in envConfig) { if (envConfig[key] === undefined && dbSettings[key] !== undefined) { this.settings[key] = dbSettings[key]; } } + + if (!this.settings) { + throw new Error("Settings not found"); + } + return this.settings; } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; @@ -40,22 +48,7 @@ class SettingsService { } async reloadSettings() { - try { - const dbSettings = await AppSettings.findOne(); - - for (const key in envConfig) { - if (envConfig[key] === undefined && dbSettings[key] !== undefined) { - this.settings[key] = dbSettings[key]; - } - } - if (!this.settings) { - throw new Error("Settings not found"); - } - } catch (error) { - error.service === undefined ? (error.service = SERVICE_NAME) : null; - error.method === undefined ? (error.method = "reloadSettings") : null; - throw error; - } + return this.loadSettings(); } getSettings() { From b6a7cfa7cc87823c2fc1387072b3ca287fde333d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 30 Sep 2024 09:56:39 +0800 Subject: [PATCH 21/25] refactor getAppSettings endpoint to user settingsService as source of truth --- Server/controllers/settingsController.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Server/controllers/settingsController.js b/Server/controllers/settingsController.js index 77895caea..e3444436b 100644 --- a/Server/controllers/settingsController.js +++ b/Server/controllers/settingsController.js @@ -4,7 +4,7 @@ const { updateAppSettingsBodyValidation } = require("../validation/joi"); const getAppSettings = async (req, res, next) => { try { - const settings = await req.db.getAppSettings(); + const settings = await req.settingsService.getSettings(); return res.status(200).json({ success: true, msg: successMessages.GET_APP_SETTINGS, @@ -30,7 +30,7 @@ const updateAppSettings = async (req, res, next) => { } try { - const settings = await req.db.updateAppSettings(req.body); + await req.db.updateAppSettings(req.body); const updatedSettings = await req.settingsService.reloadSettings(); return res.status(200).json({ success: true, From 73ac91c2014e7a960619c06972a6b9f11a5ea7d3 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 30 Sep 2024 10:08:37 +0800 Subject: [PATCH 22/25] Generate random JWT on first account signup if one is not supplied by env --- Server/controllers/authController.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index cd9284479..8d41f916b 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -18,6 +18,7 @@ const { errorMessages, successMessages } = require("../utils/messages"); var jwt = require("jsonwebtoken"); const SERVICE_NAME = "AuthController"; const { getTokenFromHeaders } = require("../utils/utils"); +const crypto = require("crypto"); /** * Creates and returns JWT token with an arbitrary payload @@ -61,7 +62,12 @@ const registerController = async (req, res, next) => { const superAdminExists = await req.db.checkSuperadmin(req, res); if (superAdminExists) { await req.db.getInviteTokenAndDelete(inviteToken); + } else { + // This is the first account, create JWT secret to use if one is not supplied by env + const jwtSecret = crypto.randomBytes(64).toString("hex"); + await req.db.updateAppSettings({ jwtSecret }); } + const newUser = await req.db.insertUser({ ...req.body }, req.file); logger.info(successMessages.AUTH_CREATE_USER, { service: SERVICE_NAME, From 6268b2573c560062e8007a56a33514589edd47a9 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 30 Sep 2024 11:14:53 +0800 Subject: [PATCH 23/25] remove jwtSecret from settings --- Client/src/App.jsx | 2 +- Client/src/Features/Settings/settingsSlice.js | 22 +++------- Client/src/Pages/AdvancedSettings/index.jsx | 43 ++++++++++++++----- Server/controllers/settingsController.js | 6 ++- Server/middleware/verifyJWT.js | 1 - Server/service/settingsService.js | 6 +-- Server/validation/joi.js | 1 - 7 files changed, 47 insertions(+), 34 deletions(-) diff --git a/Client/src/App.jsx b/Client/src/App.jsx index 7bfc630fd..df3e79c80 100644 --- a/Client/src/App.jsx +++ b/Client/src/App.jsx @@ -53,7 +53,7 @@ function App() { const dispatch = useDispatch(); useEffect(() => { - dispatch(getAppSettings(authToken)); + dispatch(getAppSettings({ authToken })); }, [dispatch, authToken]); // Cleanup diff --git a/Client/src/Features/Settings/settingsSlice.js b/Client/src/Features/Settings/settingsSlice.js index 8bec2f87c..ab6a5c746 100644 --- a/Client/src/Features/Settings/settingsSlice.js +++ b/Client/src/Features/Settings/settingsSlice.js @@ -5,26 +5,14 @@ const initialState = { isLoading: false, apiBaseUrl: "http://localhost:5000/api/v1", logLevel: "debug", - clientHost: "http://localhost:5173", - jwtSecret: "my_secret", - dbType: "MongoDB", - dbConnectionString: "mongodb://localhost:27017/uptime_db", - redisHost: "127.0.0.1", - redisPort: 6379, - jwtTTL: "99d", - pagespeedApiKey: "", - systemEmailHost: "smtp.gmail.com", - systemEmailPort: 465, - systemEmailAddress: "", - systemEmailPassword: "", }; export const getAppSettings = createAsyncThunk( "settings/getSettings", - async (authToken, thunkApi) => { + async (data, thunkApi) => { try { const res = await networkService.getAppSettings({ - authToken: authToken, + authToken: data.authToken, }); return res.data; } catch (error) { @@ -83,7 +71,8 @@ const handleGetSettingsFulfilled = (state, action) => { state.isLoading = false; state.success = action.payload.success; state.msg = action.payload.msg; - Object.assign(state, action.payload.data); + state.apiBaseUrl = action.payload.data.apiBaseUrl; + state.logLevel = action.payload.data.logLevel; }; const handleGetSettingsRejected = (state, action) => { state.isLoading = false; @@ -94,7 +83,8 @@ const handleUpdateSettingsFulfilled = (state, action) => { state.isLoading = false; state.success = action.payload.success; state.msg = action.payload.msg; - Object.assign(state, action.payload.data); + state.apiBaseUrl = action.payload.data.apiBaseUrl; + state.logLevel = action.payload.data.logLevel; }; const handleUpdateSettingsRejected = (state, action) => { state.isLoading = false; diff --git a/Client/src/Pages/AdvancedSettings/index.jsx b/Client/src/Pages/AdvancedSettings/index.jsx index 29a9fe53c..38c0a3b12 100644 --- a/Client/src/Pages/AdvancedSettings/index.jsx +++ b/Client/src/Pages/AdvancedSettings/index.jsx @@ -9,7 +9,10 @@ import PropTypes from "prop-types"; import LoadingButton from "@mui/lab/LoadingButton"; import { ConfigBox } from "../Settings/styled"; import { useNavigate } from "react-router"; -import { updateAppSettings } from "../../Features/Settings/settingsSlice"; +import { + getAppSettings, + updateAppSettings, +} from "../../Features/Settings/settingsSlice"; import { useState, useEffect } from "react"; import Select from "../../Components/Inputs/Select"; @@ -26,7 +29,33 @@ const AdvancedSettings = ({ isAdmin }) => { const { authToken } = useSelector((state) => state.auth); const dispatch = useDispatch(); const settings = useSelector((state) => state.settings); - const [localSettings, setLocalSettings] = useState(settings); + const [localSettings, setLocalSettings] = useState({ + apiBaseUrl: "", + logLevel: "debug", + systemEmailHost: "", + systemEmailPort: "", + systemEmailAddress: "", + systemEmailPassword: "", + jwtTTL: "", + dbType: "", + redisHost: "", + redisPort: "", + pagespeedApiKey: "", + }); + + useEffect(() => { + const getSettings = async () => { + const action = await dispatch(getAppSettings({ authToken })); + if (action.payload.success) { + console.log(action.payload.data); + setLocalSettings(action.payload.data); + } else { + createToast({ body: "Failed to get settings" }); + } + }; + getSettings(); + }, [authToken, dispatch]); + const logItems = [ { _id: 1, name: "none" }, { _id: 2, name: "debug" }, @@ -58,6 +87,8 @@ const AdvancedSettings = ({ isAdmin }) => { ); let body = ""; if (action.payload.success) { + console.log(action.payload.data); + setLocalSettings(action.payload.data); body = "Settings saved successfully"; } else { body = "Failed to save settings"; @@ -153,14 +184,6 @@ const AdvancedSettings = ({ isAdmin }) => { - { try { - const settings = await req.settingsService.getSettings(); + const settings = { ...(await req.settingsService.getSettings()) }; + delete settings.jwtSecret; return res.status(200).json({ success: true, msg: successMessages.GET_APP_SETTINGS, @@ -31,7 +32,8 @@ const updateAppSettings = async (req, res, next) => { try { await req.db.updateAppSettings(req.body); - const updatedSettings = await req.settingsService.reloadSettings(); + const updatedSettings = { ...(await req.settingsService.reloadSettings()) }; + delete updatedSettings.jwtSecret; return res.status(200).json({ success: true, msg: successMessages.UPDATE_APP_SETTINGS, diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js index ffbcd55a5..0007bad39 100644 --- a/Server/middleware/verifyJWT.js +++ b/Server/middleware/verifyJWT.js @@ -36,7 +36,6 @@ const verifyJWT = (req, res, next) => { const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); // Verify the token's authenticity const { jwtSecret } = req.settingsService.getSettings(); - jwt.verify(parsedToken, jwtSecret, (err, decoded) => { if (err) { return res diff --git a/Server/service/settingsService.js b/Server/service/settingsService.js index 13b19eb6c..752f86607 100644 --- a/Server/service/settingsService.js +++ b/Server/service/settingsService.js @@ -2,6 +2,8 @@ const { env } = require("process"); const AppSettings = require("../models/AppSettings"); const SERVICE_NAME = "SettingsService"; const envConfig = { + logLevel: undefined, + apiBaseUrl: undefined, clientHost: process.env.CLIENT_HOST, jwtSecret: process.env.JWT_SECRET, dbType: process.env.DB_TYPE, @@ -18,7 +20,7 @@ const envConfig = { class SettingsService { constructor() { - this.settings = envConfig; + this.settings = { ...envConfig }; } async loadSettings() { @@ -27,7 +29,6 @@ class SettingsService { if (!this.settings) { throw new Error("Settings not found"); } - // Try to load settings from env first, if not found, load from db for (const key in envConfig) { if (envConfig[key] === undefined && dbSettings[key] !== undefined) { @@ -38,7 +39,6 @@ class SettingsService { if (!this.settings) { throw new Error("Settings not found"); } - return this.settings; } catch (error) { error.service === undefined ? (error.service = SERVICE_NAME) : null; diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 7b26ea8e1..91ba53b94 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -389,7 +389,6 @@ const updateAppSettingsBodyValidation = joi.object({ apiBaseUrl: joi.string().allow(""), logLevel: joi.string().valid("debug", "none", "error", "warn").allow(""), clientHost: joi.string().allow(""), - jwtSecret: joi.string().allow(""), dbType: joi.string().allow(""), dbConnectionString: joi.string().allow(""), redisHost: joi.string().allow(""), From 9b3085c84d8971cdc59d973a9119e7016d8ed2a1 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 30 Sep 2024 11:30:52 +0800 Subject: [PATCH 24/25] Add default empty value for pagespeedApiKey in settings --- Client/src/App.jsx | 4 +++- Client/src/Pages/AdvancedSettings/index.jsx | 2 +- Server/models/AppSettings.js | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/Client/src/App.jsx b/Client/src/App.jsx index df3e79c80..56c279e78 100644 --- a/Client/src/App.jsx +++ b/Client/src/App.jsx @@ -53,7 +53,9 @@ function App() { const dispatch = useDispatch(); useEffect(() => { - dispatch(getAppSettings({ authToken })); + if (authToken) { + dispatch(getAppSettings({ authToken })); + } }, [dispatch, authToken]); // Cleanup diff --git a/Client/src/Pages/AdvancedSettings/index.jsx b/Client/src/Pages/AdvancedSettings/index.jsx index 38c0a3b12..7609b2578 100644 --- a/Client/src/Pages/AdvancedSettings/index.jsx +++ b/Client/src/Pages/AdvancedSettings/index.jsx @@ -119,7 +119,7 @@ const AdvancedSettings = ({ isAdmin }) => { diff --git a/Server/models/AppSettings.js b/Server/models/AppSettings.js index 79ede650a..f95d58f4b 100644 --- a/Server/models/AppSettings.js +++ b/Server/models/AppSettings.js @@ -48,6 +48,7 @@ const AppSettingsSchema = mongoose.Schema( }, pagespeedApiKey: { type: String, + default: "", }, systemEmailHost: { type: String, From bc85062dd154114ff05aa705d8300724f330f93b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 30 Sep 2024 12:24:53 +0800 Subject: [PATCH 25/25] Add docker images and compose for dist --- Docker/dist/build_images.sh | 24 ++++ Docker/dist/client.Dockerfile | 19 +++ .../{quickstart => dist}/docker-compose.yaml | 19 ++- Docker/dist/mongoDB.Dockerfile | 3 + Docker/dist/redis.Dockerfile | 2 + Docker/dist/server.Dockerfile | 13 ++ Docker/docker-compose.yaml | 2 +- Docker/nginx/conf.d/dist_default.conf | 35 +++++ Docker/quickstart/quickstart.sh | 123 ------------------ 9 files changed, 105 insertions(+), 135 deletions(-) create mode 100755 Docker/dist/build_images.sh create mode 100644 Docker/dist/client.Dockerfile rename Docker/{quickstart => dist}/docker-compose.yaml (63%) create mode 100644 Docker/dist/mongoDB.Dockerfile create mode 100644 Docker/dist/redis.Dockerfile create mode 100644 Docker/dist/server.Dockerfile create mode 100644 Docker/nginx/conf.d/dist_default.conf delete mode 100755 Docker/quickstart/quickstart.sh diff --git a/Docker/dist/build_images.sh b/Docker/dist/build_images.sh new file mode 100755 index 000000000..1df514e7e --- /dev/null +++ b/Docker/dist/build_images.sh @@ -0,0 +1,24 @@ + +#!/bin/bash + +# Change directory to root Server directory for correct Docker Context +cd ../.. + +#Client +client="./Docker/dist/client.Dockerfile" + +# MongoDB +mongoDB="./Docker/dist/mongoDB.Dockerfile" + +# Redis +redis="./Docker/dist/redis.Dockerfile" + +# Server +server="./Docker/dist/server.Dockerfile" + +docker build -f $client -t dist_uptime_client . +docker build -f $mongoDB -t dist_uptime_database_mongo . +docker build -f $redis -t dist_uptime_redis . +docker build -f $server -t dist_uptime_server . + +echo "All images built" \ No newline at end of file diff --git a/Docker/dist/client.Dockerfile b/Docker/dist/client.Dockerfile new file mode 100644 index 000000000..46ce12f3c --- /dev/null +++ b/Docker/dist/client.Dockerfile @@ -0,0 +1,19 @@ +FROM node:20-alpine as build + +WORKDIR /app + +COPY ./Client/package*.json ./ + +RUN npm install + +COPY ./Client . + +RUN npm run build + + +FROM nginx:1.27.1-alpine + +COPY ./Docker/nginx/conf.d/dist_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/quickstart/docker-compose.yaml b/Docker/dist/docker-compose.yaml similarity index 63% rename from Docker/quickstart/docker-compose.yaml rename to Docker/dist/docker-compose.yaml index e2c6e0bae..5a3319b81 100644 --- a/Docker/quickstart/docker-compose.yaml +++ b/Docker/dist/docker-compose.yaml @@ -1,22 +1,21 @@ services: client: - image: bluewaveuptime/uptime_client:latest + image: bluewaveuptime/uptime_client:test ports: - "80:80" - "443:443" depends_on: - server - volumes: - - ./nginx/conf.d:/etc/nginx/conf.d/:ro server: - image: bluewaveuptime/uptime_server:latest + image: bluewaveuptime/uptime_server:test ports: - "5000:5000" - env_file: - - server.env depends_on: - redis - mongodb + environment: + - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db + - REDIS_HOST=redis redis: image: bluewaveuptime/uptime_redis:latest ports: @@ -25,10 +24,8 @@ services: - ./redis/data:/data mongodb: image: bluewaveuptime/uptime_database_mongo:latest - command: ["mongod", "--quiet", "--auth"] - ports: - - "27017:27017" volumes: - ./mongo/data:/data/db - env_file: - - mongo.env + command: ["mongod", "--quiet"] + ports: + - "27017:27017" diff --git a/Docker/dist/mongoDB.Dockerfile b/Docker/dist/mongoDB.Dockerfile new file mode 100644 index 000000000..969a320c2 --- /dev/null +++ b/Docker/dist/mongoDB.Dockerfile @@ -0,0 +1,3 @@ +FROM mongo +EXPOSE 27017 +CMD ["mongod"] diff --git a/Docker/dist/redis.Dockerfile b/Docker/dist/redis.Dockerfile new file mode 100644 index 000000000..af68ec61e --- /dev/null +++ b/Docker/dist/redis.Dockerfile @@ -0,0 +1,2 @@ +FROM redis +EXPOSE 6379 \ No newline at end of file diff --git a/Docker/dist/server.Dockerfile b/Docker/dist/server.Dockerfile new file mode 100644 index 000000000..5d4ea7364 --- /dev/null +++ b/Docker/dist/server.Dockerfile @@ -0,0 +1,13 @@ +FROM node:20-alpine + +WORKDIR /app + +COPY ./Server/package*.json ./ + +RUN npm install + +COPY ./Server/ ./ + +EXPOSE 5000 + +CMD ["node", "index.js"] \ No newline at end of file diff --git a/Docker/docker-compose.yaml b/Docker/docker-compose.yaml index b72b5fc60..fa29a0d4d 100644 --- a/Docker/docker-compose.yaml +++ b/Docker/docker-compose.yaml @@ -40,4 +40,4 @@ services: - ./mongo/data:/data/db - ./mongo/init/create_users.js:/docker-entrypoint-initdb.d/create_users.js env_file: - - mongo.env \ No newline at end of file + - mongo.env diff --git a/Docker/nginx/conf.d/dist_default.conf b/Docker/nginx/conf.d/dist_default.conf new file mode 100644 index 000000000..a3ad90ead --- /dev/null +++ b/Docker/nginx/conf.d/dist_default.conf @@ -0,0 +1,35 @@ +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; + } + + location /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; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; + } + + location /api-docs/ { + proxy_pass http://server:5000/api-docs/; + proxy_http_version 1.1; + proxy_set_header Host $host; + 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; + } +} \ No newline at end of file diff --git a/Docker/quickstart/quickstart.sh b/Docker/quickstart/quickstart.sh deleted file mode 100755 index b460daaad..000000000 --- a/Docker/quickstart/quickstart.sh +++ /dev/null @@ -1,123 +0,0 @@ -default_server_base_url="http://localhost:5000/api/v1" -default_client_host="http://localhost" -default_jwt_secret="my_secret" -default_db_type="MongoDB" -default_redis_host="redis" -default_redis_port=6379 -default_token_ttl="99d" - -default_db_username="uptime_user" -default_db_password="uptime_password" - -default_system_email_host="smtp.gmail.com" -default_system_email_port=465 - - -echo "Welcome to the Uptime Monitor Setup Script! \n" -echo - -echo "Configuring server" -printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' '*' -echo - -db_type=$default_db_type -redis_host=$default_redis_host -redis_port=$default_redis_port -jwt_secret=$default_jwt_secret - - -read -p "Enter a username for your database [$default_db_username]: " db_username -db_username="${db_username:-$default_db_username}" - -read -p "Enter a password for your database [$default_db_password]: " db_password -db_password="${db_password:-$default_db_password}" - -read -p "Enter your system email host [$default_system_email_host]: " system_email_host -system_email_host="${system_email_host:-$default_system_email_host}" -echo "System email host: $system_email_host" -echo - -read -p "Enter your DB connection string [$default_db_connection_string]: " db_connection_string -db_connection_string="mongodb://${db_username}:${db_password}@mongodb:27017/uptime_db" -echo "DB connection string: $db_connection_string" -echo - -read -p "Enter your system email port [$default_system_email_port]: " system_email_port -system_email_port="${system_email_port:-$default_system_email_port}" -echo "System email port: $system_email_port" -echo - - -read -p "Enter your system email address: " system_email_address -echo "System email address: $system_email_address" -echo - -read -p "Enter your system email password: " system_email_password -echo "System email password: $system_email_password" -echo - -read -p "Enter your Token TTL [$default_token_ttl]: " token_ttl -token_ttl="${token_ttl:-$default_token_ttl}" -echo "Token TTL: $token_ttl" -echo - -read -p "Enter your Pagespeed API key: " pagespeed_api_key -echo "Pagespeed API key: $pagespeed_api_key" -echo - -echo "Writing to ./server.env" -echo - -{ - echo "CLIENT_HOST=\"$client_host\"" - echo "JWT_SECRET=\"$jwt_secret\"" - echo "DB_TYPE=\"$db_type\"" - echo "DB_CONNECTION_STRING=\"$db_connection_string\"" - echo "REDIS_HOST=\"$redis_host\"" - echo "REDIS_PORT=$redis_port" - echo "SYSTEM_EMAIL_HOST=\"$system_email_host\"" - echo "SYSTEM_EMAIL_PORT=$system_email_port" - echo "SYSTEM_EMAIL_ADDRESS=\"$system_email_address\"" - echo "SYSTEM_EMAIL_PASSWORD=\"$system_email_password\"" - echo "TOKEN_TTL=\"$token_ttl\"" - echo "PAGESPEED_API_KEY=\"$pagespeed_api_key\"" -} > ./server.env - - -echo -{ - echo USERNAME_ENV_VAR=${db_username} - echo PASSWORD_ENV_VAR=${db_password} -} > ./mongo.env - -mkdir -p ./nginx/conf.d/ - - -cat < ./nginx/conf.d/default.conf -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; - } - - location /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; - proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; - proxy_set_header X-Forwarded-Proto \$scheme; - } -} -EOL \ No newline at end of file