From 5f1c4b8674b576feb51e4a5312f4803e61cc0136 Mon Sep 17 00:00:00 2001 From: rkhatta1 Date: Sun, 9 Feb 2025 01:07:31 -0700 Subject: [PATCH 01/52] Fix: Issue#1726 - FE - Language preference should be stored in Redux store. --- Client/src/Components/LanguageSelector.jsx | 7 +++++-- Client/src/Features/UI/uiSlice.js | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/Client/src/Components/LanguageSelector.jsx b/Client/src/Components/LanguageSelector.jsx index 4f1193cd0..e1a19c231 100644 --- a/Client/src/Components/LanguageSelector.jsx +++ b/Client/src/Components/LanguageSelector.jsx @@ -3,15 +3,18 @@ import { useTranslation } from "react-i18next"; import { Box, MenuItem, Select, Stack } from "@mui/material"; import { useTheme } from "@emotion/react"; import Flag from "react-world-flags"; +import { useSelector, useDispatch } from "react-redux"; +import { setLanguage } from "../Features/Settings/uiSlice"; const LanguageSelector = () => { const { i18n } = useTranslation(); const theme = useTheme(); - const [language, setLanguage] = useState(i18n.language || "gb"); + const dispatch = useDispatch(); + const language = useSelector((state) => state.ui.language); const handleChange = (event) => { const newLang = event.target.value; - setLanguage(newLang); + dispatch(setLanguage(newLang)); i18n.changeLanguage(newLang); }; diff --git a/Client/src/Features/UI/uiSlice.js b/Client/src/Features/UI/uiSlice.js index 4565c6c5d..5731307ec 100644 --- a/Client/src/Features/UI/uiSlice.js +++ b/Client/src/Features/UI/uiSlice.js @@ -23,6 +23,7 @@ const initialState = { greeting: { index: 0, lastUpdate: null }, timezone: "America/Toronto", distributedUptimeEnabled: false, + language: "gb", }; const uiSlice = createSlice({ @@ -51,6 +52,9 @@ const uiSlice = createSlice({ setTimezone(state, action) { state.timezone = action.payload.timezone; }, + setLanguage(state, action) { + state.language = action.payload; + }, }, }); @@ -62,4 +66,5 @@ export const { setGreeting, setTimezone, setDistributedUptimeEnabled, + setLanguage, } = uiSlice.actions; From f3dfa17d7fbc4992e861e72c8062ba3f49abe9d3 Mon Sep 17 00:00:00 2001 From: rkhatta1 Date: Sun, 9 Feb 2025 13:13:23 -0700 Subject: [PATCH 02/52] Cleaned the imports and corrected the default language code in uiSlice.js --- Client/src/Components/LanguageSelector.jsx | 1 - Client/src/Features/UI/uiSlice.js | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/Client/src/Components/LanguageSelector.jsx b/Client/src/Components/LanguageSelector.jsx index 9f60a5b42..2d2fb184a 100644 --- a/Client/src/Components/LanguageSelector.jsx +++ b/Client/src/Components/LanguageSelector.jsx @@ -1,4 +1,3 @@ -import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Box, MenuItem, Select, Stack } from "@mui/material"; import { useTheme } from "@emotion/react"; diff --git a/Client/src/Features/UI/uiSlice.js b/Client/src/Features/UI/uiSlice.js index 5731307ec..2dd73bc07 100644 --- a/Client/src/Features/UI/uiSlice.js +++ b/Client/src/Features/UI/uiSlice.js @@ -23,7 +23,7 @@ const initialState = { greeting: { index: 0, lastUpdate: null }, timezone: "America/Toronto", distributedUptimeEnabled: false, - language: "gb", + language: "en", }; const uiSlice = createSlice({ From f747751563d016f6db3106724f1d1dfdda3cdbdf Mon Sep 17 00:00:00 2001 From: rkhatta1 Date: Sun, 9 Feb 2025 13:17:12 -0700 Subject: [PATCH 03/52] Corrcted the uiSlice import in LanguageSelector.jsx --- Client/src/Components/LanguageSelector.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Components/LanguageSelector.jsx b/Client/src/Components/LanguageSelector.jsx index 2d2fb184a..1893c10c4 100644 --- a/Client/src/Components/LanguageSelector.jsx +++ b/Client/src/Components/LanguageSelector.jsx @@ -3,7 +3,7 @@ import { Box, MenuItem, Select, Stack } from "@mui/material"; import { useTheme } from "@emotion/react"; import "flag-icons/css/flag-icons.min.css"; import { useSelector, useDispatch } from "react-redux"; -import { setLanguage } from "../Features/Settings/uiSlice"; +import { setLanguage } from "../Features/UI/uiSlice"; const LanguageSelector = () => { const { i18n } = useTranslation(); From 590338a48baf007d03726411dc677c6a131ec1e3 Mon Sep 17 00:00:00 2001 From: rkhatta1 Date: Sun, 9 Feb 2025 22:09:39 -0700 Subject: [PATCH 04/52] Made the requested changes to i18.js, LanguageSelector.jsx and uiSlice.js. --- Client/src/Components/LanguageSelector.jsx | 7 ++----- Client/src/Features/UI/uiSlice.js | 2 +- Client/src/Utils/i18n.js | 6 ++++-- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Client/src/Components/LanguageSelector.jsx b/Client/src/Components/LanguageSelector.jsx index 1893c10c4..ca8e9207c 100644 --- a/Client/src/Components/LanguageSelector.jsx +++ b/Client/src/Components/LanguageSelector.jsx @@ -2,18 +2,15 @@ import { useTranslation } from "react-i18next"; import { Box, MenuItem, Select, Stack } from "@mui/material"; import { useTheme } from "@emotion/react"; import "flag-icons/css/flag-icons.min.css"; -import { useSelector, useDispatch } from "react-redux"; -import { setLanguage } from "../Features/UI/uiSlice"; +import { useSelector } from "react-redux"; const LanguageSelector = () => { const { i18n } = useTranslation(); const theme = useTheme(); - const dispatch = useDispatch(); - const language = useSelector((state) => state.ui.language); + const { language } = useSelector((state) => state.ui); const handleChange = (event) => { const newLang = event.target.value; - dispatch(setLanguage(newLang)); i18n.changeLanguage(newLang); }; diff --git a/Client/src/Features/UI/uiSlice.js b/Client/src/Features/UI/uiSlice.js index 2dd73bc07..5731307ec 100644 --- a/Client/src/Features/UI/uiSlice.js +++ b/Client/src/Features/UI/uiSlice.js @@ -23,7 +23,7 @@ const initialState = { greeting: { index: 0, lastUpdate: null }, timezone: "America/Toronto", distributedUptimeEnabled: false, - language: "en", + language: "gb", }; const uiSlice = createSlice({ diff --git a/Client/src/Utils/i18n.js b/Client/src/Utils/i18n.js index f7528f4b5..7ccc0d241 100644 --- a/Client/src/Utils/i18n.js +++ b/Client/src/Utils/i18n.js @@ -1,5 +1,7 @@ import i18n from "i18next"; import { initReactI18next } from "react-i18next"; +import { setLanguage } from "../Features/UI/uiSlice"; +import store from "../store"; const primaryLanguage = "gb"; @@ -13,7 +15,7 @@ Object.keys(translations).forEach((path) => { }; }); -const savedLanguage = localStorage.getItem("language") || primaryLanguage; +const savedLanguage = store.getState.ui.language || primaryLanguage; i18n.use(initReactI18next).init({ resources, @@ -28,7 +30,7 @@ i18n.use(initReactI18next).init({ }); i18n.on("languageChanged", (lng) => { - localStorage.setItem("language", lng); + store.dispatch(setLanguage(lng)); }); export default i18n; From 46e27a69f36164b4f9f90dc841805416ef0c0f64 Mon Sep 17 00:00:00 2001 From: rkhatta1 Date: Mon, 10 Feb 2025 10:38:51 -0700 Subject: [PATCH 05/52] Corrected the usage of getState() funciotn in i18n.js. --- Client/src/Utils/i18n.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Client/src/Utils/i18n.js b/Client/src/Utils/i18n.js index 7ccc0d241..6243a2599 100644 --- a/Client/src/Utils/i18n.js +++ b/Client/src/Utils/i18n.js @@ -15,11 +15,12 @@ Object.keys(translations).forEach((path) => { }; }); -const savedLanguage = store.getState.ui.language || primaryLanguage; +const savedLanguage = store.getState()?.ui?.language; +const initialLanguage = savedLanguage || primaryLanguage; i18n.use(initReactI18next).init({ resources, - lng: savedLanguage, + lng: initialLanguage, fallbackLng: primaryLanguage, debug: import.meta.env.MODE === "development", ns: ["translation"], From 40393f3fef31b6fe5843cd8a011ed41000541b90 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 10 Feb 2025 16:15:31 -0800 Subject: [PATCH 06/52] remove console.log --- Client/src/Pages/Maintenance/MaintenanceTable/index.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx b/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx index 9bdce5887..ea8a1d7a3 100644 --- a/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx +++ b/Client/src/Pages/Maintenance/MaintenanceTable/index.jsx @@ -34,7 +34,6 @@ const MaintenanceTable = ({ updateCallback, }) => { const { rowsPerPage } = useSelector((state) => state.ui.maintenance); - console.log(rowsPerPage); const dispatch = useDispatch(); const handleChangePage = (event, newPage) => { @@ -175,8 +174,6 @@ const MaintenanceTable = ({ setSort({ field, order }); }; - console.log(handleChangePage); - return ( <> Date: Mon, 10 Feb 2025 16:15:48 -0800 Subject: [PATCH 07/52] fix success message --- Client/src/Pages/StatusPage/Create/index.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Client/src/Pages/StatusPage/Create/index.jsx b/Client/src/Pages/StatusPage/Create/index.jsx index 158aca2e9..f54189fe8 100644 --- a/Client/src/Pages/StatusPage/Create/index.jsx +++ b/Client/src/Pages/StatusPage/Create/index.jsx @@ -134,7 +134,11 @@ const CreateStatusPage = () => { if (typeof error === "undefined") { const success = await createStatusPage({ form }); if (success) { - createToast({ body: "Status page created successfully" }); + createToast({ + body: isCreate + ? "Status page created successfully" + : "Status page updated successfully", + }); navigate(`/status/uptime/${form.url}`); } return; From 6e58b6a630dfffe2e6f490e143b08f4af3d2aafe Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 10 Feb 2025 16:16:25 -0800 Subject: [PATCH 08/52] nav to /status/ --- .../StatusPage/Status/Components/ControlsHeader/index.jsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx b/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx index 2c96db054..e8ea104b8 100644 --- a/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx +++ b/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx @@ -43,11 +43,7 @@ const Controls = ({ isDeleteOpen, setIsDeleteOpen, isDeleting, url, type }) => { variant="contained" color="secondary" onClick={() => { - if (type === "uptime") { - navigate(`/status/uptime/configure/${url}`); - } else { - navigate(`/status/distributed/configure/${url}`); - } + navigate(`/status/`); }} sx={{ px: theme.spacing(5), From 52172921255d5fafa4f56eed2d15c1e83310617d Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 10 Feb 2025 16:17:07 -0800 Subject: [PATCH 09/52] don't show toast on 404 --- .../StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx b/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx index 7565f129a..29e7f2b89 100644 --- a/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx +++ b/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx @@ -20,7 +20,12 @@ const useStatusPagesFetch = () => { setStatusPages(res?.data?.data); } catch (error) { setNetworkError(true); - createToast(error.message, "error"); + if (error.response.status === 404) { + return; + } + createToast({ + body: error.message, + }); } finally { setIsLoading(false); } From 771a9c6c99d92397e71f1a79cc5473facf037a57 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 10 Feb 2025 16:17:35 -0800 Subject: [PATCH 10/52] update empty view, use table --- .../Components/StatusPagesTable/index.jsx | 85 +++++++++++++++++++ .../Pages/StatusPage/StatusPages/index.jsx | 20 ++--- 2 files changed, 90 insertions(+), 15 deletions(-) create mode 100644 Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx diff --git a/Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx b/Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx new file mode 100644 index 000000000..6e7988153 --- /dev/null +++ b/Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx @@ -0,0 +1,85 @@ +import DataTable from "../../../../../Components/Table"; +import { useTheme } from "@emotion/react"; +import { useNavigate } from "react-router-dom"; +import { ColoredLabel } from "../../../../../Components/Label"; +import ArrowOutwardIcon from "@mui/icons-material/ArrowOutward"; +import { Stack, Typography } from "@mui/material"; +const StatusPagesTable = ({ data }) => { + const theme = useTheme(); + const navigate = useNavigate(); + const headers = [ + { + id: "name", + content: "Status page name", + render: (row) => { + return row.companyName; + }, + }, + { + id: "url", + content: "URL", + render: (row) => { + return ( + + {row.url} + + + ); + }, + }, + { + id: "type", + content: "Type", + render: (row) => { + return row.type; + }, + }, + { + id: "status", + content: "Status", + render: (row) => { + return ( + + ); + }, + }, + ]; + + const handleRowClick = (statusPage) => { + if (statusPage.type === "distributed") { + navigate(`/status/distributed/${statusPage.url}`); + } else if (statusPage.type === "uptime") { + navigate(`/status/uptime/${statusPage.url}`); + } + }; + + return ( + { + handleRowClick(row); + }, + }} + headers={headers} + data={data} + /> + ); +}; + +export default StatusPagesTable; diff --git a/Client/src/Pages/StatusPage/StatusPages/index.jsx b/Client/src/Pages/StatusPage/StatusPages/index.jsx index cf5b83289..8ec741729 100644 --- a/Client/src/Pages/StatusPage/StatusPages/index.jsx +++ b/Client/src/Pages/StatusPage/StatusPages/index.jsx @@ -4,6 +4,7 @@ import Breadcrumbs from "../../../Components/Breadcrumbs"; import Fallback from "../../../Components/Fallback"; import MonitorCreateHeader from "../../../Components/MonitorCreateHeader"; import GenericFallback from "../../../Components/GenericFallback"; +import StatusPagesTable from "./Components/StatusPagesTable"; // Utils import { useTheme } from "@emotion/react"; import { useStatusPagesFetch } from "./Hooks/useStatusPagesFetch"; @@ -32,8 +33,9 @@ const StatusPages = () => { { isAdmin={isAdmin} path="/status/uptime/create" /> - {statusPages?.map((statusPage) => { - return ( - handleStatusPageClick(statusPage)} - sx={{ cursor: "pointer" }} - > - Company Name: {statusPage.companyName} - Status page URL: {statusPage.url} - Type: {statusPage.type} - - ); - })} + ); }; From eb8901f5ac2ee5cb9bd50124eeda8514c605372b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Mon, 10 Feb 2025 16:18:36 -0800 Subject: [PATCH 11/52] fix form.isPublished conditional --- Client/src/Utils/NetworkService.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Client/src/Utils/NetworkService.js b/Client/src/Utils/NetworkService.js index ebb726a2c..515ae5930 100644 --- a/Client/src/Utils/NetworkService.js +++ b/Client/src/Utils/NetworkService.js @@ -1015,11 +1015,12 @@ class NetworkService { async createStatusPage(config) { const { authToken, user, form, isCreate } = config; + const fd = new FormData(); fd.append("teamId", user.teamId); fd.append("userId", user._id); fd.append("type", form.type); - form.isPublished && fd.append("isPublished", form.isPublished); + form.isPublished !== undefined && fd.append("isPublished", form.isPublished); form.companyName && fd.append("companyName", form.companyName); form.url && fd.append("url", form.url); form.timezone && fd.append("timezone", form.timezone); From 10713428b98c855c5f99fd5173bdbceb2440a4d2 Mon Sep 17 00:00:00 2001 From: cihatata Date: Tue, 28 Jan 2025 00:09:56 +0300 Subject: [PATCH 12/52] feat: i18n support for backend --- Server/controllers/authController.js | 42 +-- Server/controllers/checkController.js | 12 +- Server/controllers/inviteController.js | 6 +- .../maintenanceWindowController.js | 12 +- Server/controllers/monitorController.js | 36 +-- Server/controllers/queueController.js | 8 +- Server/controllers/settingsController.js | 4 +- Server/controllers/statusPageController.js | 4 +- Server/db/mongo/modules/inviteModule.js | 8 +- Server/db/mongo/modules/monitorModule.js | 112 +++---- Server/db/mongo/modules/recoveryModule.js | 6 +- Server/db/mongo/modules/userModule.js | 8 +- Server/index.js | 15 +- Server/middleware/isAllowed.js | 6 +- Server/middleware/languageMiddleware.js | 9 + Server/middleware/verifyJWT.js | 8 +- Server/middleware/verifyOwnership.js | 6 +- Server/middleware/verifySuperAdmin.js | 4 +- Server/service/jobQueue.js | 4 +- Server/service/networkService.js | 8 +- Server/service/translationService.js | 201 +++++++++++++ .../tests/controllers/authController.test.js | 201 +++++++------ .../tests/controllers/checkController.test.js | 89 +++--- .../maintenanceWindowController.test.js | 94 +++--- .../controllers/monitorController.test.js | 272 +++++++++-------- Server/tests/db/inviteModule.test.js | 33 +- Server/tests/db/monitorModule.test.js | 284 +++++++++--------- Server/tests/db/recoveryModule.test.js | 44 +-- Server/tests/db/statusPageModule.test.js | 29 +- Server/tests/db/userModule.test.js | 74 ++--- Server/tests/services/networkService.test.js | 92 +++--- Server/tests/utils/messages.test.js | 18 +- Server/utils/formattedKey.js | 14 + Server/utils/messages.js | 181 +++++------ 34 files changed, 1106 insertions(+), 838 deletions(-) create mode 100644 Server/middleware/languageMiddleware.js create mode 100644 Server/service/translationService.js create mode 100644 Server/utils/formattedKey.js diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 0ffa612f3..05a9b9ccb 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -76,7 +76,7 @@ class AuthController { // If superAdmin exists, a token should be attached to all further register requests const superAdminExists = await this.db.checkSuperadmin(req, res); if (superAdminExists) { - await this.db.getInviteTokenAndDelete(inviteToken); + await this.db.getInviteTokenAndDelete(inviteToken, req.language); } 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"); @@ -85,7 +85,7 @@ class AuthController { const newUser = await this.db.insertUser({ ...req.body }, req.file); logger.info({ - message: successMessages.AUTH_CREATE_USER, + message: successMessages.AUTH_CREATE_USER(req.language), service: SERVICE_NAME, details: newUser._id, }); @@ -116,7 +116,7 @@ class AuthController { }); res.success({ - msg: successMessages.AUTH_CREATE_USER, + msg: successMessages.AUTH_CREATE_USER(req.language), data: { user: newUser, token: token, refreshToken: refreshToken }, }); } catch (error) { @@ -148,12 +148,12 @@ class AuthController { const { email, password } = req.body; // Check if user exists - const user = await this.db.getUserByEmail(email); + const user = await this.db.getUserByEmail(email, req.language); // Compare password const match = await user.comparePassword(password); if (match !== true) { - const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD); + const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD(req.language)); error.status = 401; next(error); return; @@ -176,7 +176,7 @@ class AuthController { userWithoutPassword.avatarImage = user.avatarImage; return res.success({ - msg: successMessages.AUTH_LOGIN_USER, + msg: successMessages.AUTH_LOGIN_USER(req.language), data: { user: userWithoutPassword, token: token, @@ -206,7 +206,7 @@ class AuthController { if (!refreshToken) { // No refresh token provided - const error = new Error(errorMessages.NO_REFRESH_TOKEN); + const error = new Error(errorMessages.NO_REFRESH_TOKEN(req.language)); error.status = 401; error.service = SERVICE_NAME; error.method = "refreshAuthToken"; @@ -243,7 +243,7 @@ class AuthController { ); return res.success({ - msg: successMessages.AUTH_TOKEN_REFRESHED, + msg: successMessages.AUTH_TOKEN_REFRESHED(req.language), data: { user: payloadData, token: newAuthToken, refreshToken: refreshToken }, }); } catch (error) { @@ -276,7 +276,7 @@ class AuthController { // TODO is this neccessary any longer? Verify ownership middleware should handle this if (req.params.userId !== req.user._id.toString()) { - const error = new Error(errorMessages.AUTH_UNAUTHORIZED); + const error = new Error(errorMessages.AUTH_UNAUTHORIZED(req.language)); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -294,13 +294,13 @@ class AuthController { // Add user email to body for DB operation req.body.email = email; // Get user - const user = await this.db.getUserByEmail(email); + const user = await this.db.getUserByEmail(email, req.language); // Compare passwords const match = await user.comparePassword(req.body.password); // If not a match, throw a 403 // 403 instead of 401 to avoid triggering axios interceptor if (!match) { - const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD); + const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD(req.language)); error.status = 403; next(error); return; @@ -311,7 +311,7 @@ class AuthController { const updatedUser = await this.db.updateUser(req, res); res.success({ - msg: successMessages.AUTH_UPDATE_USER, + msg: successMessages.AUTH_UPDATE_USER(req.language), data: updatedUser, }); } catch (error) { @@ -333,7 +333,7 @@ class AuthController { const superAdminExists = await this.db.checkSuperadmin(req, res); return res.success({ - msg: successMessages.AUTH_ADMIN_EXISTS, + msg: successMessages.AUTH_ADMIN_EXISTS(req.language), data: superAdminExists, }); } catch (error) { @@ -362,7 +362,7 @@ class AuthController { try { const { email } = req.body; - const user = await this.db.getUserByEmail(email); + const user = await this.db.getUserByEmail(email, req.language); const recoveryToken = await this.db.requestRecoveryToken(req, res); const name = user.firstName; const { clientHost } = this.settingsService.getSettings(); @@ -379,7 +379,7 @@ class AuthController { ); return res.success({ - msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, + msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN(req.language), data: msgId, }); } catch (error) { @@ -410,7 +410,7 @@ class AuthController { await this.db.validateRecoveryToken(req, res); return res.success({ - msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN, + msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN(req.language), }); } catch (error) { next(handleError(error, SERVICE_NAME, "validateRecoveryTokenController")); @@ -443,7 +443,7 @@ class AuthController { const token = this.issueToken(user._doc, tokenType.ACCESS_TOKEN, appSettings); return res.success({ - msg: successMessages.AUTH_RESET_PASSWORD, + msg: successMessages.AUTH_RESET_PASSWORD(req.language), data: { user, token }, }); } catch (error) { @@ -467,7 +467,7 @@ class AuthController { const { email } = decodedToken; // Check if the user exists - const user = await this.db.getUserByEmail(email); + const user = await this.db.getUserByEmail(email, req.language); // 1. Find all the monitors associated with the team ID if superadmin const result = await this.db.getMonitorsByTeamId({ @@ -494,10 +494,10 @@ class AuthController { await this.db.deleteMonitorsByUserId(user._id); } // 6. Delete the user by id - await this.db.deleteUser(user._id); + await this.db.deleteUser(user._id, req.language); return res.success({ - msg: successMessages.AUTH_DELETE_USER, + msg: successMessages.AUTH_DELETE_USER(req.language), }); } catch (error) { next(handleError(error, SERVICE_NAME, "deleteUserController")); @@ -509,7 +509,7 @@ class AuthController { const allUsers = await this.db.getAllUsers(req, res); return res.success({ - msg: successMessages.AUTH_GET_ALL_USERS, + msg: successMessages.AUTH_GET_ALL_USERS(req.language), data: allUsers, }); } catch (error) { diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js index ae6b96dbd..0f532dea0 100644 --- a/Server/controllers/checkController.js +++ b/Server/controllers/checkController.js @@ -36,7 +36,7 @@ class CheckController { const check = await this.db.createCheck(checkData); return res.success({ - msg: successMessages.CHECK_CREATE, + msg: successMessages.CHECK_CREATE(req.language), data: check, }); } catch (error) { @@ -57,7 +57,7 @@ class CheckController { const result = await this.db.getChecksByMonitor(req); return res.success({ - msg: successMessages.CHECK_GET, + msg: successMessages.CHECK_GET(req.language), data: result, }); } catch (error) { @@ -77,7 +77,7 @@ class CheckController { const checkData = await this.db.getChecksByTeam(req); return res.success({ - msg: successMessages.CHECK_GET, + msg: successMessages.CHECK_GET(req.language), data: checkData, }); } catch (error) { @@ -97,7 +97,7 @@ class CheckController { const deletedCount = await this.db.deleteChecks(req.params.monitorId); return res.success({ - msg: successMessages.CHECK_DELETE, + msg: successMessages.CHECK_DELETE(req.language), data: { deletedCount }, }); } catch (error) { @@ -117,7 +117,7 @@ class CheckController { const deletedCount = await this.db.deleteChecksByTeamId(req.params.teamId); return res.success({ - msg: successMessages.CHECK_DELETE, + msg: successMessages.CHECK_DELETE(req.language), data: { deletedCount }, }); } catch (error) { @@ -144,7 +144,7 @@ class CheckController { await this.db.updateChecksTTL(teamId, ttl); return res.success({ - msg: successMessages.CHECK_UPDATE_TTL, + msg: successMessages.CHECK_UPDATE_TTL(req.language), }); } catch (error) { next(handleError(error, SERVICE_NAME, "updateTTL")); diff --git a/Server/controllers/inviteController.js b/Server/controllers/inviteController.js index a40f43a32..6c5592f69 100644 --- a/Server/controllers/inviteController.js +++ b/Server/controllers/inviteController.js @@ -66,7 +66,7 @@ class InviteController { }); return res.success({ - msg: successMessages.INVITE_ISSUED, + msg: successMessages.INVITE_ISSUED(req.language), data: inviteToken, }); } catch (error) { @@ -83,10 +83,10 @@ class InviteController { } try { - const invite = await this.db.getInviteToken(req.body.token); + const invite = await this.db.getInviteToken(req.body.token, req.language); return res.success({ - msg: successMessages.INVITE_VERIFIED, + msg: successMessages.INVITE_VERIFIED(req.language), data: invite, }); } catch (error) { diff --git a/Server/controllers/maintenanceWindowController.js b/Server/controllers/maintenanceWindowController.js index b301ba94c..f6de3d479 100644 --- a/Server/controllers/maintenanceWindowController.js +++ b/Server/controllers/maintenanceWindowController.js @@ -45,7 +45,7 @@ class MaintenanceWindowController { await Promise.all(dbTransactions); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_CREATE, + msg: successMessages.MAINTENANCE_WINDOW_CREATE(req.language), }); } catch (error) { next(handleError(error, SERVICE_NAME, "createMaintenanceWindow")); @@ -63,7 +63,7 @@ class MaintenanceWindowController { const maintenanceWindow = await this.db.getMaintenanceWindowById(req.params.id); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID(req.language), data: maintenanceWindow, }); } catch (error) { @@ -89,7 +89,7 @@ class MaintenanceWindowController { ); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM(req.language), data: maintenanceWindows, }); } catch (error) { @@ -111,7 +111,7 @@ class MaintenanceWindowController { ); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_USER, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_USER(req.language), data: maintenanceWindows, }); } catch (error) { @@ -129,7 +129,7 @@ class MaintenanceWindowController { try { await this.db.deleteMaintenanceWindowById(req.params.id); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_DELETE, + msg: successMessages.MAINTENANCE_WINDOW_DELETE(req.language), }); } catch (error) { next(handleError(error, SERVICE_NAME, "deleteMaintenanceWindow")); @@ -150,7 +150,7 @@ class MaintenanceWindowController { req.body ); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_EDIT, + msg: successMessages.MAINTENANCE_WINDOW_EDIT(req.language), data: editedMaintenanceWindow, }); } catch (error) { diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index ccd8ec26f..a9d140745 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -43,7 +43,7 @@ class MonitorController { try { const monitors = await this.db.getAllMonitors(); return res.success({ - msg: successMessages.MONITOR_GET_ALL, + msg: successMessages.MONITOR_GET_ALL(req.language), data: monitors, }); } catch (error) { @@ -64,7 +64,7 @@ class MonitorController { try { const monitors = await this.db.getAllMonitorsWithUptimeStats(); return res.success({ - msg: successMessages.MONITOR_GET_ALL, + msg: successMessages.MONITOR_GET_ALL(req.language), data: monitors, }); } catch (error) { @@ -76,7 +76,7 @@ class MonitorController { try { const monitor = await this.db.getUptimeDetailsById(req); return res.success({ - msg: successMessages.MONITOR_GET_BY_ID, + msg: successMessages.MONITOR_GET_BY_ID(req.language), data: monitor, }); } catch (error) { @@ -105,7 +105,7 @@ class MonitorController { try { const monitorStats = await this.db.getMonitorStatsById(req); return res.success({ - msg: successMessages.MONITOR_STATS_BY_ID, + msg: successMessages.MONITOR_STATS_BY_ID(req.language), data: monitorStats, }); } catch (error) { @@ -133,7 +133,7 @@ class MonitorController { try { const monitor = await this.db.getHardwareDetailsById(req); return res.success({ - msg: successMessages.MONITOR_GET_BY_ID, + msg: successMessages.MONITOR_GET_BY_ID(req.language), data: monitor, }); } catch (error) { @@ -154,7 +154,7 @@ class MonitorController { const certificate = await fetchMonitorCertificate(sslChecker, monitor); return res.success({ - msg: successMessages.MONITOR_CERTIFICATE, + msg: successMessages.MONITOR_CERTIFICATE(req.language), data: { certificateDate: new Date(certificate.validTo), }, @@ -187,7 +187,7 @@ class MonitorController { try { const monitor = await this.db.getMonitorById(req.params.monitorId); return res.success({ - msg: successMessages.MONITOR_GET_BY_ID, + msg: successMessages.MONITOR_GET_BY_ID(req.language), data: monitor, }); } catch (error) { @@ -231,7 +231,7 @@ class MonitorController { // Add monitor to job queue this.jobQueue.addJob(monitor._id, monitor); return res.success({ - msg: successMessages.MONITOR_CREATE, + msg: successMessages.MONITOR_CREATE(req.language), data: monitor, }); } catch (error) { @@ -309,7 +309,7 @@ class MonitorController { stack: error.stack, }); } - return res.success({ msg: successMessages.MONITOR_DELETE }); + return res.success({ msg: successMessages.MONITOR_DELETE(req.language) }); } catch (error) { next(handleError(error, SERVICE_NAME, "deleteMonitor")); } @@ -390,10 +390,10 @@ class MonitorController { await Promise.all( notifications && - notifications.map(async (notification) => { - notification.monitorId = editedMonitor._id; - await this.db.createNotification(notification); - }) + notifications.map(async (notification) => { + notification.monitorId = editedMonitor._id; + await this.db.createNotification(notification); + }) ); // Delete the old job(editedMonitor has the same ID as the old monitor) @@ -401,7 +401,7 @@ class MonitorController { // Add the new job back to the queue await this.jobQueue.addJob(editedMonitor._id, editedMonitor); return res.success({ - msg: successMessages.MONITOR_EDIT, + msg: successMessages.MONITOR_EDIT(req.language), data: editedMonitor, }); } catch (error) { @@ -438,8 +438,8 @@ class MonitorController { monitor.save(); return res.success({ msg: monitor.isActive - ? successMessages.MONITOR_RESUME - : successMessages.MONITOR_PAUSE, + ? successMessages.MONITOR_RESUME(req.language) + : successMessages.MONITOR_PAUSE(req.language), data: monitor, }); } catch (error) { @@ -469,7 +469,7 @@ class MonitorController { ); return res.success({ - msg: successMessages.MONITOR_DEMO_ADDED, + msg: successMessages.MONITOR_DEMO_ADDED(req.language), data: demoMonitors.length, }); } catch (error) { @@ -488,7 +488,7 @@ class MonitorController { try { const monitors = await this.db.getMonitorsByTeamId(req); return res.success({ - msg: successMessages.MONITOR_GET_BY_TEAM_ID, + msg: successMessages.MONITOR_GET_BY_TEAM_ID(req.language), data: monitors, }); } catch (error) { diff --git a/Server/controllers/queueController.js b/Server/controllers/queueController.js index f62b9fdeb..f43237821 100644 --- a/Server/controllers/queueController.js +++ b/Server/controllers/queueController.js @@ -12,7 +12,7 @@ class JobQueueController { try { const metrics = await this.jobQueue.getMetrics(); res.success({ - msg: successMessages.QUEUE_GET_METRICS, + msg: successMessages.QUEUE_GET_METRICS(req.language), data: metrics, }); } catch (error) { @@ -25,7 +25,7 @@ class JobQueueController { try { const jobs = await this.jobQueue.getJobStats(); return res.success({ - msg: successMessages.QUEUE_GET_METRICS, + msg: successMessages.QUEUE_GET_METRICS(req.language), data: jobs, }); } catch (error) { @@ -38,7 +38,7 @@ class JobQueueController { try { await this.jobQueue.addJob(Math.random().toString(36).substring(7)); return res.success({ - msg: successMessages.QUEUE_ADD_JOB, + msg: successMessages.QUEUE_ADD_JOB(req.language), }); } catch (error) { next(handleError(error, SERVICE_NAME, "addJob")); @@ -50,7 +50,7 @@ class JobQueueController { try { await this.jobQueue.obliterate(); return res.success({ - msg: successMessages.QUEUE_OBLITERATE, + msg: successMessages.QUEUE_OBLITERATE(req.language), }); } catch (error) { next(handleError(error, SERVICE_NAME, "obliterateQueue")); diff --git a/Server/controllers/settingsController.js b/Server/controllers/settingsController.js index 496ed057a..209b43ebc 100644 --- a/Server/controllers/settingsController.js +++ b/Server/controllers/settingsController.js @@ -14,7 +14,7 @@ class SettingsController { const settings = { ...(await this.settingsService.getSettings()) }; delete settings.jwtSecret; return res.success({ - msg: successMessages.GET_APP_SETTINGS, + msg: successMessages.GET_APP_SETTINGS(req.language), data: settings, }); } catch (error) { @@ -35,7 +35,7 @@ class SettingsController { const updatedSettings = { ...(await this.settingsService.reloadSettings()) }; delete updatedSettings.jwtSecret; return res.success({ - msg: successMessages.UPDATE_APP_SETTINGS, + msg: successMessages.UPDATE_APP_SETTINGS(req.language), data: updatedSettings, }); } catch (error) { diff --git a/Server/controllers/statusPageController.js b/Server/controllers/statusPageController.js index 1a69c92ec..5be9050f1 100644 --- a/Server/controllers/statusPageController.js +++ b/Server/controllers/statusPageController.js @@ -26,7 +26,7 @@ class StatusPageController { try { const statusPage = await this.db.createStatusPage(req.body, req.file); return res.success({ - msg: successMessages.STATUS_PAGE_CREATE, + msg: successMessages.STATUS_PAGE_CREATE(req.language), data: statusPage, }); } catch (error) { @@ -63,7 +63,7 @@ class StatusPageController { try { const statusPage = await this.db.getStatusPage(); return res.success({ - msg: successMessages.STATUS_PAGE, + msg: successMessages.STATUS_PAGE_BY_URL(req.language), data: statusPage, }); } catch (error) { diff --git a/Server/db/mongo/modules/inviteModule.js b/Server/db/mongo/modules/inviteModule.js index 0cbf0ab04..2249ff1fb 100644 --- a/Server/db/mongo/modules/inviteModule.js +++ b/Server/db/mongo/modules/inviteModule.js @@ -41,13 +41,13 @@ const requestInviteToken = async (userData) => { * @returns {Promise} The invite token data. * @throws {Error} If the invite token is not found or there is another error. */ -const getInviteToken = async (token) => { +const getInviteToken = async (token, language) => { try { const invite = await InviteToken.findOne({ token, }); if (invite === null) { - throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND); + throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND(language)); } return invite; } catch (error) { @@ -67,13 +67,13 @@ const getInviteToken = async (token) => { * @returns {Promise} The invite token data. * @throws {Error} If the invite token is not found or there is another error. */ -const getInviteTokenAndDelete = async (token) => { +const getInviteTokenAndDelete = async (token, language) => { try { const invite = await InviteToken.findOneAndDelete({ token, }); if (invite === null) { - throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND); + throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND(language)); } return invite; } catch (error) { diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index 9a8e98f4e..c64d41c8e 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -312,7 +312,7 @@ const calculateGroupStats = (group) => { avgResponseTime: checksWithResponseTime.length > 0 ? checksWithResponseTime.reduce((sum, check) => sum + check.responseTime, 0) / - checksWithResponseTime.length + checksWithResponseTime.length : 0, }; }; @@ -330,7 +330,7 @@ const getUptimeDetailsById = async (req) => { const { monitorId } = req.params; const monitor = await Monitor.findById(monitorId); if (monitor === null || monitor === undefined) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, req.language)); } const { dateRange, normalize } = req.query; @@ -601,78 +601,78 @@ const getMonitorsByTeamId = async (req) => { filteredMonitors: [ ...(filter !== undefined ? [ - { - $match: { - $or: [ - { name: { $regex: filter, $options: "i" } }, - { url: { $regex: filter, $options: "i" } }, - ], - }, + { + $match: { + $or: [ + { name: { $regex: filter, $options: "i" } }, + { url: { $regex: filter, $options: "i" } }, + ], }, - ] + }, + ] : []), { $sort: sort }, { $skip: skip }, ...(rowsPerPage ? [{ $limit: rowsPerPage }] : []), ...(limit ? [ - { - $lookup: { - from: "checks", - let: { monitorId: "$_id" }, - pipeline: [ - { - $match: { - $expr: { $eq: ["$monitorId", "$$monitorId"] }, - }, + { + $lookup: { + from: "checks", + let: { monitorId: "$_id" }, + pipeline: [ + { + $match: { + $expr: { $eq: ["$monitorId", "$$monitorId"] }, }, - { $sort: { createdAt: -1 } }, - ...(limit ? [{ $limit: limit }] : []), - ], - as: "standardchecks", - }, + }, + { $sort: { createdAt: -1 } }, + ...(limit ? [{ $limit: limit }] : []), + ], + as: "standardchecks", }, - ] + }, + ] : []), ...(limit ? [ - { - $lookup: { - from: "pagespeedchecks", - let: { monitorId: "$_id" }, - pipeline: [ - { - $match: { - $expr: { $eq: ["$monitorId", "$$monitorId"] }, - }, + { + $lookup: { + from: "pagespeedchecks", + let: { monitorId: "$_id" }, + pipeline: [ + { + $match: { + $expr: { $eq: ["$monitorId", "$$monitorId"] }, }, - { $sort: { createdAt: -1 } }, - ...(limit ? [{ $limit: limit }] : []), - ], - as: "pagespeedchecks", - }, + }, + { $sort: { createdAt: -1 } }, + ...(limit ? [{ $limit: limit }] : []), + ], + as: "pagespeedchecks", }, - ] + }, + ] : []), ...(limit ? [ - { - $lookup: { - from: "hardwarechecks", - let: { monitorId: "$_id" }, - pipeline: [ - { - $match: { - $expr: { $eq: ["$monitorId", "$$monitorId"] }, - }, + { + $lookup: { + from: "hardwarechecks", + let: { monitorId: "$_id" }, + pipeline: [ + { + $match: { + $expr: { $eq: ["$monitorId", "$$monitorId"] }, }, - { $sort: { createdAt: -1 } }, - ...(limit ? [{ $limit: limit }] : []), - ], - as: "hardwarechecks", - }, + }, + { $sort: { createdAt: -1 } }, + ...(limit ? [{ $limit: limit }] : []), + ], + as: "hardwarechecks", }, - ] + }, + ] : []), ...(limit ? [ @@ -787,7 +787,7 @@ const deleteMonitor = async (req, res) => { try { const monitor = await Monitor.findByIdAndDelete(monitorId); if (!monitor) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, req.language)); } return monitor; } catch (error) { diff --git a/Server/db/mongo/modules/recoveryModule.js b/Server/db/mongo/modules/recoveryModule.js index 40dd66c56..0efcca4e8 100644 --- a/Server/db/mongo/modules/recoveryModule.js +++ b/Server/db/mongo/modules/recoveryModule.js @@ -39,7 +39,7 @@ const validateRecoveryToken = async (req, res) => { if (recoveryToken !== null) { return recoveryToken; } else { - throw new Error(errorMessages.DB_TOKEN_NOT_FOUND); + throw new Error(errorMessages.DB_TOKEN_NOT_FOUND(req.language)); } } catch (error) { error.service = SERVICE_NAME; @@ -57,12 +57,12 @@ const resetPassword = async (req, res) => { const user = await UserModel.findOne({ email: recoveryToken.email }); if (user === null) { - throw new Error(errorMessages.DB_USER_NOT_FOUND); + throw new Error(errorMessages.DB_USER_NOT_FOUND(req.language)); } const match = await user.comparePassword(newPassword); if (match === true) { - throw new Error(errorMessages.DB_RESET_PASSWORD_BAD_MATCH); + throw new Error(errorMessages.DB_RESET_PASSWORD_BAD_MATCH(req.language)); } user.password = newPassword; diff --git a/Server/db/mongo/modules/userModule.js b/Server/db/mongo/modules/userModule.js index 5b951ff64..3460f7513 100644 --- a/Server/db/mongo/modules/userModule.js +++ b/Server/db/mongo/modules/userModule.js @@ -69,13 +69,13 @@ const insertUser = async ( * @returns {Promise} * @throws {Error} */ -const getUserByEmail = async (email) => { +const getUserByEmail = async (email, language) => { try { // Need the password to be able to compare, removed .select() // We can strip the hash before returning the user const user = await UserModel.findOne({ email: email }).select("-profileImage"); if (!user) { - throw new Error(errorMessages.DB_USER_NOT_FOUND); + throw new Error(errorMessages.DB_USER_NOT_FOUND(language)); } return user; } catch (error) { @@ -149,11 +149,11 @@ const updateUser = async ( * @returns {Promise} * @throws {Error} */ -const deleteUser = async (userId) => { +const deleteUser = async (userId, language) => { try { const deletedUser = await UserModel.findByIdAndDelete(userId); if (!deletedUser) { - throw new Error(errorMessages.DB_USER_NOT_FOUND); + throw new Error(errorMessages.DB_USER_NOT_FOUND(language)); } return deletedUser; } catch (error) { diff --git a/Server/index.js b/Server/index.js index 23d16856a..1b908436b 100644 --- a/Server/index.js +++ b/Server/index.js @@ -74,6 +74,9 @@ import MongoDB from "./db/mongo/MongoDB.js"; import IORedis from "ioredis"; +import TranslationService from './service/translationService.js'; +import languageMiddleware from './middleware/languageMiddleware.js'; + const SERVICE_NAME = "Server"; const SHUTDOWN_TIMEOUT = 1000; let isShuttingDown = false; @@ -175,6 +178,7 @@ const startApp = async () => { const networkService = new NetworkService(axios, ping, logger, http, Docker, net); const statusService = new StatusService(db, logger); const notificationService = new NotificationService(emailService, db, logger); + const translationService = new TranslationService(logger); const jobQueue = new JobQueue( db, @@ -195,6 +199,11 @@ const startApp = async () => { ServiceRegistry.register(NetworkService.SERVICE_NAME, networkService); ServiceRegistry.register(StatusService.SERVICE_NAME, statusService); ServiceRegistry.register(NotificationService.SERVICE_NAME, notificationService); + ServiceRegistry.register(TranslationService.SERVICE_NAME, translationService); + + + await translationService.initialize(); + server = app.listen(PORT, () => { logger.info({ message: `server started on port:${PORT}` }); }); @@ -267,12 +276,10 @@ const startApp = async () => { // Init job queue await jobQueue.initJobQueue(); // Middleware - app.use( - cors() - //We will add configuration later - ); + app.use(cors()); app.use(express.json()); app.use(helmet()); + app.use(languageMiddleware); // Swagger UI app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec)); diff --git a/Server/middleware/isAllowed.js b/Server/middleware/isAllowed.js index bd75b00ad..a4fbcbf64 100644 --- a/Server/middleware/isAllowed.js +++ b/Server/middleware/isAllowed.js @@ -12,7 +12,7 @@ const isAllowed = (allowedRoles) => { // If no token is pressent, return an error if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN); + const error = new Error(errorMessages.NO_AUTH_TOKEN(req.language)); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -21,7 +21,7 @@ const isAllowed = (allowedRoles) => { // If the token is improperly formatted, return an error if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN); + const error = new Error(errorMessages.INVALID_AUTH_TOKEN(req.language)); error.status = 400; error.service = SERVICE_NAME; next(error); @@ -41,7 +41,7 @@ const isAllowed = (allowedRoles) => { next(); return; } else { - const error = new Error(errorMessages.INSUFFICIENT_PERMISSIONS); + const error = new Error(errorMessages.INSUFFICIENT_PERMISSIONS(req.language)); error.status = 401; error.service = SERVICE_NAME; next(error); diff --git a/Server/middleware/languageMiddleware.js b/Server/middleware/languageMiddleware.js new file mode 100644 index 000000000..52e7b9a2e --- /dev/null +++ b/Server/middleware/languageMiddleware.js @@ -0,0 +1,9 @@ +const languageMiddleware = (req, res, next) => { + const acceptLanguage = req.headers['accept-language'] || 'en'; + + req.language = acceptLanguage.split(',')[0].slice(0, 2).toLowerCase(); + + next(); +}; + +export default languageMiddleware; \ No newline at end of file diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js index 87fa53bd6..08a55cd78 100644 --- a/Server/middleware/verifyJWT.js +++ b/Server/middleware/verifyJWT.js @@ -17,7 +17,7 @@ const verifyJWT = (req, res, next) => { const token = req.headers["authorization"]; // Make sure a token is provided if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN); + const error = new Error(errorMessages.NO_AUTH_TOKEN(req.language)); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -25,7 +25,7 @@ const verifyJWT = (req, res, next) => { } // Make sure it is properly formatted if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token + const error = new Error(errorMessages.INVALID_AUTH_TOKEN(req.language)); // Instantiate a new Error object for improperly formatted token error.status = 400; error.service = SERVICE_NAME; error.method = "verifyJWT"; @@ -43,7 +43,7 @@ const verifyJWT = (req, res, next) => { handleExpiredJwtToken(req, res, next); } else { // Invalid token (signature or token altered or other issue) - const errorMessage = errorMessages.INVALID_AUTH_TOKEN; + const errorMessage = errorMessages.INVALID_AUTH_TOKEN(req.language); return res.status(401).json({ success: false, msg: errorMessage }); } } else { @@ -60,7 +60,7 @@ function handleExpiredJwtToken(req, res, next) { if (!refreshToken) { // No refresh token provided - const error = new Error(errorMessages.NO_REFRESH_TOKEN); + const error = new Error(errorMessages.NO_REFRESH_TOKEN(req.language)); error.status = 401; error.service = SERVICE_NAME; error.method = "handleExpiredJwtToken"; diff --git a/Server/middleware/verifyOwnership.js b/Server/middleware/verifyOwnership.js index d812dd543..10de1f7b6 100644 --- a/Server/middleware/verifyOwnership.js +++ b/Server/middleware/verifyOwnership.js @@ -15,7 +15,7 @@ const verifyOwnership = (Model, paramName) => { service: SERVICE_NAME, method: "verifyOwnership", }); - const error = new Error(errorMessages.VERIFY_OWNER_NOT_FOUND); + const error = new Error(errorMessages.VERIFY_OWNER_NOT_FOUND(req.language)); error.status = 404; throw error; } @@ -23,7 +23,7 @@ const verifyOwnership = (Model, paramName) => { // Special case for User model, as it will not have a `userId` field as other docs will if (Model.modelName === "User") { if (userId.toString() !== doc._id.toString()) { - const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED); + const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED(req.language)); error.status = 403; throw error; } @@ -33,7 +33,7 @@ const verifyOwnership = (Model, paramName) => { // If the userID does not match the document's userID, return a 403 error if (userId.toString() !== doc.userId.toString()) { - const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED); + const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED(req.language)); error.status = 403; throw error; } diff --git a/Server/middleware/verifySuperAdmin.js b/Server/middleware/verifySuperAdmin.js index bf4c780a7..b4b764f5a 100644 --- a/Server/middleware/verifySuperAdmin.js +++ b/Server/middleware/verifySuperAdmin.js @@ -17,7 +17,7 @@ const verifySuperAdmin = (req, res, next) => { const token = req.headers["authorization"]; // Make sure a token is provided if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN); + const error = new Error(errorMessages.NO_AUTH_TOKEN(req.language)); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -25,7 +25,7 @@ const verifySuperAdmin = (req, res, next) => { } // Make sure it is properly formatted if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token + const error = new Error(errorMessages.INVALID_AUTH_TOKEN(req.language)); // Instantiate a new Error object for improperly formatted token error.status = 400; error.service = SERVICE_NAME; error.method = "verifySuperAdmin"; diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index f7c70f0a7..bbcca6601 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -455,7 +455,7 @@ class NewJobQueue { if (wasDeleted === true) { this.logger.info({ - message: successMessages.JOB_QUEUE_DELETE_JOB, + message: successMessages.JOB_QUEUE_DELETE_JOB('en'), service: SERVICE_NAME, method: "deleteJob", details: `Deleted job ${monitor._id}`, @@ -587,7 +587,7 @@ class NewJobQueue { const metrics = await this.getMetrics(); this.logger.info({ - message: successMessages.JOB_QUEUE_OBLITERATE, + message: successMessages.JOB_QUEUE_OBLITERATE('en'), service: SERVICE_NAME, method: "obliterate", details: metrics, diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 8a58d4b88..5e5f62721 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -93,7 +93,7 @@ class NetworkService { pingResponse.code = 200; pingResponse.status = response.alive; - pingResponse.message = successMessages.PING_SUCCESS; + pingResponse.message = successMessages.PING_SUCCESS('en'); return pingResponse; } catch (error) { error.service = this.SERVICE_NAME; @@ -240,7 +240,7 @@ class NetworkService { const containers = await docker.listContainers({ all: true }); const containerExists = containers.some((c) => c.Id.startsWith(job.data.url)); if (!containerExists) { - throw new Error(errorMessages.DOCKER_NOT_FOUND); + throw new Error(errorMessages.DOCKER_NOT_FOUND('en')); } const container = docker.getContainer(job.data.url); @@ -262,7 +262,7 @@ class NetworkService { } dockerResponse.status = response?.State?.Status === "running" ? true : false; dockerResponse.code = 200; - dockerResponse.message = successMessages.DOCKER_SUCCESS; + dockerResponse.message = successMessages.DOCKER_SUCCESS('en'); return dockerResponse; } catch (error) { error.service = this.SERVICE_NAME; @@ -316,7 +316,7 @@ class NetworkService { portResponse.status = response.success; portResponse.code = 200; - portResponse.message = successMessages.PORT_SUCCESS; + portResponse.message = successMessages.PORT_SUCCESS('en'); return portResponse; } catch (error) { error.service = this.SERVICE_NAME; diff --git a/Server/service/translationService.js b/Server/service/translationService.js new file mode 100644 index 000000000..11709262a --- /dev/null +++ b/Server/service/translationService.js @@ -0,0 +1,201 @@ +import axios from 'axios'; +import fs from 'fs'; +import path from 'path'; +import { formattedKey } from '../utils/formattedKey.js'; + +class TranslationService { + static SERVICE_NAME = 'TranslationService'; + + constructor(logger) { + this.logger = logger; + this.translations = {}; + this.apiToken = "ddf8d5fdbe1baa12bb3b5519b639d00a"; + this.projectId = "757606"; + this.baseUrl = 'https://api.poeditor.com/v2'; + this.localesDir = path.join(process.cwd(), 'locales'); + } + + async initialize() { + try { + // Önce dosyalardan okumayı dene + const loadedFromFiles = await this.loadFromFiles(); + + // Eğer dosyalardan yüklenemezse veya dosyalar yoksa POEditor'dan çek + if (!loadedFromFiles) { + await this.loadTranslations(); + } + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'initialize', + stack: error.stack + }); + } + } + + async loadFromFiles() { + try { + // locales klasörü yoksa false dön + if (!fs.existsSync(this.localesDir)) { + return false; + } + + // Klasördeki tüm .json dosyalarını oku + const files = fs.readdirSync(this.localesDir).filter(file => file.endsWith('.json')); + + if (files.length === 0) { + return false; + } + + // Her dosyayı oku ve translations objesine ekle + for (const file of files) { + const language = file.replace('.json', ''); + const filePath = path.join(this.localesDir, file); + const content = fs.readFileSync(filePath, 'utf8'); + this.translations[language] = JSON.parse(content); + } + + this.logger.info({ + message: 'Translations loaded from files successfully', + service: 'TranslationService', + method: 'loadFromFiles' + }); + + return true; + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'loadFromFiles', + stack: error.stack + }); + return false; + } + } + + async loadTranslations() { + try { + // Önce mevcut dilleri al + const languages = await this.getLanguages(); + + // Her dil için çevirileri indir + for (const language of languages) { + const translations = await this.exportTranslations(language); + this.translations[language] = translations; + } + + // Çevirileri dosyaya kaydet + await this.saveTranslations(); + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'loadTranslations', + stack: error.stack + }); + } + } + + async getLanguages() { + try { + const params = new URLSearchParams(); + params.append('api_token', this.apiToken); + params.append('id', this.projectId); + + const response = await axios.post(`${this.baseUrl}/languages/list`, params, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + return response.data.result.languages.map(lang => lang.code); + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'getLanguages', + stack: error.stack + }); + return ['en']; // Varsayılan olarak İngilizce + } + } + + async exportTranslations(language) { + try { + const params = new URLSearchParams(); + params.append('api_token', this.apiToken); + params.append('id', this.projectId); + params.append('language', language); + params.append('type', 'key_value_json'); + + // Export isteği + const exportResponse = await axios.post(`${this.baseUrl}/projects/export`, params, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + // İndirme URL'sini al + const { url } = exportResponse.data.result; + + // Çevirileri indir + const translationsResponse = await axios.get(url); + return translationsResponse.data; + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'exportTranslations', + stack: error.stack + }); + return {}; + } + } + + async saveTranslations() { + try { + // locales klasörü yoksa oluştur + if (!fs.existsSync(this.localesDir)) { + fs.mkdirSync(this.localesDir); + } + + // Her dil için JSON dosyası oluştur + for (const [language, translations] of Object.entries(this.translations)) { + const filePath = path.join(this.localesDir, `${language}.json`); + fs.writeFileSync(filePath, JSON.stringify(translations, null, 2)); + } + + this.logger.info({ + message: 'Translations saved to files successfully', + service: 'TranslationService', + method: 'saveTranslations' + }); + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'saveTranslations', + stack: error.stack + }); + } + } + + getTranslation(key, language = 'en') { + // Convert key from AUTH_INCORRECT_PASSWORD format to authIncorrectPassword format + const formattedKeyText = formattedKey(key); + try { + return this.translations[language]?.[formattedKeyText] || this.translations['en']?.[formattedKeyText] || formattedKeyText; + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'getTranslation', + stack: error.stack + }); + return key; + } + } +} + +export default TranslationService; \ No newline at end of file diff --git a/Server/tests/controllers/authController.test.js b/Server/tests/controllers/authController.test.js index 3f45d1c42..c96b77da3 100644 --- a/Server/tests/controllers/authController.test.js +++ b/Server/tests/controllers/authController.test.js @@ -18,14 +18,16 @@ import { getTokenFromHeaders, tokenType } from "../../utils/utils.js"; import logger from "../../utils/logger.js"; import e from "cors"; -describe("Auth Controller - issueToken", function() { +const mockLanguage = 'en'; + +describe("Auth Controller - issueToken", function () { let stub; - afterEach(function() { + afterEach(function () { sinon.restore(); // Restore stubs after each test }); - it("should reject with an error if jwt.sign fails", function() { + it("should reject with an error if jwt.sign fails", function () { const error = new Error("jwt.sign error"); stub = sinon.stub(jwt, "sign").throws(error); const payload = { id: "123" }; @@ -35,7 +37,7 @@ describe("Auth Controller - issueToken", function() { ); }); - it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", function() { + it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", function () { const payload = { id: "123" }; const appSettings = { jwtSecret: "my_secret" }; const expectedToken = "mockToken"; @@ -45,7 +47,7 @@ describe("Auth Controller - issueToken", function() { expect(token).to.equal(expectedToken); }); - it("should return a token if jwt.sign is successful and appSettings.jwtTTL is defined", function() { + it("should return a token if jwt.sign is successful and appSettings.jwtTTL is defined", function () { const payload = { id: "123" }; const appSettings = { jwtSecret: "my_secret", jwtTTL: "1s" }; const expectedToken = "mockToken"; @@ -55,7 +57,7 @@ describe("Auth Controller - issueToken", function() { expect(token).to.equal(expectedToken); }); - it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is not defined", function() { + it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is not defined", function () { const payload = {}; const appSettings = { refreshTokenSecret: "my_refresh_secret" }; const expectedToken = "mockRefreshToken"; @@ -65,7 +67,7 @@ describe("Auth Controller - issueToken", function() { expect(token).to.equal(expectedToken); }); - it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is defined", function() { + it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is defined", function () { const payload = {}; const appSettings = { refreshTokenSecret: "my_refresh_secret", @@ -79,10 +81,10 @@ describe("Auth Controller - issueToken", function() { }); }); -describe("Auth Controller - registerUser", function() { +describe("Auth Controller - registerUser", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { body: { firstName: "firstname", @@ -118,25 +120,25 @@ describe("Auth Controller - registerUser", function() { sinon.stub(logger, "error"); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if body validation fails", async function() { + it("should reject with an error if body validation fails", async function () { req.body = {}; await registerUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if checkSuperadmin fails", async function() { + it("should reject with an error if checkSuperadmin fails", async function () { req.db.checkSuperadmin.throws(new Error("checkSuperadmin error")); await registerUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); }); - it("should reject with an error if getInviteTokenAndDelete fails", async function() { + it("should reject with an error if getInviteTokenAndDelete fails", async function () { req.db.checkSuperadmin.returns(true); req.db.getInviteTokenAndDelete.throws(new Error("getInviteTokenAndDelete error")); await registerUser(req, res, next); @@ -144,7 +146,7 @@ describe("Auth Controller - registerUser", function() { expect(next.firstCall.args[0].message).to.equal("getInviteTokenAndDelete error"); }); - it("should reject with an error if updateAppSettings fails", async function() { + it("should reject with an error if updateAppSettings fails", async function () { req.db.checkSuperadmin.returns(false); req.db.updateAppSettings.throws(new Error("updateAppSettings error")); await registerUser(req, res, next); @@ -152,7 +154,7 @@ describe("Auth Controller - registerUser", function() { expect(next.firstCall.args[0].message).to.equal("updateAppSettings error"); }); - it("should reject with an error if insertUser fails", async function() { + it("should reject with an error if insertUser fails", async function () { req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); req.db.insertUser.rejects(new Error("insertUser error")); @@ -161,7 +163,7 @@ describe("Auth Controller - registerUser", function() { expect(next.firstCall.args[0].message).to.equal("insertUser error"); }); - it("should reject with an error if settingsService.getSettings fails", async function() { + it("should reject with an error if settingsService.getSettings fails", async function () { req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); req.db.insertUser.resolves({ _id: "123" }); @@ -173,7 +175,7 @@ describe("Auth Controller - registerUser", function() { expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error"); }); - it("should log an error if emailService.buildAndSendEmail fails", async function() { + it("should log an error if emailService.buildAndSendEmail fails", async function () { req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); req.db.insertUser.returns({ _id: "123" }); @@ -187,7 +189,7 @@ describe("Auth Controller - registerUser", function() { expect(logger.error.firstCall.args[0].message).to.equal("emailService error"); }); - it("should return a success message and data if all operations are successful", async function() { + it("should return a success message and data if all operations are successful", async function () { const user = { _id: "123" }; req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); @@ -202,14 +204,14 @@ describe("Auth Controller - registerUser", function() { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_CREATE_USER, + msg: successMessages.AUTH_CREATE_USER(mockLanguage), data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should return a success message and data if all operations are successful and superAdmin true", async function() { + it("should return a success message and data if all operations are successful and superAdmin true", async function () { const user = { _id: "123" }; req.db.checkSuperadmin.resolves(true); req.db.updateAppSettings.resolves(); @@ -224,7 +226,7 @@ describe("Auth Controller - registerUser", function() { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_CREATE_USER, + msg: successMessages.AUTH_CREATE_USER(mockLanguage), data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, }) ).to.be.true; @@ -232,15 +234,16 @@ describe("Auth Controller - registerUser", function() { }); }); -describe("Auth Controller - loginUser", function() { +describe("Auth Controller - loginUser", function () { let req, res, next, user; - beforeEach(function() { + beforeEach(function () { req = { body: { email: "test@example.com", password: "Password123!" }, db: { getUserByEmail: sinon.stub(), }, + language: 'en', settingsService: { getSettings: sinon.stub().resolves({ jwtSecret: "my_secret", @@ -261,21 +264,21 @@ describe("Auth Controller - loginUser", function() { }; }); - it("should reject with an error if validation fails", async function() { + it("should reject with an error if validation fails", async function () { req.body = {}; await loginUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getUserByEmail fails", async function() { + it("should reject with an error if getUserByEmail fails", async function () { req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); await loginUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); }); - it("should login user successfully", async function() { + it("should login user successfully", async function () { req.db.getUserByEmail.resolves(user); user.comparePassword.resolves(true); await loginUser(req, res, next); @@ -283,7 +286,7 @@ describe("Auth Controller - loginUser", function() { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_LOGIN_USER, + msg: successMessages.AUTH_LOGIN_USER(mockLanguage), data: { user: { email: "test@example.com", @@ -297,7 +300,7 @@ describe("Auth Controller - loginUser", function() { expect(next.notCalled).to.be.true; }); - it("should reject a user with an incorrect password", async function() { + it("should reject a user with an incorrect password", async function () { req.body = { email: "test@test.com", password: "Password123!", @@ -307,15 +310,15 @@ describe("Auth Controller - loginUser", function() { await loginUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal( - errorMessages.AUTH_INCORRECT_PASSWORD + errorMessages.AUTH_INCORRECT_PASSWORD(mockLanguage) ); }); }); -describe("Auth Controller - refreshAuthToken", function() { +describe("Auth Controller - refreshAuthToken", function () { let req, res, next, issueTokenStub; - beforeEach(function() { + beforeEach(function () { req = { headers: { "x-refresh-token": "valid_refresh_token", @@ -339,39 +342,39 @@ describe("Auth Controller - refreshAuthToken", function() { sinon.replace({ issueToken }, "issueToken", issueTokenStub); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject if no refresh token is provided", async function() { + it("should reject if no refresh token is provided", async function () { delete req.headers["x-refresh-token"]; await refreshAuthToken(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.NO_REFRESH_TOKEN); + expect(next.firstCall.args[0].message).to.equal(errorMessages.NO_REFRESH_TOKEN(req.language)); expect(next.firstCall.args[0].status).to.equal(401); }); - it("should reject if the refresh token is invalid", async function() { + it("should reject if the refresh token is invalid", async function () { jwt.verify.yields(new Error("invalid token")); await refreshAuthToken(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.INVALID_REFRESH_TOKEN); + expect(next.firstCall.args[0].message).to.equal(errorMessages.INVALID_REFRESH_TOKEN(req.language)); expect(next.firstCall.args[0].status).to.equal(401); }); - it("should reject if the refresh token is expired", async function() { + it("should reject if the refresh token is expired", async function () { const error = new Error("Token expired"); error.name = "TokenExpiredError"; jwt.verify.yields(error); await refreshAuthToken(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.EXPIRED_REFRESH_TOKEN); + expect(next.firstCall.args[0].message).to.equal(errorMessages.EXPIRED_REFRESH_TOKEN(req.language)); expect(next.firstCall.args[0].status).to.equal(401); }); - it("should reject if settingsService.getSettings fails", async function() { + it("should reject if settingsService.getSettings fails", async function () { req.settingsService.getSettings.rejects( new Error("settingsService.getSettings error") ); @@ -381,7 +384,7 @@ describe("Auth Controller - refreshAuthToken", function() { expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error"); }); - it("should generate a new auth token if the refresh token is valid", async function() { + it("should generate a new auth token if the refresh token is valid", async function () { const decodedPayload = { expiresIn: "60" }; jwt.verify.callsFake(() => { return decodedPayload; @@ -392,7 +395,7 @@ describe("Auth Controller - refreshAuthToken", function() { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_TOKEN_REFRESHED, + msg: successMessages.AUTH_TOKEN_REFRESHED(mockLanguage), data: { user: decodedPayload, token: sinon.match.string, @@ -403,10 +406,10 @@ describe("Auth Controller - refreshAuthToken", function() { }); }); -describe("Auth Controller - editUser", function() { +describe("Auth Controller - editUser", function () { let req, res, next, stub, user; - beforeEach(function() { + beforeEach(function () { req = { params: { userId: "123" }, body: { password: "Password1!", newPassword: "Password2!" }, @@ -428,40 +431,40 @@ describe("Auth Controller - editUser", function() { stub = sinon.stub(jwt, "verify").returns({ email: "test@example.com" }); }); - afterEach(function() { + afterEach(function () { sinon.restore(); stub.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await editUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if body validation fails", async function() { + it("should reject with an error if body validation fails", async function () { req.body = { invalid: 1 }; await editUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if param.userId !== req.user._id", async function() { + it("should reject with an error if param.userId !== req.user._id", async function () { req.params = { userId: "456" }; await editUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(401); }); - it("should reject with an error if !req.body.password and getUserByEmail fails", async function() { + it("should reject with an error if !req.body.password and getUserByEmail fails", async function () { req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); await editUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); }); - it("should reject with an error if user.comparePassword fails", async function() { + it("should reject with an error if user.comparePassword fails", async function () { req.db.getUserByEmail.returns({ comparePassword: sinon.stub().rejects(new Error("Bad Password Match")), }); @@ -470,7 +473,7 @@ describe("Auth Controller - editUser", function() { expect(next.firstCall.args[0].message).to.equal("Bad Password Match"); }); - it("should reject with an error if user.comparePassword returns false", async function() { + it("should reject with an error if user.comparePassword returns false", async function () { req.db.getUserByEmail.returns({ comparePassword: sinon.stub().returns(false), }); @@ -478,11 +481,11 @@ describe("Auth Controller - editUser", function() { expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(401); expect(next.firstCall.args[0].message).to.equal( - errorMessages.AUTH_INCORRECT_PASSWORD + errorMessages.AUTH_INCORRECT_PASSWORD(mockLanguage) ); }); - it("should edit a user if it receives a proper request", async function() { + it("should edit a user if it receives a proper request", async function () { const user = { comparePassword: sinon.stub().resolves(true), }; @@ -497,14 +500,14 @@ describe("Auth Controller - editUser", function() { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_UPDATE_USER, + msg: successMessages.AUTH_UPDATE_USER(mockLanguage), data: { email: "test@example.com" }, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should edit a user if it receives a proper request and both password fields are undefined", async function() { + it("should edit a user if it receives a proper request and both password fields are undefined", async function () { req.body.password = undefined; req.body.newPassword = undefined; req.db.getUserByEmail.resolves(user); @@ -515,14 +518,14 @@ describe("Auth Controller - editUser", function() { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_UPDATE_USER, + msg: successMessages.AUTH_UPDATE_USER(mockLanguage), data: { email: "test@example.com" }, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should reject an edit request if password format is incorrect", async function() { + it("should reject an edit request if password format is incorrect", async function () { req.body = { password: "bad_password", newPassword: "bad_password" }; const user = { comparePassword: sinon.stub().resolves(true), @@ -536,10 +539,10 @@ describe("Auth Controller - editUser", function() { }); }); -describe("Auth Controller - checkSuperadminExists", function() { +describe("Auth Controller - checkSuperadminExists", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { db: { checkSuperadmin: sinon.stub(), @@ -552,35 +555,35 @@ describe("Auth Controller - checkSuperadminExists", function() { next = sinon.stub(); }); - it("should reject with an error if checkSuperadmin fails", async function() { + it("should reject with an error if checkSuperadmin fails", async function () { req.db.checkSuperadmin.rejects(new Error("checkSuperadmin error")); await checkSuperadminExists(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); }); - it("should return true if a superadmin exists", async function() { + it("should return true if a superadmin exists", async function () { req.db.checkSuperadmin.resolves(true); await checkSuperadminExists(req, res, next); expect(res.status.calledWith(200)).to.be.true; expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_SUPERADMIN_EXISTS, + msg: successMessages.AUTH_SUPERADMIN_EXISTS(mockLanguage), data: true, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should return false if a superadmin does not exist", async function() { + it("should return false if a superadmin does not exist", async function () { req.db.checkSuperadmin.resolves(false); await checkSuperadminExists(req, res, next); expect(res.status.calledWith(200)).to.be.true; expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_SUPERADMIN_EXISTS, + msg: successMessages.AUTH_SUPERADMIN_EXISTS(mockLanguage), data: false, }) ).to.be.true; @@ -588,10 +591,10 @@ describe("Auth Controller - checkSuperadminExists", function() { }); }); -describe("Auth Controller - requestRecovery", function() { +describe("Auth Controller - requestRecovery", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { body: { email: "test@test.com" }, db: { @@ -612,21 +615,21 @@ describe("Auth Controller - requestRecovery", function() { next = sinon.stub(); }); - it("should reject with an error if validation fails", async function() { + it("should reject with an error if validation fails", async function () { req.body = {}; await requestRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getUserByEmail fails", async function() { + it("should reject with an error if getUserByEmail fails", async function () { req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); await requestRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); }); - it("should throw an error if the user is not found", async function() { + it("should throw an error if the user is not found", async function () { req.db.getUserByEmail.resolves(null); await requestRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); @@ -635,14 +638,14 @@ describe("Auth Controller - requestRecovery", function() { // ); }); - it("should throw an error if the email is not provided", async function() { + it("should throw an error if the email is not provided", async function () { req.body = {}; await requestRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should return a success message if the email is provided", async function() { + it("should return a success message if the email is provided", async function () { const user = { firstName: "John" }; const recoveryToken = { token: "recovery-token" }; const msgId = "message-id"; @@ -668,7 +671,7 @@ describe("Auth Controller - requestRecovery", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, + msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN(mockLanguage), data: msgId, }) ).to.be.true; @@ -676,10 +679,10 @@ describe("Auth Controller - requestRecovery", function() { }); }); -describe("Auth Controller - validateRecovery", function() { +describe("Auth Controller - validateRecovery", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { body: { recoveryToken: "recovery-token" }, db: { @@ -693,38 +696,38 @@ describe("Auth Controller - validateRecovery", function() { next = sinon.stub(); }); - it("should reject with an error if validation fails", async function() { + it("should reject with an error if validation fails", async function () { req.body = {}; await validateRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if validateRecoveryToken fails", async function() { + it("should reject with an error if validateRecoveryToken fails", async function () { req.db.validateRecoveryToken.rejects(new Error("validateRecoveryToken error")); await validateRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("validateRecoveryToken error"); }); - it("should return a success message if the token is valid", async function() { + it("should return a success message if the token is valid", async function () { req.db.validateRecoveryToken.resolves(); await validateRecovery(req, res, next); expect(res.status.calledOnceWith(200)).to.be.true; expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN, + msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN(mockLanguage), }) ).to.be.true; expect(next.notCalled).to.be.true; }); }); -describe("Auth Controller - resetPassword", function() { +describe("Auth Controller - resetPassword", function () { let req, res, next, newPasswordValidation, handleValidationError, handleError; - beforeEach(function() { + beforeEach(function () { req = { body: { recoveryToken: "recovery-token", @@ -749,14 +752,14 @@ describe("Auth Controller - resetPassword", function() { handleError = sinon.stub(); }); - it("should reject with an error if validation fails", async function() { + it("should reject with an error if validation fails", async function () { req.body = { password: "bad_password" }; await resetPassword(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if resetPassword fails", async function() { + it("should reject with an error if resetPassword fails", async function () { const error = new Error("resetPassword error"); newPasswordValidation.validateAsync.resolves(); req.db.resetPassword.rejects(error); @@ -765,7 +768,7 @@ describe("Auth Controller - resetPassword", function() { expect(next.firstCall.args[0].message).to.equal("resetPassword error"); }); - it("should reset password successfully", async function() { + it("should reset password successfully", async function () { const user = { _doc: {} }; const appSettings = { jwtSecret: "my_secret" }; const token = "token"; @@ -782,7 +785,7 @@ describe("Auth Controller - resetPassword", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_RESET_PASSWORD, + msg: successMessages.AUTH_RESET_PASSWORD(mockLanguage), data: { user: sinon.match.object, token: sinon.match.string }, }) ).to.be.true; @@ -790,10 +793,10 @@ describe("Auth Controller - resetPassword", function() { }); }); -describe("Auth Controller - deleteUser", function() { +describe("Auth Controller - deleteUser", function () { let req, res, next, handleError; - beforeEach(function() { + beforeEach(function () { req = { headers: { authorization: "Bearer token", @@ -825,24 +828,24 @@ describe("Auth Controller - deleteUser", function() { handleError = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should throw an error if user is not found", async function() { + it("should throw an error if user is not found", async function () { jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.throws(new Error(errorMessages.DB_USER_NOT_FOUND)); + req.db.getUserByEmail.throws(new Error(errorMessages.DB_USER_NOT_FOUND(req.language))); await deleteUser(req, res, next); expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; expect(next.calledOnce).to.be.true; - expect(next.firstCall.args[0].message).to.equal(errorMessages.DB_USER_NOT_FOUND); + expect(next.firstCall.args[0].message).to.equal(errorMessages.DB_USER_NOT_FOUND(req.language)); expect(res.status.notCalled).to.be.true; expect(res.json.notCalled).to.be.true; }); - it("should delete user and associated data if user is superadmin", async function() { + it("should delete user and associated data if user is superadmin", async function () { const user = { _id: "user_id", email: "test@example.com", @@ -876,13 +879,13 @@ describe("Auth Controller - deleteUser", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_DELETE_USER, + msg: successMessages.AUTH_DELETE_USER(mockLanguage), }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should delete user if user is not superadmin", async function() { + it("should delete user if user is not superadmin", async function () { const user = { _id: "user_id", email: "test@example.com", @@ -906,13 +909,13 @@ describe("Auth Controller - deleteUser", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_DELETE_USER, + msg: successMessages.AUTH_DELETE_USER(mockLanguage), }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should handle errors", async function() { + it("should handle errors", async function () { const error = new Error("Something went wrong"); const SERVICE_NAME = "AuthController"; jwt.decode.returns({ email: "test@example.com" }); @@ -925,10 +928,10 @@ describe("Auth Controller - deleteUser", function() { }); }); -describe("Auth Controller - getAllUsers", function() { +describe("Auth Controller - getAllUsers", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { db: { getAllUsers: sinon.stub(), @@ -941,11 +944,11 @@ describe("Auth Controller - getAllUsers", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); // Restore the original methods after each test }); - it("should return 200 and all users", async function() { + it("should return 200 and all users", async function () { const allUsers = [{ id: 1, name: "John Doe" }]; req.db.getAllUsers.resolves(allUsers); @@ -963,7 +966,7 @@ describe("Auth Controller - getAllUsers", function() { expect(next.notCalled).to.be.true; }); - it("should call next with error when an exception occurs", async function() { + it("should call next with error when an exception occurs", async function () { const error = new Error("Something went wrong"); req.db.getAllUsers.rejects(error); await getAllUsers(req, res, next); diff --git a/Server/tests/controllers/checkController.test.js b/Server/tests/controllers/checkController.test.js index 0c00b9487..67207bd9b 100644 --- a/Server/tests/controllers/checkController.test.js +++ b/Server/tests/controllers/checkController.test.js @@ -9,11 +9,12 @@ import { import jwt from "jsonwebtoken"; import { errorMessages, successMessages } from "../../utils/messages.js"; import sinon from "sinon"; -describe("Check Controller - createCheck", function() { +describe("Check Controller - createCheck", function () { let req, res, next, handleError; - beforeEach(function() { + beforeEach(function () { req = { + language: 'en', params: {}, body: {}, db: { @@ -28,17 +29,17 @@ describe("Check Controller - createCheck", function() { handleError = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); // Restore the original methods after each test }); - it("should reject with a validation if params are invalid", async function() { + it("should reject with a validation if params are invalid", async function () { await createCheck(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with a validation error if body is invalid", async function() { + it("should reject with a validation error if body is invalid", async function () { req.params = { monitorId: "monitorId", }; @@ -47,7 +48,7 @@ describe("Check Controller - createCheck", function() { expect(next.firstCall.args[0].status).to.equal(422); }); - it("should call next with error if data retrieval fails", async function() { + it("should call next with error if data retrieval fails", async function () { req.params = { monitorId: "monitorId", }; @@ -63,7 +64,7 @@ describe("Check Controller - createCheck", function() { expect(next.firstCall.args[0]).to.be.an("error"); }); - it("should return a success message if check is created", async function() { + it("should return a success message if check is created", async function () { req.params = { monitorId: "monitorId", }; @@ -80,7 +81,7 @@ describe("Check Controller - createCheck", function() { expect( res.json.calledWith({ success: true, - msg: successMessages.CHECK_CREATE, + msg: successMessages.CHECK_CREATE(req.language), data: { id: "123" }, }) ).to.be.true; @@ -88,10 +89,10 @@ describe("Check Controller - createCheck", function() { }); }); -describe("Check Controller - getChecks", function() { +describe("Check Controller - getChecks", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: {}, query: {}, @@ -107,17 +108,17 @@ describe("Check Controller - getChecks", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with a validation error if params are invalid", async function() { + it("should reject with a validation error if params are invalid", async function () { await getChecks(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should return a success message if checks are found", async function() { + it("should return a success message if checks are found", async function () { req.params = { monitorId: "monitorId", }; @@ -128,14 +129,14 @@ describe("Check Controller - getChecks", function() { expect( res.json.calledWith({ success: true, - msg: successMessages.CHECK_GET, + msg: successMessages.CHECK_GET(req.language), data: { checksCount: 1, checks: [{ id: "123" }] }, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should call next with error if data retrieval fails", async function() { + it("should call next with error if data retrieval fails", async function () { req.params = { monitorId: "monitorId", }; @@ -145,10 +146,10 @@ describe("Check Controller - getChecks", function() { }); }); -describe("Check Controller - getTeamChecks", function() { +describe("Check Controller - getTeamChecks", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: {}, query: {}, @@ -163,17 +164,17 @@ describe("Check Controller - getTeamChecks", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with a validation error if params are invalid", async function() { + it("should reject with a validation error if params are invalid", async function () { await getTeamChecks(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should return 200 and check data on successful validation and data retrieval", async function() { + it("should return 200 and check data on successful validation and data retrieval", async function () { req.params = { teamId: "1" }; const checkData = [{ id: 1, name: "Check 1" }]; req.db.getTeamChecks.resolves(checkData); @@ -184,13 +185,13 @@ describe("Check Controller - getTeamChecks", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.CHECK_GET, + msg: successMessages.CHECK_GET(req.language), data: checkData, }) ).to.be.true; }); - it("should call next with error if data retrieval fails", async function() { + it("should call next with error if data retrieval fails", async function () { req.params = { teamId: "1" }; req.db.getTeamChecks.rejects(new Error("Retrieval Error")); await getTeamChecks(req, res, next); @@ -201,10 +202,10 @@ describe("Check Controller - getTeamChecks", function() { }); }); -describe("Check Controller - deleteChecks", function() { +describe("Check Controller - deleteChecks", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: {}, db: { @@ -218,17 +219,17 @@ describe("Check Controller - deleteChecks", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { await deleteChecks(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should call next with error if data retrieval fails", async function() { + it("should call next with error if data retrieval fails", async function () { req.params = { monitorId: "1" }; req.db.deleteChecks.rejects(new Error("Deletion Error")); await deleteChecks(req, res, next); @@ -238,7 +239,7 @@ describe("Check Controller - deleteChecks", function() { expect(res.json.notCalled).to.be.true; }); - it("should delete checks successfully", async function() { + it("should delete checks successfully", async function () { req.params = { monitorId: "123" }; req.db.deleteChecks.resolves(1); await deleteChecks(req, res, next); @@ -247,17 +248,17 @@ describe("Check Controller - deleteChecks", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.CHECK_DELETE, + msg: successMessages.CHECK_DELETE(req.language), data: { deletedCount: 1 }, }) ).to.be.true; }); }); -describe("Check Controller - deleteChecksByTeamId", function() { +describe("Check Controller - deleteChecksByTeamId", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: {}, db: { @@ -271,17 +272,17 @@ describe("Check Controller - deleteChecksByTeamId", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { await deleteChecksByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should call next with error if data retrieval fails", async function() { + it("should call next with error if data retrieval fails", async function () { req.params = { teamId: "1" }; req.db.deleteChecksByTeamId.rejects(new Error("Deletion Error")); await deleteChecksByTeamId(req, res, next); @@ -291,7 +292,7 @@ describe("Check Controller - deleteChecksByTeamId", function() { expect(res.json.notCalled).to.be.true; }); - it("should delete checks successfully", async function() { + it("should delete checks successfully", async function () { req.params = { teamId: "123" }; req.db.deleteChecksByTeamId.resolves(1); await deleteChecksByTeamId(req, res, next); @@ -300,17 +301,17 @@ describe("Check Controller - deleteChecksByTeamId", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.CHECK_DELETE, + msg: successMessages.CHECK_DELETE(req.language), data: { deletedCount: 1 }, }) ).to.be.true; }); }); -describe("Check Controller - updateCheckTTL", function() { +describe("Check Controller - updateCheckTTL", function () { let stub, req, res, next; - beforeEach(function() { + beforeEach(function () { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -332,18 +333,18 @@ describe("Check Controller - updateCheckTTL", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); stub.restore(); }); - it("should reject if body validation fails", async function() { + it("should reject if body validation fails", async function () { await updateChecksTTL(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should throw a JwtError if verification fails", async function() { + it("should throw a JwtError if verification fails", async function () { stub.restore(); req.body = { ttl: 1, @@ -352,7 +353,7 @@ describe("Check Controller - updateCheckTTL", function() { expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); }); - it("should call next with error if data retrieval fails", async function() { + it("should call next with error if data retrieval fails", async function () { req.body = { ttl: 1, }; @@ -361,7 +362,7 @@ describe("Check Controller - updateCheckTTL", function() { expect(next.firstCall.args[0]).to.be.an("error"); }); - it("should update TTL successfully", async function() { + it("should update TTL successfully", async function () { req.body = { ttl: 1, }; @@ -372,7 +373,7 @@ describe("Check Controller - updateCheckTTL", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.CHECK_UPDATE_TTL, + msg: successMessages.CHECK_UPDATE_TTL(req.language), }) ).to.be.true; }); diff --git a/Server/tests/controllers/maintenanceWindowController.test.js b/Server/tests/controllers/maintenanceWindowController.test.js index 974bc07be..c4b03b23b 100644 --- a/Server/tests/controllers/maintenanceWindowController.test.js +++ b/Server/tests/controllers/maintenanceWindowController.test.js @@ -11,11 +11,12 @@ import jwt from "jsonwebtoken"; import { successMessages } from "../../utils/messages.js"; import sinon from "sinon"; -describe("maintenanceWindowController - createMaintenanceWindows", function() { +describe("maintenanceWindowController - createMaintenanceWindows", function () { let req, res, next, stub; - beforeEach(function() { + beforeEach(function () { req = { + language: 'en', body: { monitors: ["66ff52e7c5911c61698ac724"], name: "window", @@ -41,11 +42,11 @@ describe("maintenanceWindowController - createMaintenanceWindows", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if body validation fails", async function() { + it("should reject with an error if body validation fails", async function () { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -56,14 +57,14 @@ describe("maintenanceWindowController - createMaintenanceWindows", function() { stub.restore(); }); - it("should reject with an error if jwt.verify fails", async function() { + it("should reject with an error if jwt.verify fails", async function () { stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); await createMaintenanceWindows(req, res, next); expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); stub.restore(); }); - it("should reject with an error DB operations fail", async function() { + it("should reject with an error DB operations fail", async function () { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -74,7 +75,7 @@ describe("maintenanceWindowController - createMaintenanceWindows", function() { stub.restore(); }); - it("should return success message if all operations are successful", async function() { + it("should return success message if all operations are successful", async function () { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -83,13 +84,13 @@ describe("maintenanceWindowController - createMaintenanceWindows", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE, + msg: successMessages.MAINTENANCE_WINDOW_CREATE(req.language), }) ).to.be.true; stub.restore(); }); - it("should return success message if all operations are successful with active set to undefined", async function() { + it("should return success message if all operations are successful with active set to undefined", async function () { req.body.active = undefined; stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; @@ -99,17 +100,17 @@ describe("maintenanceWindowController - createMaintenanceWindows", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE, + msg: successMessages.MAINTENANCE_WINDOW_CREATE(req.language), }) ).to.be.true; stub.restore(); }); }); -describe("maintenanceWindowController - getMaintenanceWindowById", function() { +describe("maintenanceWindowController - getMaintenanceWindowById", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { body: {}, params: { @@ -121,6 +122,7 @@ describe("maintenanceWindowController - getMaintenanceWindowById", function() { settingsService: { getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), }, + language: 'en', db: { getMaintenanceWindowById: sinon.stub(), }, @@ -132,38 +134,38 @@ describe("maintenanceWindowController - getMaintenanceWindowById", function() { next = sinon.stub(); }); - it("should reject if param validation fails", async function() { + it("should reject if param validation fails", async function () { req.params = {}; await getMaintenanceWindowById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject if DB operations fail", async function() { + it("should reject if DB operations fail", async function () { req.db.getMaintenanceWindowById.throws(new Error("DB error")); await getMaintenanceWindowById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message with data if all operations are successful", async function() { + it("should return success message with data if all operations are successful", async function () { req.db.getMaintenanceWindowById.returns({ id: "123" }); await getMaintenanceWindowById(req, res, next); expect(res.status.firstCall.args[0]).to.equal(200); expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID(req.language), data: { id: "123" }, }) ).to.be.true; }); }); -describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function() { +describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function () { let req, res, next, stub; - beforeEach(function() { + beforeEach(function () { req = { body: {}, params: {}, @@ -177,6 +179,7 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function db: { getMaintenanceWindowsByTeamId: sinon.stub(), }, + language: 'en', }; res = { status: sinon.stub().returnsThis(), @@ -185,7 +188,7 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function next = sinon.stub(); }); - it("should reject if query validation fails", async function() { + it("should reject if query validation fails", async function () { req.query = { invalid: 1, }; @@ -194,14 +197,14 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject if jwt.verify fails", async function() { + it("should reject if jwt.verify fails", async function () { stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); await getMaintenanceWindowsByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); stub.restore(); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -212,7 +215,7 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function stub.restore(); }); - it("should return success message with data if all operations are successful", async function() { + it("should return success message with data if all operations are successful", async function () { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -222,7 +225,7 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM(req.language), data: [{ id: jwt.verify().teamId }], }) ).to.be.true; @@ -230,10 +233,10 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function }); }); -describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", function() { +describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { body: {}, params: { @@ -257,25 +260,25 @@ describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", funct next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject if param validation fails", async function() { + it("should reject if param validation fails", async function () { req.params = {}; await getMaintenanceWindowsByMonitorId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.getMaintenanceWindowsByMonitorId.throws(new Error("DB error")); await getMaintenanceWindowsByMonitorId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message with data if all operations are successful", async function() { + it("should return success message with data if all operations are successful", async function () { const data = [{ monitorId: "123" }]; req.db.getMaintenanceWindowsByMonitorId.returns(data); await getMaintenanceWindowsByMonitorId(req, res, next); @@ -284,17 +287,17 @@ describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", funct expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_MONITOR, + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_MONITOR(req.language), data: data, }) ).to.be.true; }); }); -describe("maintenanceWindowController - deleteMaintenanceWindow", function() { +describe("maintenanceWindowController - deleteMaintenanceWindow", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { body: {}, params: { @@ -318,46 +321,47 @@ describe("maintenanceWindowController - deleteMaintenanceWindow", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject if param validation fails", async function() { + it("should reject if param validation fails", async function () { req.params = {}; await deleteMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.deleteMaintenanceWindowById.throws(new Error("DB error")); await deleteMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message if all operations are successful", async function() { + it("should return success message if all operations are successful", async function () { await deleteMaintenanceWindow(req, res, next); expect(req.db.deleteMaintenanceWindowById.calledOnceWith(req.params.id)); expect(res.status.firstCall.args[0]).to.equal(200); expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_DELETE, + msg: successMessages.MAINTENANCE_WINDOW_DELETE(req.language), }) ).to.be.true; }); }); -describe("maintenanceWindowController - editMaintenanceWindow", function() { +describe("maintenanceWindowController - editMaintenanceWindow", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { body: { active: true, name: "test", }, + language: 'en', params: { id: "123", }, @@ -379,32 +383,32 @@ describe("maintenanceWindowController - editMaintenanceWindow", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject if param validation fails", async function() { + it("should reject if param validation fails", async function () { req.params = {}; await editMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject if body validation fails", async function() { + it("should reject if body validation fails", async function () { req.body = { invalid: 1 }; await editMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.editMaintenanceWindowById.throws(new Error("DB error")); await editMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message with data if all operations are successful", async function() { + it("should return success message with data if all operations are successful", async function () { const data = { id: "123" }; req.db.editMaintenanceWindowById.returns(data); @@ -414,7 +418,7 @@ describe("maintenanceWindowController - editMaintenanceWindow", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_EDIT, + msg: successMessages.MAINTENANCE_WINDOW_EDIT(req.language), data: data, }) ).to.be.true; diff --git a/Server/tests/controllers/monitorController.test.js b/Server/tests/controllers/monitorController.test.js index 33440d897..ea35abac4 100644 --- a/Server/tests/controllers/monitorController.test.js +++ b/Server/tests/controllers/monitorController.test.js @@ -19,16 +19,16 @@ import sinon from "sinon"; import { successMessages } from "../../utils/messages.js"; import logger from "../../utils/logger.js"; import axios from "axios"; -const SERVICE_NAME = "monitorController"; -describe("Monitor Controller - getAllMonitors", function() { +describe("Monitor Controller - getAllMonitors", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: {}, query: {}, body: {}, + language: 'en', db: { getAllMonitors: sinon.stub(), }, @@ -40,18 +40,18 @@ describe("Monitor Controller - getAllMonitors", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.getAllMonitors.throws(new Error("DB error")); await getAllMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function() { + it("should return success message and data if all operations succeed", async function () { const data = [{ monitor: "data" }]; req.db.getAllMonitors.returns(data); await getAllMonitors(req, res, next); @@ -59,20 +59,21 @@ describe("Monitor Controller - getAllMonitors", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_ALL, + msg: successMessages.MONITOR_GET_ALL(req.language), data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getAllMonitorsWithUptimeStats", function() { +describe("Monitor Controller - getAllMonitorsWithUptimeStats", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: {}, query: {}, body: {}, + language: 'en', db: { getAllMonitorsWithUptimeStats: sinon.stub(), }, @@ -84,18 +85,18 @@ describe("Monitor Controller - getAllMonitorsWithUptimeStats", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.getAllMonitorsWithUptimeStats.throws(new Error("DB error")); await getAllMonitorsWithUptimeStats(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function() { + it("should return success message and data if all operations succeed", async function () { const data = [{ monitor: "data" }]; req.db.getAllMonitorsWithUptimeStats.returns(data); await getAllMonitorsWithUptimeStats(req, res, next); @@ -103,23 +104,24 @@ describe("Monitor Controller - getAllMonitorsWithUptimeStats", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_ALL, + msg: successMessages.MONITOR_GET_ALL(req.language), data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getMonitorStatsById", function() { +describe("Monitor Controller - getMonitorStatsById", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: { monitorId: "123", }, query: {}, body: {}, + language: 'en', db: { getMonitorStatsById: sinon.stub(), }, @@ -131,32 +133,32 @@ describe("Monitor Controller - getMonitorStatsById", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await getMonitorStatsById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if query validation fails", async function() { + it("should reject with an error if query validation fails", async function () { req.query = { invalid: 1 }; await getMonitorStatsById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.getMonitorStatsById.throws(new Error("DB error")); await getMonitorStatsById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function() { + it("should return success message and data if all operations succeed", async function () { const data = [{ monitorStats: "data" }]; req.db.getMonitorStatsById.returns(data); await getMonitorStatsById(req, res, next); @@ -164,23 +166,24 @@ describe("Monitor Controller - getMonitorStatsById", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_STATS_BY_ID, + msg: successMessages.MONITOR_STATS_BY_ID(req.language), data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getMonitorCertificate", function() { +describe("Monitor Controller - getMonitorCertificate", function () { let req, res, next, fetchMonitorCertificate; - beforeEach(function() { + beforeEach(function () { req = { params: { monitorId: "123", }, query: {}, body: {}, + language: 'en', db: { getMonitorById: sinon.stub(), }, @@ -193,25 +196,25 @@ describe("Monitor Controller - getMonitorCertificate", function() { fetchMonitorCertificate = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await getMonitorCertificate(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getMonitorById operation fails", async function() { + it("should reject with an error if getMonitorById operation fails", async function () { req.db.getMonitorById.throws(new Error("DB error")); await getMonitorCertificate(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed with a valid cert", async function() { + it("should return success message and data if all operations succeed with a valid cert", async function () { req.db.getMonitorById.returns({ url: "https://www.google.com" }); const data = { certificate: "cert", validTo: "2024/08/08" }; fetchMonitorCertificate.returns(data); @@ -220,13 +223,13 @@ describe("Monitor Controller - getMonitorCertificate", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_CERTIFICATE, + msg: successMessages.MONITOR_CERTIFICATE(req.language), data: { certificateDate: new Date(data.validTo) }, }) ).to.be.true; }); - it("should return an error if fetchMonitorCertificate fails", async function() { + it("should return an error if fetchMonitorCertificate fails", async function () { req.db.getMonitorById.returns({ url: "https://www.google.com" }); fetchMonitorCertificate.throws(new Error("Certificate error")); await getMonitorCertificate(req, res, next, fetchMonitorCertificate); @@ -235,16 +238,17 @@ describe("Monitor Controller - getMonitorCertificate", function() { }); }); -describe("Monitor Controller - getMonitorById", function() { +describe("Monitor Controller - getMonitorById", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: { monitorId: "123", }, query: {}, body: {}, + language: 'en', db: { getMonitorById: sinon.stub(), }, @@ -256,32 +260,32 @@ describe("Monitor Controller - getMonitorById", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await getMonitorById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if query param validation fails", async function() { + it("should reject with an error if query param validation fails", async function () { req.query = { invalid: 1 }; await getMonitorById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.getMonitorById.throws(new Error("DB error")); await getMonitorById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return 404 if a monitor is not found", async function() { + it("should return 404 if a monitor is not found", async function () { const error = new Error("Monitor not found"); error.status = 404; req.db.getMonitorById.throws(error); @@ -290,7 +294,7 @@ describe("Monitor Controller - getMonitorById", function() { expect(next.firstCall.args[0].status).to.equal(404); }); - it("should return success message and data if all operations succeed", async function() { + it("should return success message and data if all operations succeed", async function () { const data = { monitor: "data" }; req.db.getMonitorById.returns(data); await getMonitorById(req, res, next); @@ -298,23 +302,24 @@ describe("Monitor Controller - getMonitorById", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_BY_ID, + msg: successMessages.MONITOR_GET_BY_ID(req.language), data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function() { +describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: { teamId: "123", }, query: {}, body: {}, + language: 'en', db: { getMonitorsAndSummaryByTeamId: sinon.stub(), }, @@ -326,32 +331,32 @@ describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await getMonitorsAndSummaryByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if query validation fails", async function() { + it("should reject with an error if query validation fails", async function () { req.query = { invalid: 1 }; await getMonitorsAndSummaryByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.getMonitorsAndSummaryByTeamId.throws(new Error("DB error")); await getMonitorsAndSummaryByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function() { + it("should return success message and data if all operations succeed", async function () { const data = { monitors: "data", summary: "data" }; req.db.getMonitorsAndSummaryByTeamId.returns(data); await getMonitorsAndSummaryByTeamId(req, res, next); @@ -359,23 +364,24 @@ describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), + msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId, req.language), data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getMonitorsByTeamId", function() { +describe("Monitor Controller - getMonitorsByTeamId", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: { teamId: "123", }, query: {}, body: {}, + language: 'en', db: { getMonitorsByTeamId: sinon.stub(), }, @@ -387,32 +393,32 @@ describe("Monitor Controller - getMonitorsByTeamId", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await getMonitorsByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if query validation fails", async function() { + it("should reject with an error if query validation fails", async function () { req.query = { invalid: 1 }; await getMonitorsByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function() { + it("should reject with an error if DB operations fail", async function () { req.db.getMonitorsByTeamId.throws(new Error("DB error")); await getMonitorsByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function() { + it("should return success message and data if all operations succeed", async function () { const data = { monitors: "data" }; req.db.getMonitorsByTeamId.returns(data); await getMonitorsByTeamId(req, res, next); @@ -420,17 +426,17 @@ describe("Monitor Controller - getMonitorsByTeamId", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), + msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId, req.language), data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - createMonitor", function() { +describe("Monitor Controller - createMonitor", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: {}, query: {}, @@ -443,6 +449,7 @@ describe("Monitor Controller - createMonitor", function() { url: "https://example.com", notifications: [{ email: "example@example.com" }], }, + language: 'en', db: { createMonitor: sinon.stub(), createNotification: sinon.stub(), @@ -458,25 +465,25 @@ describe("Monitor Controller - createMonitor", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if body validation fails", async function() { + it("should reject with an error if body validation fails", async function () { req.body = {}; await createMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB createMonitor operation fail", async function() { + it("should reject with an error if DB createMonitor operation fail", async function () { req.db.createMonitor.throws(new Error("DB error")); await createMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if DB createNotification operation fail", async function() { + it("should reject with an error if DB createNotification operation fail", async function () { req.db.createNotification.throws(new Error("DB error")); req.db.createMonitor.returns({ _id: "123" }); await createMonitor(req, res, next); @@ -484,7 +491,7 @@ describe("Monitor Controller - createMonitor", function() { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if monitor.save operation fail", async function() { + it("should reject with an error if monitor.save operation fail", async function () { req.db.createMonitor.returns({ _id: "123", save: sinon.stub().throws(new Error("Monitor save error")), @@ -494,7 +501,7 @@ describe("Monitor Controller - createMonitor", function() { expect(next.firstCall.args[0].message).to.equal("Monitor save error"); }); - it("should throw an error if addJob operation fails", async function() { + it("should throw an error if addJob operation fails", async function () { req.db.createMonitor.returns({ _id: "123", save: sinon.stub() }); req.jobQueue.addJob.throws(new Error("Job error")); await createMonitor(req, res, next); @@ -502,7 +509,7 @@ describe("Monitor Controller - createMonitor", function() { expect(next.firstCall.args[0].message).to.equal("Job error"); }); - it("should return success message and data if all operations succeed", async function() { + it("should return success message and data if all operations succeed", async function () { const monitor = { _id: "123", save: sinon.stub() }; req.db.createMonitor.returns(monitor); await createMonitor(req, res, next); @@ -510,28 +517,28 @@ describe("Monitor Controller - createMonitor", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_CREATE, + msg: successMessages.MONITOR_CREATE(req.language), data: monitor, }) ).to.be.true; }); }); -describe("Monitor Controller - checkEndpointResolution", function() { +describe("Monitor Controller - checkEndpointResolution", function () { let req, res, next, axiosGetStub; - beforeEach(function() { + beforeEach(function () { req = { query: { monitorURL: "https://example.com" } }; res = { status: sinon.stub().returnsThis(), json: sinon.stub() }; next = sinon.stub(); axiosGetStub = sinon.stub(axios, "get"); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should resolve the URL successfully", async function() { + it("should resolve the URL successfully", async function () { axiosGetStub.resolves({ status: 200, statusText: "OK" }); await checkEndpointResolution(req, res, next); expect(res.status.calledWith(200)).to.be.true; @@ -546,7 +553,7 @@ describe("Monitor Controller - checkEndpointResolution", function() { expect(next.called).to.be.false; }); - it("should return an error if endpoint resolution fails", async function() { + it("should return an error if endpoint resolution fails", async function () { const axiosError = new Error("resolution failed"); axiosError.code = "ENOTFOUND"; axiosGetStub.rejects(axiosError); @@ -559,7 +566,7 @@ describe("Monitor Controller - checkEndpointResolution", function() { expect(errorPassedToNext.status).to.equal(500); }); - it("should reject with an error if query validation fails", async function() { + it("should reject with an error if query validation fails", async function () { req.query.monitorURL = "invalid-url"; await checkEndpointResolution(req, res, next); expect(next.calledOnce).to.be.true; @@ -570,16 +577,17 @@ describe("Monitor Controller - checkEndpointResolution", function() { }); }); -describe("Monitor Controller - deleteMonitor", function() { +describe("Monitor Controller - deleteMonitor", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { params: { monitorId: "123", }, query: {}, body: {}, + language: 'en', db: { deleteMonitor: sinon.stub(), deleteChecks: sinon.stub(), @@ -598,25 +606,25 @@ describe("Monitor Controller - deleteMonitor", function() { sinon.stub(logger, "error"); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await deleteMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB deleteMonitor operation fail", async function() { + it("should reject with an error if DB deleteMonitor operation fail", async function () { req.db.deleteMonitor.throws(new Error("DB error")); await deleteMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should log an error if deleteJob throws an error", async function() { + it("should log an error if deleteJob throws an error", async function () { const error = new Error("Job error"); const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); @@ -628,7 +636,7 @@ describe("Monitor Controller - deleteMonitor", function() { ); }); - it("should log an error if deleteChecks throws an error", async function() { + it("should log an error if deleteChecks throws an error", async function () { const error = new Error("Checks error"); const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); @@ -640,7 +648,7 @@ describe("Monitor Controller - deleteMonitor", function() { ); }); - it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function() { + it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function () { const error = new Error("PageSpeed error"); const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); @@ -652,7 +660,7 @@ describe("Monitor Controller - deleteMonitor", function() { ); }); - it("should log an error if deleteNotificationsByMonitorId throws an error", async function() { + it("should log an error if deleteNotificationsByMonitorId throws an error", async function () { const error = new Error("Notifications error"); const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); @@ -664,7 +672,7 @@ describe("Monitor Controller - deleteMonitor", function() { ); }); - it("should return success message if all operations succeed", async function() { + it("should return success message if all operations succeed", async function () { const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); await deleteMonitor(req, res, next); @@ -672,16 +680,16 @@ describe("Monitor Controller - deleteMonitor", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_DELETE, + msg: successMessages.MONITOR_DELETE(req.language), }) ).to.be.true; }); }); -describe("Monitor Controller - deleteAllMonitors", function() { +describe("Monitor Controller - deleteAllMonitors", function () { let req, res, next, stub; - beforeEach(function() { + beforeEach(function () { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -694,6 +702,7 @@ describe("Monitor Controller - deleteAllMonitors", function() { }, query: {}, body: {}, + language: 'en', db: { deleteAllMonitors: sinon.stub(), deleteChecks: sinon.stub(), @@ -715,12 +724,12 @@ describe("Monitor Controller - deleteAllMonitors", function() { sinon.stub(logger, "error"); }); - afterEach(function() { + afterEach(function () { sinon.restore(); stub.restore(); }); - it("should reject with an error if getTokenFromHeaders throws an error", async function() { + it("should reject with an error if getTokenFromHeaders throws an error", async function () { req.headers = {}; await deleteAllMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); @@ -728,14 +737,14 @@ describe("Monitor Controller - deleteAllMonitors", function() { expect(next.firstCall.args[0].status).to.equal(500); }); - it("should reject with an error if token validation fails", async function() { + it("should reject with an error if token validation fails", async function () { stub.restore(); req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); await deleteAllMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); }); - it("should reject with an error if DB deleteAllMonitors operation fail", async function() { + it("should reject with an error if DB deleteAllMonitors operation fail", async function () { req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.deleteAllMonitors.throws(new Error("DB error")); await deleteAllMonitors(req, res, next); @@ -743,7 +752,7 @@ describe("Monitor Controller - deleteAllMonitors", function() { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should log an error if deleteChecks throws an error", async function() { + it("should log an error if deleteChecks throws an error", async function () { const monitors = [{ name: "test_monitor", _id: "123" }]; req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); @@ -756,7 +765,7 @@ describe("Monitor Controller - deleteAllMonitors", function() { ); }); - it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function() { + it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function () { const monitors = [{ name: "test_monitor", _id: "123" }]; req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); @@ -769,7 +778,7 @@ describe("Monitor Controller - deleteAllMonitors", function() { ); }); - it("should log an error if deleteNotificationsByMonitorId throws an error", async function() { + it("should log an error if deleteNotificationsByMonitorId throws an error", async function () { const monitors = [{ name: "test_monitor", _id: "123" }]; req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); @@ -782,27 +791,25 @@ describe("Monitor Controller - deleteAllMonitors", function() { ); }); - it("should return success message if all operations succeed", async function() { + it("should return success message if all operations succeed", async function () { req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - req.db.deleteAllMonitors.returns({ - monitors: [{ name: "test_monitor", _id: "123" }], - deletedCount: 1, - }); + const { monitors, deletedCount } = { monitors: [{ name: "test_monitor", _id: "123" }], deletedCount: 1 }; + req.db.deleteAllMonitors.returns({ monitors, deletedCount }); await deleteAllMonitors(req, res, next); expect(res.status.firstCall.args[0]).to.equal(200); expect( res.json.calledOnceWith({ success: true, - msg: "Deleted 1 monitors", + msg: `Deleted ${deletedCount} monitors`, }) ).to.be.true; }); }); -describe("Monitor Controller - editMonitor", function() { +describe("Monitor Controller - editMonitor", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { headers: {}, params: { @@ -812,6 +819,7 @@ describe("Monitor Controller - editMonitor", function() { body: { notifications: [{ email: "example@example.com" }], }, + language: 'en', db: { getMonitorById: sinon.stub(), editMonitor: sinon.stub(), @@ -833,32 +841,32 @@ describe("Monitor Controller - editMonitor", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await editMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if body validation fails", async function() { + it("should reject with an error if body validation fails", async function () { req.body = { invalid: 1 }; await editMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getMonitorById operation fails", async function() { + it("should reject with an error if getMonitorById operation fails", async function () { req.db.getMonitorById.throws(new Error("DB error")); await editMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if editMonitor operation fails", async function() { + it("should reject with an error if editMonitor operation fails", async function () { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.throws(new Error("DB error")); await editMonitor(req, res, next); @@ -866,7 +874,7 @@ describe("Monitor Controller - editMonitor", function() { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if deleteNotificationsByMonitorId operation fails", async function() { + it("should reject with an error if deleteNotificationsByMonitorId operation fails", async function () { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns({ _id: "123" }); req.db.deleteNotificationsByMonitorId.throws(new Error("DB error")); @@ -875,7 +883,7 @@ describe("Monitor Controller - editMonitor", function() { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if createNotification operation fails", async function() { + it("should reject with an error if createNotification operation fails", async function () { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns({ _id: "123" }); req.db.createNotification.throws(new Error("DB error")); @@ -884,7 +892,7 @@ describe("Monitor Controller - editMonitor", function() { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if deleteJob operation fails", async function() { + it("should reject with an error if deleteJob operation fails", async function () { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns({ _id: "123" }); req.jobQueue.deleteJob.throws(new Error("Job error")); @@ -893,7 +901,7 @@ describe("Monitor Controller - editMonitor", function() { expect(next.firstCall.args[0].message).to.equal("Job error"); }); - it("should reject with an error if addJob operation fails", async function() { + it("should reject with an error if addJob operation fails", async function () { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns({ _id: "123" }); req.jobQueue.addJob.throws(new Error("Add Job error")); @@ -902,7 +910,7 @@ describe("Monitor Controller - editMonitor", function() { expect(next.firstCall.args[0].message).to.equal("Add Job error"); }); - it("should return success message with data if all operations succeed", async function() { + it("should return success message with data if all operations succeed", async function () { const monitor = { _id: "123" }; req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns(monitor); @@ -911,17 +919,17 @@ describe("Monitor Controller - editMonitor", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_EDIT, + msg: successMessages.MONITOR_EDIT(req.language), data: monitor, }) ).to.be.true; }); }); -describe("Monitor Controller - pauseMonitor", function() { +describe("Monitor Controller - pauseMonitor", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { headers: {}, params: { @@ -929,6 +937,7 @@ describe("Monitor Controller - pauseMonitor", function() { }, query: {}, body: {}, + language: 'en', db: { getMonitorById: sinon.stub(), }, @@ -947,25 +956,25 @@ describe("Monitor Controller - pauseMonitor", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should reject with an error if param validation fails", async function() { + it("should reject with an error if param validation fails", async function () { req.params = {}; await pauseMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getMonitorById operation fails", async function() { + it("should reject with an error if getMonitorById operation fails", async function () { req.db.getMonitorById.throws(new Error("DB error")); await pauseMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if deleteJob operation fails", async function() { + it("should reject with an error if deleteJob operation fails", async function () { const monitor = { _id: req.params.monitorId, isActive: true }; req.db.getMonitorById.returns(monitor); req.jobQueue.deleteJob.throws(new Error("Delete Job error")); @@ -974,7 +983,7 @@ describe("Monitor Controller - pauseMonitor", function() { expect(next.firstCall.args[0].message).to.equal("Delete Job error"); }); - it("should reject with an error if addJob operation fails", async function() { + it("should reject with an error if addJob operation fails", async function () { const monitor = { _id: req.params.monitorId, isActive: false }; req.db.getMonitorById.returns(monitor); req.jobQueue.addJob.throws(new Error("Add Job error")); @@ -983,7 +992,7 @@ describe("Monitor Controller - pauseMonitor", function() { expect(next.firstCall.args[0].message).to.equal("Add Job error"); }); - it("should reject with an error if monitor.save operation fails", async function() { + it("should reject with an error if monitor.save operation fails", async function () { const monitor = { _id: req.params.monitorId, active: false, @@ -995,7 +1004,7 @@ describe("Monitor Controller - pauseMonitor", function() { expect(next.firstCall.args[0].message).to.equal("Save error"); }); - it("should return success pause message with data if all operations succeed with inactive monitor", async function() { + it("should return success pause message with data if all operations succeed with inactive monitor", async function () { const monitor = { _id: req.params.monitorId, isActive: false, @@ -1007,13 +1016,13 @@ describe("Monitor Controller - pauseMonitor", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_PAUSE, + msg: successMessages.MONITOR_PAUSE(req.language), data: monitor, }) ).to.be.true; }); - it("should return success resume message with data if all operations succeed with active monitor", async function() { + it("should return success resume message with data if all operations succeed with active monitor", async function () { const monitor = { _id: req.params.monitorId, isActive: true, @@ -1025,17 +1034,17 @@ describe("Monitor Controller - pauseMonitor", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_RESUME, + msg: successMessages.MONITOR_RESUME(req.language), data: monitor, }) ).to.be.true; }); }); -describe("Monitor Controller - addDemoMonitors", function() { +describe("Monitor Controller - addDemoMonitors", function () { let req, res, next, stub; - beforeEach(function() { + beforeEach(function () { stub = sinon.stub(jwt, "verify").callsFake(() => { return { _id: "123", teamId: "123" }; }); @@ -1046,6 +1055,7 @@ describe("Monitor Controller - addDemoMonitors", function() { params: {}, query: {}, body: {}, + language: 'en', db: { addDemoMonitors: sinon.stub(), }, @@ -1063,12 +1073,12 @@ describe("Monitor Controller - addDemoMonitors", function() { next = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); stub.restore(); }); - it("should reject with an error if getTokenFromHeaders fails", async function() { + it("should reject with an error if getTokenFromHeaders fails", async function () { req.headers = {}; await addDemoMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); @@ -1076,21 +1086,21 @@ describe("Monitor Controller - addDemoMonitors", function() { expect(next.firstCall.args[0].status).to.equal(500); }); - it("should reject with an error if getting settings fails", async function() { + it("should reject with an error if getting settings fails", async function () { req.settingsService.getSettings.throws(new Error("Settings error")); await addDemoMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("Settings error"); }); - it("should reject with an error if JWT validation fails", async function() { + it("should reject with an error if JWT validation fails", async function () { stub.restore(); req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); await addDemoMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); }); - it("should reject with an error if addDemoMonitors operation fails", async function() { + it("should reject with an error if addDemoMonitors operation fails", async function () { req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.addDemoMonitors.throws(new Error("DB error")); await addDemoMonitors(req, res, next); @@ -1098,7 +1108,7 @@ describe("Monitor Controller - addDemoMonitors", function() { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if addJob operation fails", async function() { + it("should reject with an error if addJob operation fails", async function () { req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.addDemoMonitors.returns([{ _id: "123" }]); req.jobQueue.addJob.throws(new Error("Add Job error")); @@ -1107,7 +1117,7 @@ describe("Monitor Controller - addDemoMonitors", function() { expect(next.firstCall.args[0].message).to.equal("Add Job error"); }); - it("should return success message with data if all operations succeed", async function() { + it("should return success message with data if all operations succeed", async function () { const monitors = [{ _id: "123" }]; req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.addDemoMonitors.returns(monitors); @@ -1116,7 +1126,7 @@ describe("Monitor Controller - addDemoMonitors", function() { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_DEMO_ADDED, + msg: successMessages.MONITOR_DEMO_ADDED(req.language), data: monitors.length, }) ).to.be.true; diff --git a/Server/tests/db/inviteModule.test.js b/Server/tests/db/inviteModule.test.js index 164840826..febaf6ab7 100644 --- a/Server/tests/db/inviteModule.test.js +++ b/Server/tests/db/inviteModule.test.js @@ -7,32 +7,33 @@ import { } from "../../db/mongo/modules/inviteModule.js"; import { errorMessages } from "../../utils/messages.js"; -describe("Invite Module", function() { +describe("Invite Module", function () { const mockUserData = { email: "test@test.com", teamId: "123", role: ["admin"], token: "123", }; + const mockLanguage = 'en'; const mockInviteToken = { _id: 123, time: 123 }; let inviteTokenDeleteManyStub, inviteTokenSaveStub, inviteTokenFindOneStub, inviteTokenFindOneAndDeleteStub; - beforeEach(function() { + beforeEach(function () { inviteTokenDeleteManyStub = sinon.stub(InviteToken, "deleteMany"); inviteTokenSaveStub = sinon.stub(InviteToken.prototype, "save"); inviteTokenFindOneStub = sinon.stub(InviteToken, "findOne"); inviteTokenFindOneAndDeleteStub = sinon.stub(InviteToken, "findOneAndDelete"); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - describe("requestInviteToken", function() { - it("should return a new invite token", async function() { + describe("requestInviteToken", function () { + it("should return a new invite token", async function () { inviteTokenDeleteManyStub.resolves(); inviteTokenSaveStub.resolves(); const inviteToken = await requestInviteToken(mockUserData); @@ -41,7 +42,7 @@ describe("Invite Module", function() { expect(inviteToken.token).to.exist; }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("test error"); inviteTokenDeleteManyStub.rejects(err); try { @@ -52,23 +53,23 @@ describe("Invite Module", function() { }); }); - describe("getInviteToken", function() { - it("should return an invite token", async function() { + describe("getInviteToken", function () { + it("should return an invite token", async function () { inviteTokenFindOneStub.resolves(mockInviteToken); const inviteToken = await getInviteToken(mockUserData.token); expect(inviteToken).to.deep.equal(mockInviteToken); }); - it("should handle a token not found", async function() { + it("should handle a token not found", async function () { inviteTokenFindOneStub.resolves(null); try { await getInviteToken(mockUserData.token); } catch (error) { - expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND); + expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND(mockLanguage)); } }); - it("should handle DB errors", async function() { + it("should handle DB errors", async function () { const err = new Error("test error"); inviteTokenFindOneStub.rejects(err); try { @@ -80,23 +81,23 @@ describe("Invite Module", function() { }); }); - describe("getInviteTokenAndDelete", function() { - it("should return a deleted invite", async function() { + describe("getInviteTokenAndDelete", function () { + it("should return a deleted invite", async function () { inviteTokenFindOneAndDeleteStub.resolves(mockInviteToken); const deletedInvite = await getInviteTokenAndDelete(mockUserData.token); expect(deletedInvite).to.deep.equal(mockInviteToken); }); - it("should handle a token not found", async function() { + it("should handle a token not found", async function () { inviteTokenFindOneAndDeleteStub.resolves(null); try { await getInviteTokenAndDelete(mockUserData.token); } catch (error) { - expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND); + expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND(mockLanguage)); } }); - it("should handle DB errors", async function() { + it("should handle DB errors", async function () { const err = new Error("test error"); inviteTokenFindOneAndDeleteStub.rejects(err); try { diff --git a/Server/tests/db/monitorModule.test.js b/Server/tests/db/monitorModule.test.js index 50782494a..e60688f3f 100644 --- a/Server/tests/db/monitorModule.test.js +++ b/Server/tests/db/monitorModule.test.js @@ -31,7 +31,7 @@ import { calculateGroupStats, } from "../../db/mongo/modules/monitorModule.js"; -describe("monitorModule", function() { +describe("monitorModule", function () { let monitorFindStub, monitorFindByIdStub, monitorFindByIdAndUpdateStub, @@ -43,7 +43,7 @@ describe("monitorModule", function() { pageSpeedCheckFindStub, hardwareCheckFindStub; - beforeEach(function() { + beforeEach(function () { monitorFindStub = sinon.stub(Monitor, "find"); monitorFindByIdStub = sinon.stub(Monitor, "findById"); monitorFindByIdAndUpdateStub = sinon.stub(Monitor, "findByIdAndUpdate"); @@ -63,12 +63,12 @@ describe("monitorModule", function() { }); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - describe("getAllMonitors", function() { - it("should return all monitors", async function() { + describe("getAllMonitors", function () { + it("should return all monitors", async function () { const mockMonitors = [ { _id: "1", name: "Monitor 1", url: "test1.com" }, { _id: "2", name: "Monitor 2", url: "test2.com" }, @@ -81,13 +81,13 @@ describe("monitorModule", function() { expect(monitorFindStub.firstCall.args).to.deep.equal([]); }); - it("should handle empty results", async function() { + it("should handle empty results", async function () { monitorFindStub.returns([]); const result = await getAllMonitors(); expect(result).to.be.an("array").that.is.empty; }); - it("should throw error when database fails", async function() { + it("should throw error when database fails", async function () { // Arrange const error = new Error("Database error"); error.service = "MonitorModule"; @@ -105,8 +105,8 @@ describe("monitorModule", function() { }); }); - describe("getAllMonitorsWithUptimeStats", function() { - it("should return monitors with uptime stats for different time periods", async function() { + describe("getAllMonitorsWithUptimeStats", function () { + it("should return monitors with uptime stats for different time periods", async function () { // Mock data const mockMonitors = [ { @@ -152,7 +152,7 @@ describe("monitorModule", function() { expect(monitor["90"]).to.equal(75); }); - it("should return monitors with stats for pagespeed type", async function() { + it("should return monitors with stats for pagespeed type", async function () { // Mock data const mockMonitors = [ { @@ -198,7 +198,7 @@ describe("monitorModule", function() { expect(monitor["90"]).to.equal(75); }); - it("should return monitors with stats for hardware type", async function() { + it("should return monitors with stats for hardware type", async function () { // Mock data const mockMonitors = [ { @@ -244,7 +244,7 @@ describe("monitorModule", function() { expect(monitor["90"]).to.equal(75); }); - it("should handle errors appropriately", async function() { + it("should handle errors appropriately", async function () { // Setup stub to throw error monitorFindStub.rejects(new Error("Database error")); @@ -258,7 +258,7 @@ describe("monitorModule", function() { } }); - it("should handle empty monitor list", async function() { + it("should handle empty monitor list", async function () { monitorFindStub.resolves([]); const result = await getAllMonitorsWithUptimeStats(); @@ -267,7 +267,7 @@ describe("monitorModule", function() { expect(result).to.have.lengthOf(0); }); - it("should handle monitor with no checks", async function() { + it("should handle monitor with no checks", async function () { const mockMonitors = [ { _id: "monitor1", @@ -292,28 +292,28 @@ describe("monitorModule", function() { }); }); - describe("calculateUptimeDuration", function() { + describe("calculateUptimeDuration", function () { let clock; const NOW = new Date("2024-01-01T12:00:00Z").getTime(); - beforeEach(function() { + beforeEach(function () { // Fix the current time clock = sinon.useFakeTimers(NOW); }); - afterEach(function() { + afterEach(function () { clock.restore(); }); - it("should return 0 when checks array is empty", function() { + it("should return 0 when checks array is empty", function () { expect(calculateUptimeDuration([])).to.equal(0); }); - it("should return 0 when checks array is null", function() { + it("should return 0 when checks array is null", function () { expect(calculateUptimeDuration(null)).to.equal(0); }); - it("should calculate uptime from last down check to most recent check", function() { + it("should calculate uptime from last down check to most recent check", function () { const checks = [ { status: true, createdAt: "2024-01-01T11:00:00Z" }, // Most recent { status: true, createdAt: "2024-01-01T10:00:00Z" }, @@ -325,7 +325,7 @@ describe("monitorModule", function() { expect(calculateUptimeDuration(checks)).to.equal(7200000); }); - it("should calculate uptime from first check when no down checks exist", function() { + it("should calculate uptime from first check when no down checks exist", function () { const checks = [ { status: true, createdAt: "2024-01-01T11:00:00Z" }, { status: true, createdAt: "2024-01-01T10:00:00Z" }, @@ -337,28 +337,28 @@ describe("monitorModule", function() { }); }); - describe("getLastChecked", function() { + describe("getLastChecked", function () { let clock; const NOW = new Date("2024-01-01T12:00:00Z").getTime(); - beforeEach(function() { + beforeEach(function () { // Fix the current time clock = sinon.useFakeTimers(NOW); }); - afterEach(function() { + afterEach(function () { clock.restore(); }); - it("should return 0 when checks array is empty", function() { + it("should return 0 when checks array is empty", function () { expect(getLastChecked([])).to.equal(0); }); - it("should return 0 when checks array is null", function() { + it("should return 0 when checks array is null", function () { expect(getLastChecked(null)).to.equal(0); }); - it("should return time difference between now and most recent check", function() { + it("should return time difference between now and most recent check", function () { const checks = [ { createdAt: "2024-01-01T11:30:00Z" }, // 30 minutes ago { createdAt: "2024-01-01T11:00:00Z" }, @@ -369,7 +369,7 @@ describe("monitorModule", function() { expect(getLastChecked(checks)).to.equal(1800000); }); - it("should handle checks from different days", function() { + it("should handle checks from different days", function () { const checks = [ { createdAt: "2023-12-31T12:00:00Z" }, // 24 hours ago { createdAt: "2023-12-30T12:00:00Z" }, @@ -380,16 +380,16 @@ describe("monitorModule", function() { }); }); - describe("getLatestResponseTime", function() { - it("should return 0 when checks array is empty", function() { + describe("getLatestResponseTime", function () { + it("should return 0 when checks array is empty", function () { expect(getLatestResponseTime([])).to.equal(0); }); - it("should return 0 when checks array is null", function() { + it("should return 0 when checks array is null", function () { expect(getLatestResponseTime(null)).to.equal(0); }); - it("should return response time from most recent check", function() { + it("should return response time from most recent check", function () { const checks = [ { responseTime: 150, createdAt: "2024-01-01T11:30:00Z" }, // Most recent { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }, @@ -399,7 +399,7 @@ describe("monitorModule", function() { expect(getLatestResponseTime(checks)).to.equal(150); }); - it("should handle missing responseTime in checks", function() { + it("should handle missing responseTime in checks", function () { const checks = [ { createdAt: "2024-01-01T11:30:00Z" }, { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }, @@ -409,16 +409,16 @@ describe("monitorModule", function() { }); }); - describe("getAverageResponseTime", function() { - it("should return 0 when checks array is empty", function() { + describe("getAverageResponseTime", function () { + it("should return 0 when checks array is empty", function () { expect(getAverageResponseTime([])).to.equal(0); }); - it("should return 0 when checks array is null", function() { + it("should return 0 when checks array is null", function () { expect(getAverageResponseTime(null)).to.equal(0); }); - it("should calculate average response time from all checks", function() { + it("should calculate average response time from all checks", function () { const checks = [ { responseTime: 100, createdAt: "2024-01-01T11:30:00Z" }, { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }, @@ -429,7 +429,7 @@ describe("monitorModule", function() { expect(getAverageResponseTime(checks)).to.equal(200); }); - it("should handle missing responseTime in some checks", function() { + it("should handle missing responseTime in some checks", function () { const checks = [ { responseTime: 100, createdAt: "2024-01-01T11:30:00Z" }, { createdAt: "2024-01-01T11:00:00Z" }, @@ -440,7 +440,7 @@ describe("monitorModule", function() { expect(getAverageResponseTime(checks)).to.equal(200); }); - it("should return 0 when no checks have responseTime", function() { + it("should return 0 when no checks have responseTime", function () { const checks = [ { createdAt: "2024-01-01T11:30:00Z" }, { createdAt: "2024-01-01T11:00:00Z" }, @@ -450,26 +450,26 @@ describe("monitorModule", function() { }); }); - describe("getUptimePercentage", function() { - it("should return 0 when checks array is empty", function() { + describe("getUptimePercentage", function () { + it("should return 0 when checks array is empty", function () { expect(getUptimePercentage([])).to.equal(0); }); - it("should return 0 when checks array is null", function() { + it("should return 0 when checks array is null", function () { expect(getUptimePercentage(null)).to.equal(0); }); - it("should return 100 when all checks are up", function() { + it("should return 100 when all checks are up", function () { const checks = [{ status: true }, { status: true }, { status: true }]; expect(getUptimePercentage(checks)).to.equal(100); }); - it("should return 0 when all checks are down", function() { + it("should return 0 when all checks are down", function () { const checks = [{ status: false }, { status: false }, { status: false }]; expect(getUptimePercentage(checks)).to.equal(0); }); - it("should calculate correct percentage for mixed status checks", function() { + it("should calculate correct percentage for mixed status checks", function () { const checks = [ { status: true }, { status: false }, @@ -480,33 +480,33 @@ describe("monitorModule", function() { expect(getUptimePercentage(checks)).to.equal(75); }); - it("should handle undefined status values", function() { + it("should handle undefined status values", function () { const checks = [{ status: true }, { status: undefined }, { status: true }]; // 2 up out of 3 total ≈ 66.67% expect(getUptimePercentage(checks)).to.equal((2 / 3) * 100); }); }); - describe("getIncidents", function() { - it("should return 0 when checks array is empty", function() { + describe("getIncidents", function () { + it("should return 0 when checks array is empty", function () { expect(getIncidents([])).to.equal(0); }); - it("should return 0 when checks array is null", function() { + it("should return 0 when checks array is null", function () { expect(getIncidents(null)).to.equal(0); }); - it("should return 0 when all checks are up", function() { + it("should return 0 when all checks are up", function () { const checks = [{ status: true }, { status: true }, { status: true }]; expect(getIncidents(checks)).to.equal(0); }); - it("should count all incidents when all checks are down", function() { + it("should count all incidents when all checks are down", function () { const checks = [{ status: false }, { status: false }, { status: false }]; expect(getIncidents(checks)).to.equal(3); }); - it("should count correct number of incidents for mixed status checks", function() { + it("should count correct number of incidents for mixed status checks", function () { const checks = [ { status: true }, { status: false }, @@ -517,7 +517,7 @@ describe("monitorModule", function() { expect(getIncidents(checks)).to.equal(2); }); - it("should handle undefined status values", function() { + it("should handle undefined status values", function () { const checks = [ { status: true }, { status: undefined }, @@ -529,10 +529,10 @@ describe("monitorModule", function() { }); }); - describe("getMonitorChecks", function() { + describe("getMonitorChecks", function () { let mockModel; - beforeEach(function() { + beforeEach(function () { // Create a mock model with chainable methods const mockChecks = [ { monitorId: "123", createdAt: new Date("2024-01-01") }, @@ -546,11 +546,11 @@ describe("monitorModule", function() { }; }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should return all checks and date-ranged checks", async function() { + it("should return all checks and date-ranged checks", async function () { // Arrange const monitorId = "123"; const dateRange = { @@ -579,7 +579,7 @@ describe("monitorModule", function() { }); }); - it("should handle empty results", async function() { + it("should handle empty results", async function () { // Arrange const emptyModel = { find: sinon.stub().returns({ @@ -603,7 +603,7 @@ describe("monitorModule", function() { expect(result.checksForDateRange).to.be.an("array").that.is.empty; }); - it("should maintain sort order", async function() { + it("should maintain sort order", async function () { // Arrange const sortedChecks = [ { monitorId: "123", createdAt: new Date("2024-01-02") }, @@ -637,39 +637,39 @@ describe("monitorModule", function() { }); }); - describe("processChecksForDisplay", function() { + describe("processChecksForDisplay", function () { let normalizeStub; - beforeEach(function() { + beforeEach(function () { normalizeStub = sinon.stub(); }); - it("should return original checks when numToDisplay is not provided", function() { + it("should return original checks when numToDisplay is not provided", function () { const checks = [1, 2, 3, 4, 5]; const result = processChecksForDisplay(normalizeStub, checks); expect(result).to.deep.equal(checks); }); - it("should return original checks when numToDisplay is greater than checks length", function() { + it("should return original checks when numToDisplay is greater than checks length", function () { const checks = [1, 2, 3]; const result = processChecksForDisplay(normalizeStub, checks, 5); expect(result).to.deep.equal(checks); }); - it("should filter checks based on numToDisplay", function() { + it("should filter checks based on numToDisplay", function () { const checks = [1, 2, 3, 4, 5, 6]; const result = processChecksForDisplay(normalizeStub, checks, 3); // Should return [1, 3, 5] as n = ceil(6/3) = 2 expect(result).to.deep.equal([1, 3, 5]); }); - it("should handle empty checks array", function() { + it("should handle empty checks array", function () { const checks = []; const result = processChecksForDisplay(normalizeStub, checks, 3); expect(result).to.be.an("array").that.is.empty; }); - it("should call normalizeData when normalize is true", function() { + it("should call normalizeData when normalize is true", function () { const checks = [1, 2, 3]; normalizeStub.returns([10, 20, 30]); @@ -679,7 +679,7 @@ describe("monitorModule", function() { expect(result).to.deep.equal([10, 20, 30]); }); - it("should handle both filtering and normalization", function() { + it("should handle both filtering and normalization", function () { const checks = [1, 2, 3, 4, 5, 6]; normalizeStub.returns([10, 30, 50]); @@ -690,7 +690,7 @@ describe("monitorModule", function() { }); }); - describe("groupChecksByTime", function() { + describe("groupChecksByTime", function () { const mockChecks = [ { createdAt: "2024-01-15T10:30:45Z" }, { createdAt: "2024-01-15T10:45:15Z" }, @@ -698,7 +698,7 @@ describe("monitorModule", function() { { createdAt: "2024-01-16T10:30:00Z" }, ]; - it("should group checks by hour when dateRange is 'day'", function() { + it("should group checks by hour when dateRange is 'day'", function () { const result = groupChecksByTime(mockChecks, "day"); // Get timestamps for 10:00 and 11:00 on Jan 15 @@ -713,7 +713,7 @@ describe("monitorModule", function() { expect(result[time3].checks).to.have.lengthOf(1); }); - it("should group checks by day when dateRange is not 'day'", function() { + it("should group checks by day when dateRange is not 'day'", function () { const result = groupChecksByTime(mockChecks, "week"); expect(Object.keys(result)).to.have.lengthOf(2); @@ -721,12 +721,12 @@ describe("monitorModule", function() { expect(result["2024-01-16"].checks).to.have.lengthOf(1); }); - it("should handle empty checks array", function() { + it("should handle empty checks array", function () { const result = groupChecksByTime([], "day"); expect(result).to.deep.equal({}); }); - it("should handle single check", function() { + it("should handle single check", function () { const singleCheck = [{ createdAt: "2024-01-15T10:30:45Z" }]; const result = groupChecksByTime(singleCheck, "day"); @@ -735,7 +735,7 @@ describe("monitorModule", function() { expect(result[expectedTime].checks).to.have.lengthOf(1); }); - it("should skip invalid dates and process valid ones", function() { + it("should skip invalid dates and process valid ones", function () { const checksWithInvalidDate = [ { createdAt: "invalid-date" }, { createdAt: "2024-01-15T10:30:45Z" }, @@ -752,7 +752,7 @@ describe("monitorModule", function() { expect(result[expectedTime].checks[0].createdAt).to.equal("2024-01-15T10:30:45Z"); }); - it("should handle checks in same time group", function() { + it("should handle checks in same time group", function () { const checksInSameHour = [ { createdAt: "2024-01-15T10:15:00Z" }, { createdAt: "2024-01-15T10:45:00Z" }, @@ -766,16 +766,16 @@ describe("monitorModule", function() { }); }); - describe("calculateGroupStats", function() { + describe("calculateGroupStats", function () { // Mock getUptimePercentage function let uptimePercentageStub; - beforeEach(function() { + beforeEach(function () { uptimePercentageStub = sinon.stub(); uptimePercentageStub.returns(95); // Default return value }); - it("should calculate stats correctly for a group of checks", function() { + it("should calculate stats correctly for a group of checks", function () { const mockGroup = { time: "2024-01-15", checks: [ @@ -796,7 +796,7 @@ describe("monitorModule", function() { }); }); - it("should handle empty checks array", function() { + it("should handle empty checks array", function () { const mockGroup = { time: "2024-01-15", checks: [], @@ -813,7 +813,7 @@ describe("monitorModule", function() { }); }); - it("should handle missing responseTime values", function() { + it("should handle missing responseTime values", function () { const mockGroup = { time: "2024-01-15", checks: [ @@ -834,7 +834,7 @@ describe("monitorModule", function() { }); }); - it("should handle all checks with status false", function() { + it("should handle all checks with status false", function () { const mockGroup = { time: "2024-01-15", checks: [ @@ -855,7 +855,7 @@ describe("monitorModule", function() { }); }); - it("should handle all checks with status true", function() { + it("should handle all checks with status true", function () { const mockGroup = { time: "2024-01-15", checks: [ @@ -877,7 +877,7 @@ describe("monitorModule", function() { }); }); - describe("getMonitorStatsById", function() { + describe("getMonitorStatsById", function () { const now = new Date(); const oneHourAgo = new Date(now - 3600000); const twoHoursAgo = new Date(now - 7200000); @@ -974,18 +974,18 @@ describe("monitorModule", function() { }, }; - beforeEach(function() { + beforeEach(function () { checkFindStub.returns({ sort: () => checkDocs, }); monitorFindByIdStub.returns(mockMonitor); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should return monitor stats with calculated values, sort order desc", async function() { + it("should return monitor stats with calculated values, sort order desc", async function () { req.query.sortOrder = "desc"; const result = await getMonitorStatsById(req); expect(result).to.include.keys([ @@ -1009,7 +1009,7 @@ describe("monitorModule", function() { expect(result.aggregateData).to.be.an("array"); }); - it("should return monitor stats with calculated values, ping type", async function() { + it("should return monitor stats with calculated values, ping type", async function () { monitorFindByIdStub.returns(mockMonitorPing); req.query.sortOrder = "desc"; const result = await getMonitorStatsById(req); @@ -1034,7 +1034,7 @@ describe("monitorModule", function() { expect(result.aggregateData).to.be.an("array"); }); - it("should return monitor stats with calculated values, docker type", async function() { + it("should return monitor stats with calculated values, docker type", async function () { monitorFindByIdStub.returns(mockMonitorDocker); req.query.sortOrder = "desc"; const result = await getMonitorStatsById(req); @@ -1059,7 +1059,7 @@ describe("monitorModule", function() { expect(result.aggregateData).to.be.an("array"); }); - it("should return monitor stats with calculated values", async function() { + it("should return monitor stats with calculated values", async function () { req.query.sortOrder = "asc"; const result = await getMonitorStatsById(req); expect(result).to.include.keys([ @@ -1083,7 +1083,7 @@ describe("monitorModule", function() { expect(result.aggregateData).to.be.an("array"); }); - it("should throw error when monitor is not found", async function() { + it("should throw error when monitor is not found", async function () { monitorFindByIdStub.returns(Promise.resolve(null)); const req = { @@ -1101,21 +1101,21 @@ describe("monitorModule", function() { }); }); - describe("getMonitorById", function() { + describe("getMonitorById", function () { let notificationFindStub; let monitorSaveStub; - beforeEach(function() { + beforeEach(function () { // Create stubs notificationFindStub = sinon.stub(Notification, "find"); monitorSaveStub = sinon.stub().resolves(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should return monitor with notifications when found", async function() { + it("should return monitor with notifications when found", async function () { // Arrange const monitorId = "123"; const mockMonitor = { @@ -1139,9 +1139,10 @@ describe("monitorModule", function() { expect(monitorSaveStub.calledOnce).to.be.true; }); - it("should throw 404 error when monitor not found", async function() { + it("should throw 404 error when monitor not found", async function () { // Arrange const monitorId = "nonexistent"; + const mockLanguage = 'en'; monitorFindByIdStub.resolves(null); // Act & Assert @@ -1149,14 +1150,14 @@ describe("monitorModule", function() { await getMonitorById(monitorId); expect.fail("Should have thrown an error"); } catch (error) { - expect(error.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + expect(error.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, mockLanguage)); expect(error.status).to.equal(404); expect(error.service).to.equal("monitorModule"); expect(error.method).to.equal("getMonitorById"); } }); - it("should handle database errors properly", async function() { + it("should handle database errors properly", async function () { // Arrange const monitorId = "123"; const dbError = new Error("Database connection failed"); @@ -1173,7 +1174,7 @@ describe("monitorModule", function() { } }); - it("should handle notification fetch errors", async function() { + it("should handle notification fetch errors", async function () { // Arrange const monitorId = "123"; const mockMonitor = { @@ -1197,7 +1198,7 @@ describe("monitorModule", function() { } }); - it("should handle monitor save errors", async function() { + it("should handle monitor save errors", async function () { // Arrange const monitorId = "123"; const mockMonitor = { @@ -1222,8 +1223,8 @@ describe("monitorModule", function() { }); }); - describe("getMonitorsAndSummaryByTeamId", function() { - it("should return monitors and correct summary counts", async function() { + describe("getMonitorsAndSummaryByTeamId", function () { + it("should return monitors and correct summary counts", async function () { // Arrange const teamId = "team123"; const type = "http"; @@ -1249,7 +1250,7 @@ describe("monitorModule", function() { expect(monitorFindStub.calledOnceWith({ teamId, type })).to.be.true; }); - it("should return empty results for non-existent team", async function() { + it("should return empty results for non-existent team", async function () { // Arrange monitorFindStub.resolves([]); @@ -1266,7 +1267,7 @@ describe("monitorModule", function() { }); }); - it("should handle database errors", async function() { + it("should handle database errors", async function () { // Arrange const error = new Error("Database error"); error.service = "MonitorModule"; @@ -1285,8 +1286,8 @@ describe("monitorModule", function() { }); }); - describe("getMonitorsByTeamId", function() { - beforeEach(function() { + describe("getMonitorsByTeamId", function () { + beforeEach(function () { // Chain stubs for Monitor.find().skip().limit().sort() // Stub for CHECK_MODEL_LOOKUP model find @@ -1297,11 +1298,11 @@ describe("monitorModule", function() { }); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should return monitors with basic query parameters", async function() { + it("should return monitors with basic query parameters", async function () { const mockMonitors = [ { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) }, { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) }, @@ -1334,7 +1335,7 @@ describe("monitorModule", function() { expect(result).to.have.property("monitorCount", 2); }); - it("should return monitors with basic query parameters", async function() { + it("should return monitors with basic query parameters", async function () { const mockMonitors = [ { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) }, { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) }, @@ -1367,7 +1368,7 @@ describe("monitorModule", function() { expect(result).to.have.property("monitorCount", 2); }); - it("should handle type filter with array input", async function() { + it("should handle type filter with array input", async function () { const req = { params: { teamId: "team123" }, query: { @@ -1392,7 +1393,7 @@ describe("monitorModule", function() { }); }); - it("should handle text search filter", async function() { + it("should handle text search filter", async function () { const req = { params: { teamId: "team123" }, query: { @@ -1420,7 +1421,7 @@ describe("monitorModule", function() { }); }); - it("should handle pagination parameters", async function() { + it("should handle pagination parameters", async function () { const req = { params: { teamId: "team123" }, query: { @@ -1445,7 +1446,7 @@ describe("monitorModule", function() { }); }); - it("should handle sorting parameters", async function() { + it("should handle sorting parameters", async function () { const req = { params: { teamId: "team123" }, query: { @@ -1472,7 +1473,7 @@ describe("monitorModule", function() { }); }); - it("should return early when limit is -1", async function() { + it("should return early when limit is -1", async function () { // Arrange const req = { params: { teamId: "team123" }, @@ -1506,7 +1507,7 @@ describe("monitorModule", function() { }); }); - it("should normalize checks when normalize parameter is provided", async function() { + it("should normalize checks when normalize parameter is provided", async function () { const req = { params: { teamId: "team123" }, query: { normalize: "true" }, @@ -1531,7 +1532,7 @@ describe("monitorModule", function() { expect(result.monitors).to.have.lengthOf(2); }); - it("should handle database errors", async function() { + it("should handle database errors", async function () { const req = { params: { teamId: "team123" }, query: {}, @@ -1557,8 +1558,8 @@ describe("monitorModule", function() { }); }); - describe("createMonitor", function() { - it("should create a monitor without notifications", async function() { + describe("createMonitor", function () { + it("should create a monitor without notifications", async function () { let monitorSaveStub = sinon.stub(Monitor.prototype, "save").resolves(); const req = { @@ -1583,7 +1584,7 @@ describe("monitorModule", function() { expect(result.url).to.equal(expectedMonitor.url); }); - it("should handle database errors", async function() { + it("should handle database errors", async function () { const req = { body: { name: "Test Monitor", @@ -1600,8 +1601,8 @@ describe("monitorModule", function() { }); }); - describe("deleteMonitor", function() { - it("should delete a monitor successfully", async function() { + describe("deleteMonitor", function () { + it("should delete a monitor successfully", async function () { const monitorId = "123456789"; const mockMonitor = { _id: monitorId, @@ -1621,10 +1622,11 @@ describe("monitorModule", function() { sinon.assert.calledWith(monitorFindByIdAndDeleteStub, monitorId); }); - it("should throw error when monitor not found", async function() { + it("should throw error when monitor not found", async function () { const monitorId = "nonexistent123"; const req = { params: { monitorId }, + language: 'en', }; monitorFindByIdAndDeleteStub.resolves(null); @@ -1633,13 +1635,13 @@ describe("monitorModule", function() { await deleteMonitor(req); expect.fail("Should have thrown an error"); } catch (err) { - expect(err.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + expect(err.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, req.language)); expect(err.service).to.equal("monitorModule"); expect(err.method).to.equal("deleteMonitor"); } }); - it("should handle database errors", async function() { + it("should handle database errors", async function () { const monitorId = "123456789"; const req = { params: { monitorId }, @@ -1659,8 +1661,8 @@ describe("monitorModule", function() { }); }); - describe("deleteAllMonitors", function() { - it("should delete all monitors for a team successfully", async function() { + describe("deleteAllMonitors", function () { + it("should delete all monitors for a team successfully", async function () { const teamId = "team123"; const mockMonitors = [ { _id: "1", name: "Monitor 1", teamId }, @@ -1680,7 +1682,7 @@ describe("monitorModule", function() { sinon.assert.calledWith(monitorDeleteManyStub, { teamId }); }); - it("should return empty array when no monitors found", async function() { + it("should return empty array when no monitors found", async function () { const teamId = "emptyTeam"; monitorFindStub.resolves([]); @@ -1696,7 +1698,7 @@ describe("monitorModule", function() { sinon.assert.calledWith(monitorDeleteManyStub, { teamId }); }); - it("should handle database errors", async function() { + it("should handle database errors", async function () { const teamId = "team123"; const dbError = new Error("Database connection error"); monitorFindStub.rejects(dbError); @@ -1710,7 +1712,7 @@ describe("monitorModule", function() { } }); - it("should handle deleteMany errors", async function() { + it("should handle deleteMany errors", async function () { const teamId = "team123"; monitorFindStub.resolves([{ _id: "1", name: "Monitor 1" }]); monitorDeleteManyStub.rejects(new Error("Delete operation failed")); @@ -1725,14 +1727,14 @@ describe("monitorModule", function() { }); }); - describe("deleteMonitorsByUserId", function() { - beforeEach(function() {}); + describe("deleteMonitorsByUserId", function () { + beforeEach(function () { }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should delete all monitors for a user successfully", async function() { + it("should delete all monitors for a user successfully", async function () { // Arrange const userId = "user123"; const mockResult = { @@ -1750,7 +1752,7 @@ describe("monitorModule", function() { sinon.assert.calledWith(monitorDeleteManyStub, { userId: userId }); }); - it("should return zero deletedCount when no monitors found", async function() { + it("should return zero deletedCount when no monitors found", async function () { // Arrange const userId = "nonexistentUser"; const mockResult = { @@ -1768,7 +1770,7 @@ describe("monitorModule", function() { sinon.assert.calledWith(monitorDeleteManyStub, { userId: userId }); }); - it("should handle database errors", async function() { + it("should handle database errors", async function () { // Arrange const userId = "user123"; const dbError = new Error("Database connection error"); @@ -1786,8 +1788,8 @@ describe("monitorModule", function() { }); }); - describe("editMonitor", function() { - it("should edit a monitor successfully", async function() { + describe("editMonitor", function () { + it("should edit a monitor successfully", async function () { // Arrange const candidateId = "monitor123"; const candidateMonitor = { @@ -1826,7 +1828,7 @@ describe("monitorModule", function() { ); }); - it("should return null when monitor not found", async function() { + it("should return null when monitor not found", async function () { // Arrange const candidateId = "nonexistent123"; const candidateMonitor = { @@ -1848,7 +1850,7 @@ describe("monitorModule", function() { ); }); - it("should remove notifications from update data", async function() { + it("should remove notifications from update data", async function () { // Arrange const candidateId = "monitor123"; const candidateMonitor = { @@ -1880,7 +1882,7 @@ describe("monitorModule", function() { ); }); - it("should handle database errors", async function() { + it("should handle database errors", async function () { // Arrange const candidateId = "monitor123"; const candidateMonitor = { @@ -1902,8 +1904,8 @@ describe("monitorModule", function() { }); }); - describe("addDemoMonitors", function() { - it("should add demo monitors successfully", async function() { + describe("addDemoMonitors", function () { + it("should add demo monitors successfully", async function () { // Arrange const userId = "user123"; const teamId = "team123"; @@ -1912,7 +1914,7 @@ describe("monitorModule", function() { expect(result).to.deep.equal([{ _id: "123" }]); }); - it("should handle database errors", async function() { + it("should handle database errors", async function () { const userId = "user123"; const teamId = "team123"; diff --git a/Server/tests/db/recoveryModule.test.js b/Server/tests/db/recoveryModule.test.js index 9d2978dd3..1c6d1d43c 100644 --- a/Server/tests/db/recoveryModule.test.js +++ b/Server/tests/db/recoveryModule.test.js @@ -43,7 +43,7 @@ const createQueryChain = (finalResult, comparePasswordResult = false) => ({ save: sinon.stub().resolves(), }); -describe("recoveryModule", function() { +describe("recoveryModule", function () { let deleteManyStub, saveStub, findOneStub, @@ -52,9 +52,10 @@ describe("recoveryModule", function() { userFindOneStub; let req, res; - beforeEach(function() { + beforeEach(function () { req = { body: { email: "test@test.com" }, + language: 'en', }; deleteManyStub = sinon.stub(RecoveryToken, "deleteMany"); saveStub = sinon.stub(RecoveryToken.prototype, "save"); @@ -64,19 +65,19 @@ describe("recoveryModule", function() { userFindOneStub = sinon.stub().resolves(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - describe("requestRecoveryToken", function() { - it("should return a recovery token", async function() { + describe("requestRecoveryToken", function () { + it("should return a recovery token", async function () { deleteManyStub.resolves(); saveStub.resolves(mockRecoveryToken); const result = await requestRecoveryToken(req, res); expect(result.email).to.equal(mockRecoveryToken.email); }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("Test error"); deleteManyStub.rejects(err); try { @@ -88,24 +89,24 @@ describe("recoveryModule", function() { }); }); - describe("validateRecoveryToken", function() { - it("should return a recovery token if found", async function() { + describe("validateRecoveryToken", function () { + it("should return a recovery token if found", async function () { findOneStub.resolves(mockRecoveryToken); const result = await validateRecoveryToken(req, res); expect(result).to.deep.equal(mockRecoveryToken); }); - it("should thrown an error if a token is not found", async function() { + it("should thrown an error if a token is not found", async function () { findOneStub.resolves(null); try { await validateRecoveryToken(req, res); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND); + expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND(req.language)); } }); - it("should handle DB errors", async function() { + it("should handle DB errors", async function () { const err = new Error("Test error"); findOneStub.rejects(err); try { @@ -117,40 +118,41 @@ describe("recoveryModule", function() { }); }); - describe("resetPassword", function() { - beforeEach(function() { + describe("resetPassword", function () { + beforeEach(function () { req.body = { password: "test", newPassword: "test1", }; + req.language = 'en'; }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should thrown an error if a recovery token is not found", async function() { + it("should thrown an error if a recovery token is not found", async function () { findOneStub.resolves(null); try { await resetPassword(req, res); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND); + expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND(req.language)); } }); - it("should throw an error if a user is not found", async function() { + it("should throw an error if a user is not found", async function () { findOneStub.resolves(mockRecoveryToken); userFindOneStub = sinon.stub(User, "findOne").resolves(null); try { await resetPassword(req, res); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); + expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND(req.language)); } }); - it("should throw an error if the passwords match", async function() { + it("should throw an error if the passwords match", async function () { findOneStub.resolves(mockRecoveryToken); saveStub.resolves(); userFindOneStub = sinon @@ -160,11 +162,11 @@ describe("recoveryModule", function() { await resetPassword(req, res); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_RESET_PASSWORD_BAD_MATCH); + expect(error.message).to.equal(errorMessages.DB_RESET_PASSWORD_BAD_MATCH(req.language)); } }); - it("should return a user without password if successful", async function() { + it("should return a user without password if successful", async function () { findOneStub.resolves(mockRecoveryToken); saveStub.resolves(); userFindOneStub = sinon diff --git a/Server/tests/db/statusPageModule.test.js b/Server/tests/db/statusPageModule.test.js index 3d5d5e9d0..0793dc6b9 100644 --- a/Server/tests/db/statusPageModule.test.js +++ b/Server/tests/db/statusPageModule.test.js @@ -6,31 +6,32 @@ import { import StatusPage from "../../db/models/StatusPage.js"; import { errorMessages } from "../../utils/messages.js"; -describe("statusPageModule", function() { - let statusPageFindOneStub, statusPageSaveStub, statusPageFindStub; +describe("statusPageModule", function () { + let statusPageFindOneStub, statusPageSaveStub, statusPageFindStub, mockLanguage; - beforeEach(function() { + beforeEach(function () { + mockLanguage = 'en'; statusPageSaveStub = sinon.stub(StatusPage.prototype, "save"); statusPageFindOneStub = sinon.stub(StatusPage, "findOne"); statusPageFindStub = sinon.stub(StatusPage, "find"); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - describe("createStatusPage", function() { - it("should throw an error if a non-unique url is provided", async function() { + describe("createStatusPage", function () { + it("should throw an error if a non-unique url is provided", async function () { statusPageFindOneStub.resolves(true); try { await createStatusPage({ url: "test" }); } catch (error) { expect(error.status).to.equal(400); - expect(error.message).to.equal(errorMessages.STATUS_PAGE_URL_NOT_UNIQUE); + expect(error.message).to.equal(errorMessages.STATUS_PAGE_URL_NOT_UNIQUE(mockLanguage)); } }); - it("should handle duplicate URL errors", async function() { + it("should handle duplicate URL errors", async function () { const err = new Error("test"); err.code = 11000; statusPageSaveStub.rejects(err); @@ -41,7 +42,7 @@ describe("statusPageModule", function() { } }); - it("should return a status page if a unique url is provided", async function() { + it("should return a status page if a unique url is provided", async function () { statusPageFindOneStub.resolves(null); statusPageFindStub.resolves([]); const mockStatusPage = { url: "test" }; @@ -51,21 +52,21 @@ describe("statusPageModule", function() { }); }); - describe("getStatusPageByUrl", function() { - it("should throw an error if a status page is not found", async function() { + describe("getStatusPageByUrl", function () { + it("should throw an error if a status page is not found", async function () { statusPageFindOneStub.resolves(null); try { await getStatusPageByUrl("test"); } catch (error) { expect(error.status).to.equal(404); - expect(error.message).to.equal(errorMessages.STATUS_PAGE_NOT_FOUND); + expect(error.message).to.equal(errorMessages.STATUS_PAGE_NOT_FOUND(mockLanguage)); } }); - it("should return a status page if a status page is found", async function() { + it("should return a status page if a status page is found", async function () { const mockStatusPage = { url: "test" }; statusPageFindOneStub.resolves(mockStatusPage); - const statusPage = await getStatusPageByUrl(mockStatusPage.url); + const statusPage = await getStatusPageByUrl(mockStatusPage.url, mockLanguage); expect(statusPage).to.exist; expect(statusPage).to.deep.equal(mockStatusPage); }); diff --git a/Server/tests/db/userModule.test.js b/Server/tests/db/userModule.test.js index b52ba1880..0bf1fd067 100644 --- a/Server/tests/db/userModule.test.js +++ b/Server/tests/db/userModule.test.js @@ -27,7 +27,9 @@ const imageFile = { image: 1, }; -describe("userModule", function() { +const mockLanguage = 'en'; + +describe("userModule", function () { let teamSaveStub, teamFindByIdAndDeleteStub, userSaveStub, @@ -40,7 +42,7 @@ describe("userModule", function() { generateAvatarImageStub, parseBooleanStub; - beforeEach(function() { + beforeEach(function () { teamSaveStub = sinon.stub(TeamModel.prototype, "save"); teamFindByIdAndDeleteStub = sinon.stub(TeamModel, "findByIdAndDelete"); userSaveStub = sinon.stub(UserModel.prototype, "save"); @@ -54,12 +56,12 @@ describe("userModule", function() { parseBooleanStub = sinon.stub().returns(true); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - describe("insertUser", function() { - it("should insert a regular user", async function() { + describe("insertUser", function () { + it("should insert a regular user", async function () { userSaveStub.resolves(mockUser); userFindOneStub.returns({ select: sinon.stub().returns({ @@ -70,7 +72,7 @@ describe("userModule", function() { expect(result).to.deep.equal(mockUser); }); - it("should insert a superadmin user", async function() { + it("should insert a superadmin user", async function () { userSaveStub.resolves(mockSuperUser); userFindOneStub.returns({ select: sinon.stub().returns({ @@ -81,7 +83,7 @@ describe("userModule", function() { expect(result).to.deep.equal(mockSuperUser); }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("test error"); userSaveStub.rejects(err); try { @@ -92,7 +94,7 @@ describe("userModule", function() { } }); - it("should handle a duplicate key error", async function() { + it("should handle a duplicate key error", async function () { const err = new Error("test error"); err.code = 11000; userSaveStub.rejects(err); @@ -105,8 +107,8 @@ describe("userModule", function() { }); }); - describe("getUserByEmail", function() { - it("should return a user", async function() { + describe("getUserByEmail", function () { + it("should return a user", async function () { userFindOneStub.returns({ select: sinon.stub().resolves(mockUser), }); @@ -115,23 +117,23 @@ describe("userModule", function() { }); }); - describe("getUserByEmail", function() { - it("throw an error if a user is not found", async function() { + describe("getUserByEmail", function () { + it("throw an error if a user is not found", async function () { userFindOneStub.returns({ select: sinon.stub().resolves(null), }); try { await getUserByEmail(mockUser.email); } catch (error) { - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); + expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND(mockLanguage)); } }); }); - describe("updateUser", function() { + describe("updateUser", function () { let req, res; - beforeEach(function() { + beforeEach(function () { req = { params: { userId: "testId", @@ -148,9 +150,9 @@ describe("userModule", function() { res = {}; }); - afterEach(function() {}); + afterEach(function () { }); - it("should update a user", async function() { + it("should update a user", async function () { parseBooleanStub.returns(false); userFindByIdAndUpdateStub.returns({ select: sinon.stub().returns({ @@ -166,7 +168,7 @@ describe("userModule", function() { expect(result).to.deep.equal(mockUser); }); - it("should delete a user profile image", async function() { + it("should delete a user profile image", async function () { req.body.deleteProfileImage = "true"; userFindByIdAndUpdateStub.returns({ select: sinon.stub().returns({ @@ -182,7 +184,7 @@ describe("userModule", function() { expect(result).to.deep.equal(mockUser); }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("test error"); userFindByIdAndUpdateStub.throws(err); try { @@ -194,23 +196,23 @@ describe("userModule", function() { }); }); - describe("deleteUser", function() { - it("should return a deleted user", async function() { + describe("deleteUser", function () { + it("should return a deleted user", async function () { userFindByIdAndDeleteStub.resolves(mockUser); const result = await deleteUser("testId"); expect(result).to.deep.equal(mockUser); }); - it("should throw an error if a user is not found", async function() { + it("should throw an error if a user is not found", async function () { try { await deleteUser("testId"); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); + expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND(mockLanguage)); } }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("test error"); userFindByIdAndDeleteStub.throws(err); try { @@ -222,14 +224,14 @@ describe("userModule", function() { }); }); - describe("deleteTeam", function() { - it("should return true if team deleted", async function() { + describe("deleteTeam", function () { + it("should return true if team deleted", async function () { teamFindByIdAndDeleteStub.resolves(); const result = await deleteTeam("testId"); expect(result).to.equal(true); }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("test error"); teamFindByIdAndDeleteStub.throws(err); try { @@ -241,14 +243,14 @@ describe("userModule", function() { }); }); - describe("deleteAllOtherUsers", function() { - it("should return true if all other users deleted", async function() { + describe("deleteAllOtherUsers", function () { + it("should return true if all other users deleted", async function () { userDeleteManyStub.resolves(true); const result = await deleteAllOtherUsers(); expect(result).to.equal(true); }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("test error"); userDeleteManyStub.throws(err); try { @@ -260,8 +262,8 @@ describe("userModule", function() { }); }); - describe("getAllUsers", function() { - it("should return all users", async function() { + describe("getAllUsers", function () { + it("should return all users", async function () { userFindStub.returns({ select: sinon.stub().returns({ select: sinon.stub().resolves([mockUser]), @@ -271,7 +273,7 @@ describe("userModule", function() { expect(result).to.deep.equal([mockUser]); }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("test error"); userFindStub.throws(err); try { @@ -283,14 +285,14 @@ describe("userModule", function() { }); }); - describe("logoutUser", function() { - it("should return true if user logged out", async function() { + describe("logoutUser", function () { + it("should return true if user logged out", async function () { userUpdateOneStub.resolves(true); const result = await logoutUser("testId"); expect(result).to.equal(true); }); - it("should handle an error", async function() { + it("should handle an error", async function () { const err = new Error("test error"); userUpdateOneStub.throws(err); try { diff --git a/Server/tests/services/networkService.test.js b/Server/tests/services/networkService.test.js index a5a59c938..f20b2628d 100644 --- a/Server/tests/services/networkService.test.js +++ b/Server/tests/services/networkService.test.js @@ -3,10 +3,10 @@ import NetworkService from "../../service/networkService.js"; import { expect } from "chai"; import http from "http"; import { errorMessages } from "../../utils/messages.js"; -describe("Network Service", function() { +describe("Network Service", function () { let axios, ping, Docker, logger, networkService; - beforeEach(function() { + beforeEach(function () { axios = { get: sinon.stub().resolves({ data: { foo: "bar" }, @@ -35,22 +35,22 @@ describe("Network Service", function() { networkService = new NetworkService(axios, ping, logger, http, Docker); }); - describe("constructor", function() { - it("should create a new NetworkService instance", function() { + describe("constructor", function () { + it("should create a new NetworkService instance", function () { const networkService = new NetworkService(); expect(networkService).to.be.an.instanceOf(NetworkService); }); }); - describe("timeRequest", function() { - it("should time an asynchronous operation", async function() { + describe("timeRequest", function () { + it("should time an asynchronous operation", async function () { const operation = sinon.stub().resolves("success"); const { response, responseTime } = await networkService.timeRequest(operation); expect(response).to.equal("success"); expect(responseTime).to.be.a("number"); }); - it("should handle errors if operation throws error", async function() { + it("should handle errors if operation throws error", async function () { const error = new Error("Test error"); const operation = sinon.stub().throws(error); const { response, responseTime } = await networkService.timeRequest(operation); @@ -60,8 +60,8 @@ describe("Network Service", function() { }); }); - describe("requestPing", function() { - it("should return a response object if ping successful", async function() { + describe("requestPing", function () { + it("should return a response object if ping successful", async function () { const pingResult = await networkService.requestPing({ data: { url: "http://test.com", _id: "123" }, }); @@ -71,7 +71,7 @@ describe("Network Service", function() { expect(pingResult.status).to.be.true; }); - it("should return a response object if ping unsuccessful", async function() { + it("should return a response object if ping unsuccessful", async function () { const error = new Error("Test error"); networkService.timeRequest = sinon .stub() @@ -86,7 +86,7 @@ describe("Network Service", function() { expect(pingResult.code).to.equal(networkService.PING_ERROR); }); - it("should throw an error if ping cannot resolve", async function() { + it("should throw an error if ping cannot resolve", async function () { const error = new Error("test error"); networkService.timeRequest = sinon.stub().throws(error); try { @@ -100,8 +100,8 @@ describe("Network Service", function() { }); }); - describe("requestHttp", function() { - it("should return a response object if http successful", async function() { + describe("requestHttp", function () { + it("should return a response object if http successful", async function () { const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; const httpResult = await networkService.requestHttp(job); expect(httpResult.monitorId).to.equal("123"); @@ -110,7 +110,7 @@ describe("Network Service", function() { expect(httpResult.status).to.be.true; }); - it("should return a response object if http unsuccessful", async function() { + it("should return a response object if http unsuccessful", async function () { const error = new Error("Test error"); error.response = { status: 404 }; networkService.timeRequest = sinon @@ -125,7 +125,7 @@ describe("Network Service", function() { expect(httpResult.code).to.equal(404); }); - it("should return a response object if http unsuccessful with unknown code", async function() { + it("should return a response object if http unsuccessful with unknown code", async function () { const error = new Error("Test error"); error.response = {}; networkService.timeRequest = sinon @@ -140,7 +140,7 @@ describe("Network Service", function() { expect(httpResult.code).to.equal(networkService.NETWORK_ERROR); }); - it("should throw an error if an error occurs", async function() { + it("should throw an error if an error occurs", async function () { const error = new Error("test error"); networkService.timeRequest = sinon.stub().throws(error); try { @@ -154,8 +154,8 @@ describe("Network Service", function() { }); }); - describe("requestPagespeed", function() { - it("should return a response object if pagespeed successful", async function() { + describe("requestPagespeed", function () { + it("should return a response object if pagespeed successful", async function () { const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; const pagespeedResult = await networkService.requestPagespeed(job); expect(pagespeedResult.monitorId).to.equal("123"); @@ -164,7 +164,7 @@ describe("Network Service", function() { expect(pagespeedResult.status).to.be.true; }); - it("should return a response object if pagespeed unsuccessful", async function() { + it("should return a response object if pagespeed unsuccessful", async function () { const error = new Error("Test error"); error.response = { status: 404 }; networkService.timeRequest = sinon @@ -179,7 +179,7 @@ describe("Network Service", function() { expect(pagespeedResult.code).to.equal(404); }); - it("should return a response object if pagespeed unsuccessful with an unknown code", async function() { + it("should return a response object if pagespeed unsuccessful with an unknown code", async function () { const error = new Error("Test error"); error.response = {}; networkService.timeRequest = sinon @@ -194,7 +194,7 @@ describe("Network Service", function() { expect(pagespeedResult.code).to.equal(networkService.NETWORK_ERROR); }); - it("should throw an error if pagespeed cannot resolve", async function() { + it("should throw an error if pagespeed cannot resolve", async function () { const error = new Error("test error"); networkService.timeRequest = sinon.stub().throws(error); try { @@ -208,8 +208,8 @@ describe("Network Service", function() { }); }); - describe("requestHardware", function() { - it("should return a response object if hardware successful", async function() { + describe("requestHardware", function () { + it("should return a response object if hardware successful", async function () { const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; const httpResult = await networkService.requestHardware(job); expect(httpResult.monitorId).to.equal("123"); @@ -218,7 +218,7 @@ describe("Network Service", function() { expect(httpResult.status).to.be.true; }); - it("should return a response object if hardware successful and job has a secret", async function() { + it("should return a response object if hardware successful and job has a secret", async function () { const job = { data: { url: "http://test.com", @@ -234,7 +234,7 @@ describe("Network Service", function() { expect(httpResult.status).to.be.true; }); - it("should return a response object if hardware unsuccessful", async function() { + it("should return a response object if hardware unsuccessful", async function () { const error = new Error("Test error"); error.response = { status: 404 }; networkService.timeRequest = sinon @@ -249,7 +249,7 @@ describe("Network Service", function() { expect(httpResult.code).to.equal(404); }); - it("should return a response object if hardware unsuccessful with unknown code", async function() { + it("should return a response object if hardware unsuccessful with unknown code", async function () { const error = new Error("Test error"); error.response = {}; networkService.timeRequest = sinon @@ -264,7 +264,7 @@ describe("Network Service", function() { expect(httpResult.code).to.equal(networkService.NETWORK_ERROR); }); - it("should throw an error if hardware cannot resolve", async function() { + it("should throw an error if hardware cannot resolve", async function () { const error = new Error("test error"); networkService.timeRequest = sinon.stub().throws(error); try { @@ -278,8 +278,8 @@ describe("Network Service", function() { }); }); - describe("requestDocker", function() { - it("should return a response object if docker successful", async function() { + describe("requestDocker", function () { + it("should return a response object if docker successful", async function () { const job = { data: { url: "http://test.com", _id: "123", type: "docker" } }; const dockerResult = await networkService.requestDocker(job); expect(dockerResult.monitorId).to.equal("123"); @@ -288,7 +288,7 @@ describe("Network Service", function() { expect(dockerResult.status).to.be.true; }); - it("should return a response object with status false if container not running", async function() { + it("should return a response object with status false if container not running", async function () { Docker = class { listContainers = sinon.stub().resolves([ { @@ -307,7 +307,7 @@ describe("Network Service", function() { expect(dockerResult.code).to.equal(200); }); - it("should handle an error when fetching the container", async function() { + it("should handle an error when fetching the container", async function () { Docker = class { listContainers = sinon.stub().resolves([ { @@ -326,7 +326,7 @@ describe("Network Service", function() { expect(dockerResult.code).to.equal(networkService.NETWORK_ERROR); }); - it("should throw an error if operations fail", async function() { + it("should throw an error if operations fail", async function () { Docker = class { listContainers = sinon.stub().resolves([ { @@ -345,7 +345,7 @@ describe("Network Service", function() { } }); - it("should throw an error if no matching images found", async function() { + it("should throw an error if no matching images found", async function () { Docker = class { listContainers = sinon.stub().resolves([]); getContainer = sinon.stub().throws(new Error("test error")); @@ -355,13 +355,13 @@ describe("Network Service", function() { try { await networkService.requestDocker(job); } catch (error) { - expect(error.message).to.equal(errorMessages.DOCKER_NOT_FOUND); + expect(error.message).to.equal(errorMessages.DOCKER_NOT_FOUND(mockLanguage)); } }); }); - describe("getStatus", function() { - beforeEach(function() { + describe("getStatus", function () { + beforeEach(function () { networkService.requestPing = sinon.stub(); networkService.requestHttp = sinon.stub(); networkService.requestPagespeed = sinon.stub(); @@ -369,11 +369,11 @@ describe("Network Service", function() { networkService.requestDocker = sinon.stub(); }); - afterEach(function() { + afterEach(function () { sinon.restore(); }); - it("should call requestPing if type is ping", async function() { + it("should call requestPing if type is ping", async function () { await networkService.getStatus({ data: { type: "ping" } }); expect(networkService.requestPing.calledOnce).to.be.true; expect(networkService.requestDocker.notCalled).to.be.true; @@ -381,7 +381,7 @@ describe("Network Service", function() { expect(networkService.requestPagespeed.notCalled).to.be.true; }); - it("should call requestHttp if type is http", async function() { + it("should call requestHttp if type is http", async function () { await networkService.getStatus({ data: { type: "http" } }); expect(networkService.requestPing.notCalled).to.be.true; expect(networkService.requestDocker.notCalled).to.be.true; @@ -389,7 +389,7 @@ describe("Network Service", function() { expect(networkService.requestPagespeed.notCalled).to.be.true; }); - it("should call requestPagespeed if type is pagespeed", async function() { + it("should call requestPagespeed if type is pagespeed", async function () { await networkService.getStatus({ data: { type: "pagespeed" } }); expect(networkService.requestPing.notCalled).to.be.true; expect(networkService.requestDocker.notCalled).to.be.true; @@ -397,7 +397,7 @@ describe("Network Service", function() { expect(networkService.requestPagespeed.calledOnce).to.be.true; }); - it("should call requestHardware if type is hardware", async function() { + it("should call requestHardware if type is hardware", async function () { await networkService.getStatus({ data: { type: "hardware" } }); expect(networkService.requestHardware.calledOnce).to.be.true; expect(networkService.requestDocker.notCalled).to.be.true; @@ -405,7 +405,7 @@ describe("Network Service", function() { expect(networkService.requestPagespeed.notCalled).to.be.true; }); - it("should call requestDocker if type is Docker", async function() { + it("should call requestDocker if type is Docker", async function () { await networkService.getStatus({ data: { type: "docker" } }); expect(networkService.requestDocker.calledOnce).to.be.true; expect(networkService.requestHardware.notCalled).to.be.true; @@ -413,7 +413,7 @@ describe("Network Service", function() { expect(networkService.requestPagespeed.notCalled).to.be.true; }); - it("should throw an error if an unknown type is provided", async function() { + it("should throw an error if an unknown type is provided", async function () { try { await networkService.getStatus({ data: { type: "unknown" } }); } catch (error) { @@ -423,7 +423,7 @@ describe("Network Service", function() { } }); - it("should throw an error if job type is undefined", async function() { + it("should throw an error if job type is undefined", async function () { try { await networkService.getStatus({ data: { type: undefined } }); } catch (error) { @@ -433,7 +433,7 @@ describe("Network Service", function() { } }); - it("should throw an error if job is empty", async function() { + it("should throw an error if job is empty", async function () { try { await networkService.getStatus({}); } catch (error) { @@ -442,7 +442,7 @@ describe("Network Service", function() { } }); - it("should throw an error if job is null", async function() { + it("should throw an error if job is null", async function () { try { await networkService.getStatus(null); } catch (error) { diff --git a/Server/tests/utils/messages.test.js b/Server/tests/utils/messages.test.js index 7ece332c4..a36fc2a4a 100644 --- a/Server/tests/utils/messages.test.js +++ b/Server/tests/utils/messages.test.js @@ -1,23 +1,25 @@ import { errorMessages, successMessages } from "../../utils/messages.js"; -describe("Messages", function() { - describe("messages - errorMessages", function() { - it("should have a DB_FIND_MONITOR_BY_ID function", function() { +describe("Messages", function () { + describe("messages - errorMessages", function () { + it("should have a DB_FIND_MONITOR_BY_ID function", function () { const monitorId = "12345"; - expect(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)).to.equal( + const mockLanguage = 'en'; + expect(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, mockLanguage)).to.equal( `Monitor with id ${monitorId} not found` ); }); - it("should have a DB_DELETE_CHECKS function", function() { + it("should have a DB_DELETE_CHECKS function", function () { const monitorId = "12345"; - expect(errorMessages.DB_DELETE_CHECKS(monitorId)).to.equal( + const mockLanguage = 'en'; + expect(errorMessages.DB_DELETE_CHECKS(monitorId, mockLanguage)).to.equal( `No checks found for monitor with id ${monitorId}` ); }); }); - describe("messages - successMessages", function() { - it("should have a MONITOR_GET_BY_USER_ID function", function() { + describe("messages - successMessages", function () { + it("should have a MONITOR_GET_BY_USER_ID function", function () { const userId = "12345"; expect(successMessages.MONITOR_GET_BY_USER_ID(userId)).to.equal( `Got monitor for ${userId} successfully"` diff --git a/Server/utils/formattedKey.js b/Server/utils/formattedKey.js new file mode 100644 index 000000000..b60e6783b --- /dev/null +++ b/Server/utils/formattedKey.js @@ -0,0 +1,14 @@ +/** + * Converts a snake_case or SCREAMING_SNAKE_CASE key to camelCase + * Example: AUTH_INCORRECT_PASSWORD -> authIncorrectPassword + * @param {string} key - The key to format + * @returns {string} - The formatted key in camelCase + */ +export const formattedKey = (key) => { + return key.toLowerCase() + .split('_') + .map((word, index) => + index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1) + ) + .join(''); +}; \ No newline at end of file diff --git a/Server/utils/messages.js b/Server/utils/messages.js index 38b1bf9d1..f4568165b 100644 --- a/Server/utils/messages.js +++ b/Server/utils/messages.js @@ -1,137 +1,146 @@ +import ServiceRegistry from '../service/serviceRegistry.js'; +import TranslationService from '../service/translationService.js'; + +const getTranslatedMessage = (key, language = 'en') => { + console.log("getTranslatedMessage", key, language); + const translationService = ServiceRegistry.get(TranslationService.SERVICE_NAME); + return translationService.getTranslation(key, language); +}; + const errorMessages = { // General Errors: - FRIENDLY_ERROR: "Something went wrong...", - UNKNOWN_ERROR: "An unknown error occurred", + FRIENDLY_ERROR: (language) => getTranslatedMessage('FRIENDLY_ERROR', language), + UNKNOWN_ERROR: (language) => getTranslatedMessage('UNKNOWN_ERROR', language), // Auth Controller - UNAUTHORIZED: "Unauthorized access", - AUTH_ADMIN_EXISTS: "Admin already exists", - AUTH_INVITE_NOT_FOUND: "Invite not found", + UNAUTHORIZED: (language) => getTranslatedMessage('UNAUTHORIZED', language), + AUTH_ADMIN_EXISTS: (language) => getTranslatedMessage('AUTH_ADMIN_EXISTS', language), + AUTH_INVITE_NOT_FOUND: (language) => getTranslatedMessage('AUTH_INVITE_NOT_FOUND', language), //Error handling middleware - UNKNOWN_SERVICE: "Unknown service", - NO_AUTH_TOKEN: "No auth token provided", - INVALID_AUTH_TOKEN: "Invalid auth token", - EXPIRED_AUTH_TOKEN: "Token expired", - NO_REFRESH_TOKEN: "No refresh token provided", - INVALID_REFRESH_TOKEN: "Invalid refresh token", - EXPIRED_REFRESH_TOKEN: "Refresh token expired", - REQUEST_NEW_ACCESS_TOKEN: "Request new access token", + UNKNOWN_SERVICE: (language) => getTranslatedMessage('UNKNOWN_SERVICE', language), + NO_AUTH_TOKEN: (language) => getTranslatedMessage('NO_AUTH_TOKEN', language), + INVALID_AUTH_TOKEN: (language) => getTranslatedMessage('INVALID_AUTH_TOKEN', language), + EXPIRED_AUTH_TOKEN: (language) => getTranslatedMessage('EXPIRED_AUTH_TOKEN', language), + NO_REFRESH_TOKEN: (language) => getTranslatedMessage('NO_REFRESH_TOKEN', language), + INVALID_REFRESH_TOKEN: (language) => getTranslatedMessage('INVALID_REFRESH_TOKEN', language), + EXPIRED_REFRESH_TOKEN: (language) => getTranslatedMessage('EXPIRED_REFRESH_TOKEN', language), + REQUEST_NEW_ACCESS_TOKEN: (language) => getTranslatedMessage('REQUEST_NEW_ACCESS_TOKEN', language), //Payload - INVALID_PAYLOAD: "Invalid payload", + INVALID_PAYLOAD: (language) => getTranslatedMessage('INVALID_PAYLOAD', language), //Ownership Middleware - VERIFY_OWNER_NOT_FOUND: "Document not found", - VERIFY_OWNER_UNAUTHORIZED: "Unauthorized access", + VERIFY_OWNER_NOT_FOUND: (language) => getTranslatedMessage('VERIFY_OWNER_NOT_FOUND', language), + VERIFY_OWNER_UNAUTHORIZED: (language) => getTranslatedMessage('VERIFY_OWNER_UNAUTHORIZED', language), //Permissions Middleware - INSUFFICIENT_PERMISSIONS: "Insufficient permissions", + INSUFFICIENT_PERMISSIONS: (language) => getTranslatedMessage('INSUFFICIENT_PERMISSIONS', language), //DB Errors - DB_USER_EXISTS: "User already exists", - DB_USER_NOT_FOUND: "User not found", - DB_TOKEN_NOT_FOUND: "Token not found", - DB_RESET_PASSWORD_BAD_MATCH: "New password must be different from old password", - DB_FIND_MONITOR_BY_ID: (monitorId) => `Monitor with id ${monitorId} not found`, - DB_DELETE_CHECKS: (monitorId) => `No checks found for monitor with id ${monitorId}`, + DB_USER_EXISTS: (language) => getTranslatedMessage('DB_USER_EXISTS', language), + DB_USER_NOT_FOUND: (language) => getTranslatedMessage('DB_USER_NOT_FOUND', language), + DB_TOKEN_NOT_FOUND: (language) => getTranslatedMessage('DB_TOKEN_NOT_FOUND', language), + DB_RESET_PASSWORD_BAD_MATCH: (language) => getTranslatedMessage('DB_RESET_PASSWORD_BAD_MATCH', language), + DB_FIND_MONITOR_BY_ID: (monitorId, language) => getTranslatedMessage('DB_FIND_MONITOR_BY_ID', language).replace('{monitorId}', monitorId), + DB_DELETE_CHECKS: (monitorId, language) => getTranslatedMessage('DB_DELETE_CHECKS', language).replace('{monitorId}', monitorId), //Auth errors - AUTH_INCORRECT_PASSWORD: "Incorrect password", - AUTH_UNAUTHORIZED: "Unauthorized access", + AUTH_INCORRECT_PASSWORD: (language) => getTranslatedMessage('AUTH_INCORRECT_PASSWORD', language), + AUTH_UNAUTHORIZED: (language) => getTranslatedMessage('AUTH_UNAUTHORIZED', language), // Monitor Errors - MONITOR_GET_BY_ID: "Monitor not found", - MONITOR_GET_BY_USER_ID: "No monitors found for user", + MONITOR_GET_BY_ID: (language) => getTranslatedMessage('MONITOR_GET_BY_ID', language), + MONITOR_GET_BY_USER_ID: (language) => getTranslatedMessage('MONITOR_GET_BY_USER_ID', language), // Job Queue Errors - JOB_QUEUE_WORKER_CLOSE: "Error closing worker", - JOB_QUEUE_DELETE_JOB: "Job not found in queue", - JOB_QUEUE_OBLITERATE: "Error obliterating queue", + JOB_QUEUE_WORKER_CLOSE: (language) => getTranslatedMessage('JOB_QUEUE_WORKER_CLOSE', language), + JOB_QUEUE_DELETE_JOB: (language) => getTranslatedMessage('JOB_QUEUE_DELETE_JOB', language), + JOB_QUEUE_OBLITERATE: (language) => getTranslatedMessage('JOB_QUEUE_OBLITERATE', language), // PING Operations - PING_CANNOT_RESOLVE: "No response", + PING_CANNOT_RESOLVE: (language) => getTranslatedMessage('PING_CANNOT_RESOLVE', language), // Status Page Errors - STATUS_PAGE_NOT_FOUND: "Status page not found", - STATUS_PAGE_URL_NOT_UNIQUE: "Status page url must be unique", + STATUS_PAGE_NOT_FOUND: (language) => getTranslatedMessage('STATUS_PAGE_NOT_FOUND', language), + STATUS_PAGE_URL_NOT_UNIQUE: (language) => getTranslatedMessage('STATUS_PAGE_URL_NOT_UNIQUE', language), // Docker - DOCKER_FAIL: "Failed to fetch Docker container information", - DOCKER_NOT_FOUND: "Docker container not found", + DOCKER_FAIL: (language) => getTranslatedMessage('DOCKER_FAIL', language), + DOCKER_NOT_FOUND: (language) => getTranslatedMessage('DOCKER_NOT_FOUND', language), // Port - PORT_FAIL: "Failed to connect to port", + PORT_FAIL: (language) => getTranslatedMessage('PORT_FAIL', language), }; const successMessages = { //Alert Controller - ALERT_CREATE: "Alert created successfully", - ALERT_GET_BY_USER: "Got alerts successfully", - ALERT_GET_BY_MONITOR: "Got alerts by Monitor successfully", - ALERT_GET_BY_ID: "Got alert by Id successfully", - ALERT_EDIT: "Alert edited successfully", - ALERT_DELETE: "Alert deleted successfully", + ALERT_CREATE: (language) => getTranslatedMessage('ALERT_CREATE', language), + ALERT_GET_BY_USER: (language) => getTranslatedMessage('ALERT_GET_BY_USER', language), + ALERT_GET_BY_MONITOR: (language) => getTranslatedMessage('ALERT_GET_BY_MONITOR', language), + ALERT_GET_BY_ID: (language) => getTranslatedMessage('ALERT_GET_BY_ID', language), + ALERT_EDIT: (language) => getTranslatedMessage('ALERT_EDIT', language), + ALERT_DELETE: (language) => getTranslatedMessage('ALERT_DELETE', language), // Auth Controller - AUTH_CREATE_USER: "User created successfully", - AUTH_LOGIN_USER: "User logged in successfully", - AUTH_LOGOUT_USER: "User logged out successfully", - AUTH_UPDATE_USER: "User updated successfully", - AUTH_CREATE_RECOVERY_TOKEN: "Recovery token created successfully", - AUTH_VERIFY_RECOVERY_TOKEN: "Recovery token verified successfully", - AUTH_RESET_PASSWORD: "Password reset successfully", - AUTH_ADMIN_CHECK: "Admin check completed successfully", - AUTH_DELETE_USER: "User deleted successfully", - AUTH_TOKEN_REFRESHED: "Auth token is refreshed", - AUTH_GET_ALL_USERS: "Got all users successfully", + AUTH_CREATE_USER: (language) => getTranslatedMessage('AUTH_CREATE_USER', language), + AUTH_LOGIN_USER: (language) => getTranslatedMessage('AUTH_LOGIN_USER', language), + AUTH_LOGOUT_USER: (language) => getTranslatedMessage('AUTH_LOGOUT_USER', language), + AUTH_UPDATE_USER: (language) => getTranslatedMessage('AUTH_UPDATE_USER', language), + AUTH_CREATE_RECOVERY_TOKEN: (language) => getTranslatedMessage('AUTH_CREATE_RECOVERY_TOKEN', language), + AUTH_VERIFY_RECOVERY_TOKEN: (language) => getTranslatedMessage('AUTH_VERIFY_RECOVERY_TOKEN', language), + AUTH_RESET_PASSWORD: (language) => getTranslatedMessage('AUTH_RESET_PASSWORD', language), + AUTH_ADMIN_CHECK: (language) => getTranslatedMessage('AUTH_ADMIN_CHECK', language), + AUTH_DELETE_USER: (language) => getTranslatedMessage('AUTH_DELETE_USER', language), + AUTH_TOKEN_REFRESHED: (language) => getTranslatedMessage('AUTH_TOKEN_REFRESHED', language), + AUTH_GET_ALL_USERS: (language) => getTranslatedMessage('AUTH_GET_ALL_USERS', language), // Invite Controller - INVITE_ISSUED: "Invite sent successfully", - INVITE_VERIFIED: "Invite verified successfully", + INVITE_ISSUED: (language) => getTranslatedMessage('INVITE_ISSUED', language), + INVITE_VERIFIED: (language) => getTranslatedMessage('INVITE_VERIFIED', language), // Check Controller - CHECK_CREATE: "Check created successfully", - CHECK_GET: "Got checks successfully", - CHECK_DELETE: "Checks deleted successfully", - CHECK_UPDATE_TTL: "Checks TTL updated successfully", + CHECK_CREATE: (language) => getTranslatedMessage('CHECK_CREATE', language), + CHECK_GET: (language) => getTranslatedMessage('CHECK_GET', language), + CHECK_DELETE: (language) => getTranslatedMessage('CHECK_DELETE', language), + CHECK_UPDATE_TTL: (language) => getTranslatedMessage('CHECK_UPDATE_TTL', language), //Monitor Controller - MONITOR_GET_ALL: "Got all monitors successfully", - MONITOR_STATS_BY_ID: "Got monitor stats by Id successfully", - MONITOR_GET_BY_ID: "Got monitor by Id successfully", - MONITOR_GET_BY_TEAM_ID: "Got monitors by Team Id successfully", - MONITOR_GET_BY_USER_ID: (userId) => `Got monitor for ${userId} successfully"`, - MONITOR_CREATE: "Monitor created successfully", - MONITOR_DELETE: "Monitor deleted successfully", - MONITOR_EDIT: "Monitor edited successfully", - MONITOR_CERTIFICATE: "Got monitor certificate successfully", - MONITOR_DEMO_ADDED: "Successfully added demo monitors", + MONITOR_GET_ALL: (language) => getTranslatedMessage('MONITOR_GET_ALL', language), + MONITOR_STATS_BY_ID: (language) => getTranslatedMessage('MONITOR_STATS_BY_ID', language), + MONITOR_GET_BY_ID: (language) => getTranslatedMessage('MONITOR_GET_BY_ID', language), + MONITOR_GET_BY_TEAM_ID: (language) => getTranslatedMessage('MONITOR_GET_BY_TEAM_ID', language), + MONITOR_GET_BY_USER_ID: (userId, language) => getTranslatedMessage('MONITOR_GET_BY_USER_ID', language).replace('{userId}', userId), + MONITOR_CREATE: (language) => getTranslatedMessage('MONITOR_CREATE', language), + MONITOR_DELETE: (language) => getTranslatedMessage('MONITOR_DELETE', language), + MONITOR_EDIT: (language) => getTranslatedMessage('MONITOR_EDIT', language), + MONITOR_CERTIFICATE: (language) => getTranslatedMessage('MONITOR_CERTIFICATE', language), + MONITOR_DEMO_ADDED: (language) => getTranslatedMessage('MONITOR_DEMO_ADDED', language), // Queue Controller - QUEUE_GET_METRICS: "Got metrics successfully", - QUEUE_ADD_JOB: "Job added successfully", - QUEUE_OBLITERATE: "Queue obliterated", + QUEUE_GET_METRICS: (language) => getTranslatedMessage('QUEUE_GET_METRICS', language), + QUEUE_ADD_JOB: (language) => getTranslatedMessage('QUEUE_ADD_JOB', language), + QUEUE_OBLITERATE: (language) => getTranslatedMessage('QUEUE_OBLITERATE', language), //Job Queue - JOB_QUEUE_DELETE_JOB: "Job removed successfully", - JOB_QUEUE_OBLITERATE: "Queue OBLITERATED!!!", - JOB_QUEUE_PAUSE_JOB: "Job paused successfully", - JOB_QUEUE_RESUME_JOB: "Job resumed successfully", + JOB_QUEUE_DELETE_JOB: (language) => getTranslatedMessage('JOB_QUEUE_DELETE_JOB', language), + JOB_QUEUE_OBLITERATE: (language) => getTranslatedMessage('JOB_QUEUE_OBLITERATE', language), + JOB_QUEUE_PAUSE_JOB: (language) => getTranslatedMessage('JOB_QUEUE_PAUSE_JOB', language), + JOB_QUEUE_RESUME_JOB: (language) => getTranslatedMessage('JOB_QUEUE_RESUME_JOB', language), //Maintenance Window Controller - MAINTENANCE_WINDOW_GET_BY_ID: "Got Maintenance Window by Id successfully", - MAINTENANCE_WINDOW_CREATE: "Maintenance Window created successfully", - MAINTENANCE_WINDOW_GET_BY_TEAM: "Got Maintenance Windows by Team successfully", - MAINTENANCE_WINDOW_DELETE: "Maintenance Window deleted successfully", - MAINTENANCE_WINDOW_EDIT: "Maintenance Window edited successfully", + MAINTENANCE_WINDOW_GET_BY_ID: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_GET_BY_ID', language), + MAINTENANCE_WINDOW_CREATE: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_CREATE', language), + MAINTENANCE_WINDOW_GET_BY_TEAM: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_GET_BY_TEAM', language), + MAINTENANCE_WINDOW_DELETE: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_DELETE', language), + MAINTENANCE_WINDOW_EDIT: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_EDIT', language), //Ping Operations - PING_SUCCESS: "Success", + PING_SUCCESS: (language) => getTranslatedMessage('PING_SUCCESS', language), // App Settings - GET_APP_SETTINGS: "Got app settings successfully", - UPDATE_APP_SETTINGS: "Updated app settings successfully", + GET_APP_SETTINGS: (language) => getTranslatedMessage('GET_APP_SETTINGS', language), + UPDATE_APP_SETTINGS: (language) => getTranslatedMessage('UPDATE_APP_SETTINGS', language), // Status Page STATUS_PAGE_BY_URL: "Got status page by url successfully", @@ -141,10 +150,10 @@ const successMessages = { STATUS_PAGE_UPDATE: "Status page updated successfully", STATUS_PAGE_BY_TEAM_ID: "Got status pages by team id successfully", // Docker - DOCKER_SUCCESS: "Docker container status fetched successfully", + DOCKER_SUCCESS: (language) => getTranslatedMessage('DOCKER_SUCCESS', language), // Port - PORT_SUCCESS: "Port connected successfully", + PORT_SUCCESS: (language) => getTranslatedMessage('PORT_SUCCESS', language), }; export { errorMessages, successMessages }; From e18f1d9b8571918c6c17ad87facc2cc8a350a2fc Mon Sep 17 00:00:00 2001 From: cihatata Date: Tue, 28 Jan 2025 00:32:00 +0300 Subject: [PATCH 13/52] feat: i18n support for backend --- Server/service/translationService.js | 20 +++----------------- 1 file changed, 3 insertions(+), 17 deletions(-) diff --git a/Server/service/translationService.js b/Server/service/translationService.js index 11709262a..5da13b45b 100644 --- a/Server/service/translationService.js +++ b/Server/service/translationService.js @@ -9,18 +9,16 @@ class TranslationService { constructor(logger) { this.logger = logger; this.translations = {}; - this.apiToken = "ddf8d5fdbe1baa12bb3b5519b639d00a"; - this.projectId = "757606"; + this.apiToken = process.env.POEDITOR_API_TOKEN; + this.projectId = process.env.POEDITOR_PROJECT_ID; this.baseUrl = 'https://api.poeditor.com/v2'; this.localesDir = path.join(process.cwd(), 'locales'); } async initialize() { try { - // Önce dosyalardan okumayı dene const loadedFromFiles = await this.loadFromFiles(); - // Eğer dosyalardan yüklenemezse veya dosyalar yoksa POEditor'dan çek if (!loadedFromFiles) { await this.loadTranslations(); } @@ -36,19 +34,16 @@ class TranslationService { async loadFromFiles() { try { - // locales klasörü yoksa false dön if (!fs.existsSync(this.localesDir)) { return false; } - // Klasördeki tüm .json dosyalarını oku const files = fs.readdirSync(this.localesDir).filter(file => file.endsWith('.json')); if (files.length === 0) { return false; } - // Her dosyayı oku ve translations objesine ekle for (const file of files) { const language = file.replace('.json', ''); const filePath = path.join(this.localesDir, file); @@ -76,16 +71,13 @@ class TranslationService { async loadTranslations() { try { - // Önce mevcut dilleri al const languages = await this.getLanguages(); - // Her dil için çevirileri indir for (const language of languages) { const translations = await this.exportTranslations(language); this.translations[language] = translations; } - // Çevirileri dosyaya kaydet await this.saveTranslations(); } catch (error) { this.logger.error({ @@ -117,7 +109,7 @@ class TranslationService { method: 'getLanguages', stack: error.stack }); - return ['en']; // Varsayılan olarak İngilizce + return ['en']; } } @@ -129,17 +121,14 @@ class TranslationService { params.append('language', language); params.append('type', 'key_value_json'); - // Export isteği const exportResponse = await axios.post(`${this.baseUrl}/projects/export`, params, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }); - // İndirme URL'sini al const { url } = exportResponse.data.result; - // Çevirileri indir const translationsResponse = await axios.get(url); return translationsResponse.data; } catch (error) { @@ -155,12 +144,10 @@ class TranslationService { async saveTranslations() { try { - // locales klasörü yoksa oluştur if (!fs.existsSync(this.localesDir)) { fs.mkdirSync(this.localesDir); } - // Her dil için JSON dosyası oluştur for (const [language, translations] of Object.entries(this.translations)) { const filePath = path.join(this.localesDir, `${language}.json`); fs.writeFileSync(filePath, JSON.stringify(translations, null, 2)); @@ -182,7 +169,6 @@ class TranslationService { } getTranslation(key, language = 'en') { - // Convert key from AUTH_INCORRECT_PASSWORD format to authIncorrectPassword format const formattedKeyText = formattedKey(key); try { return this.translations[language]?.[formattedKeyText] || this.translations['en']?.[formattedKeyText] || formattedKeyText; From 6fc0e31743b90509347d4808075ce14284a66ec6 Mon Sep 17 00:00:00 2001 From: cihatata Date: Wed, 29 Jan 2025 01:57:49 +0300 Subject: [PATCH 14/52] fix: create stringService --- Docker/dist/docker-compose.yaml | 16 +- Server/controllers/authController.js | 15 +- Server/index.js | 11 +- Server/middleware/languageMiddleware.js | 6 +- Server/service/networkService.js | 102 ++++++ Server/service/stringService.js | 322 ++++++++++++++++++ Server/service/translationService.js | 185 +++++++--- .../tests/controllers/queueController.test.js | 32 +- Server/utils/formattedKey.js | 14 - Server/utils/locales_en.json | 143 ++++++++ 10 files changed, 760 insertions(+), 86 deletions(-) create mode 100644 Server/service/stringService.js delete mode 100644 Server/utils/formattedKey.js create mode 100644 Server/utils/locales_en.json diff --git a/Docker/dist/docker-compose.yaml b/Docker/dist/docker-compose.yaml index 420941e7e..db8bc5c07 100644 --- a/Docker/dist/docker-compose.yaml +++ b/Docker/dist/docker-compose.yaml @@ -1,6 +1,8 @@ services: client: - image: bluewaveuptime/uptime_client:latest + build: + context: ../../ + dockerfile: Docker/dist/client.Dockerfile restart: always environment: UPTIME_APP_API_BASE_URL: "http://localhost:5000/api/v1" @@ -11,7 +13,9 @@ services: depends_on: - server server: - image: bluewaveuptime/uptime_server:latest + build: + context: ../../ + dockerfile: Docker/dist/server.Dockerfile restart: always ports: - "5000:5000" @@ -24,7 +28,9 @@ services: # volumes: # - /var/run/docker.sock:/var/run/docker.sock:ro redis: - image: bluewaveuptime/uptime_redis:latest + build: + context: ../../ + dockerfile: Docker/dist/redis.Dockerfile restart: always ports: - "6379:6379" @@ -37,7 +43,9 @@ services: retries: 5 start_period: 5s mongodb: - image: bluewaveuptime/uptime_database_mongo:latest + build: + context: ../../ + dockerfile: Docker/dist/mongoDB.Dockerfile restart: always volumes: - ./mongo/data:/data/db diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 05a9b9ccb..b5c21d619 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -16,11 +16,12 @@ import { handleValidationError, handleError } from "./controllerUtils.js"; const SERVICE_NAME = "authController"; class AuthController { - constructor(db, settingsService, emailService, jobQueue) { + constructor(db, settingsService, emailService, jobQueue, stringService) { this.db = db; this.settingsService = settingsService; this.emailService = emailService; this.jobQueue = jobQueue; + this.stringService = stringService; } /** @@ -153,7 +154,7 @@ class AuthController { // Compare password const match = await user.comparePassword(password); if (match !== true) { - const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD(req.language)); + const error = new Error(this.stringService.authIncorrectPassword); error.status = 401; next(error); return; @@ -206,7 +207,7 @@ class AuthController { if (!refreshToken) { // No refresh token provided - const error = new Error(errorMessages.NO_REFRESH_TOKEN(req.language)); + const error = new Error(this.stringService.noRefreshToken); error.status = 401; error.service = SERVICE_NAME; error.method = "refreshAuthToken"; @@ -221,8 +222,8 @@ class AuthController { // Invalid or expired refresh token, trigger logout const errorMessage = refreshErr.name === "TokenExpiredError" - ? errorMessages.EXPIRED_REFRESH_TOKEN - : errorMessages.INVALID_REFRESH_TOKEN; + ? this.stringService.expiredAuthToken + : this.stringService.invalidAuthToken; const error = new Error(errorMessage); error.status = 401; error.service = SERVICE_NAME; @@ -276,7 +277,7 @@ class AuthController { // TODO is this neccessary any longer? Verify ownership middleware should handle this if (req.params.userId !== req.user._id.toString()) { - const error = new Error(errorMessages.AUTH_UNAUTHORIZED(req.language)); + const error = new Error(this.stringService.unauthorized); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -300,7 +301,7 @@ class AuthController { // If not a match, throw a 403 // 403 instead of 401 to avoid triggering axios interceptor if (!match) { - const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD(req.language)); + const error = new Error(this.stringService.authIncorrectPassword); error.status = 403; next(error); return; diff --git a/Server/index.js b/Server/index.js index 1b908436b..8bd4be260 100644 --- a/Server/index.js +++ b/Server/index.js @@ -76,6 +76,7 @@ import IORedis from "ioredis"; import TranslationService from './service/translationService.js'; import languageMiddleware from './middleware/languageMiddleware.js'; +import StringService from './service/stringService.js'; const SERVICE_NAME = "Server"; const SHUTDOWN_TIMEOUT = 1000; @@ -178,7 +179,8 @@ const startApp = async () => { const networkService = new NetworkService(axios, ping, logger, http, Docker, net); const statusService = new StatusService(db, logger); const notificationService = new NotificationService(emailService, db, logger); - const translationService = new TranslationService(logger); + const translationService = new TranslationService(logger, networkService); + const stringService = new StringService(translationService); const jobQueue = new JobQueue( db, @@ -200,7 +202,7 @@ const startApp = async () => { ServiceRegistry.register(StatusService.SERVICE_NAME, statusService); ServiceRegistry.register(NotificationService.SERVICE_NAME, notificationService); ServiceRegistry.register(TranslationService.SERVICE_NAME, translationService); - + ServiceRegistry.register(StringService.SERVICE_NAME, stringService); await translationService.initialize(); @@ -217,7 +219,8 @@ const startApp = async () => { ServiceRegistry.get(MongoDB.SERVICE_NAME), ServiceRegistry.get(SettingsService.SERVICE_NAME), ServiceRegistry.get(EmailService.SERVICE_NAME), - ServiceRegistry.get(JobQueue.SERVICE_NAME) + ServiceRegistry.get(JobQueue.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) ); const monitorController = new MonitorController( @@ -279,7 +282,7 @@ const startApp = async () => { app.use(cors()); app.use(express.json()); app.use(helmet()); - app.use(languageMiddleware); + app.use(languageMiddleware(stringService, translationService)); // Swagger UI app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec)); diff --git a/Server/middleware/languageMiddleware.js b/Server/middleware/languageMiddleware.js index 52e7b9a2e..11c2f2aec 100644 --- a/Server/middleware/languageMiddleware.js +++ b/Server/middleware/languageMiddleware.js @@ -1,7 +1,9 @@ -const languageMiddleware = (req, res, next) => { +const languageMiddleware = (stringService, translationService) => (req, res, next) => { const acceptLanguage = req.headers['accept-language'] || 'en'; + const language = acceptLanguage.split(',')[0].slice(0, 2).toLowerCase(); - req.language = acceptLanguage.split(',')[0].slice(0, 2).toLowerCase(); + translationService.setLanguage(language); + stringService.setLanguage(language); next(); }; diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 5e5f62721..f57abdd74 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -13,6 +13,8 @@ const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push"; */ class NetworkService { static SERVICE_NAME = SERVICE_NAME; + static POEDITOR_BASE_URL = 'https://api.poeditor.com/v2'; + constructor(axios, ping, logger, http, Docker, net) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; @@ -30,6 +32,17 @@ class NetworkService { this.http = http; this.Docker = Docker; this.net = net; + + this.apiToken = process.env.POEDITOR_API_TOKEN; + this.projectId = process.env.POEDITOR_PROJECT_ID; + + if (!this.apiToken || !this.projectId) { + this.logger.error({ + message: 'POEditor API token or project ID is missing in environment variables', + service: this.SERVICE_NAME, + method: 'constructor' + }); + } } /** @@ -398,6 +411,95 @@ class NetworkService { return this.handleUnsupportedType(type); } } + + async getPoEditorLanguages() { + try { + const params = new URLSearchParams(); + params.append('api_token', this.apiToken); + params.append('id', this.projectId); + + const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/languages/list`, params, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + return response.data.result.languages.map(lang => lang.code); + } catch (error) { + error.service = this.SERVICE_NAME; + error.method = "getPoEditorLanguages"; + throw error; + } + } + + async exportPoEditorTranslations(language) { + try { + const params = new URLSearchParams(); + params.append('api_token', this.apiToken); + params.append('id', this.projectId); + params.append('language', language); + params.append('type', 'key_value_json'); + + const exportResponse = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/projects/export`, params, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + const { url } = exportResponse.data.result; + const translationsResponse = await this.axios.get(url); + return translationsResponse.data; + } catch (error) { + error.service = this.SERVICE_NAME; + error.method = "exportPoEditorTranslations"; + throw error; + } + } + + async getPoEditorTerms() { + try { + const params = new URLSearchParams(); + params.append('api_token', this.apiToken); + params.append('id', this.projectId); + + const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/terms/list`, params, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + return response.data.result?.terms?.map(term => term.term.trim()) || []; + } catch (error) { + error.service = this.SERVICE_NAME; + error.method = "getPoEditorTerms"; + throw error; + } + } + + async addPoEditorTerms(terms) { + try { + const formattedTerms = terms.map(termObj => ({ + term: Object.keys(termObj)[0] + })); + + const params = new URLSearchParams(); + params.append('api_token', this.apiToken); + params.append('id', this.projectId); + params.append('data', JSON.stringify(formattedTerms)); + + const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/terms/add`, params, { + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + } + }); + + return response.data; + } catch (error) { + error.service = this.SERVICE_NAME; + error.method = "addPoEditorTerms"; + throw error; + } + } } export default NetworkService; diff --git a/Server/service/stringService.js b/Server/service/stringService.js new file mode 100644 index 000000000..3c95dacd9 --- /dev/null +++ b/Server/service/stringService.js @@ -0,0 +1,322 @@ +class StringService { + static SERVICE_NAME = "StringService"; + + constructor(translationService) { + if (StringService.instance) { + return StringService.instance; + } + + this.translationService = translationService; + this._language = 'en'; // default language + StringService.instance = this; + } + + setLanguage(language) { + this._language = language; + } + + get language() { + return this._language; + } + + // Auth Messages + get dontHaveAccount() { + return this.translationService.getTranslation('dontHaveAccount'); + } + + get email() { + return this.translationService.getTranslation('email'); + } + + get forgotPassword() { + return this.translationService.getTranslation('forgotPassword'); + } + + get password() { + return this.translationService.getTranslation('password'); + } + + get signUp() { + return this.translationService.getTranslation('signUp'); + } + + get submit() { + return this.translationService.getTranslation('submit'); + } + + get title() { + return this.translationService.getTranslation('title'); + } + + get continue() { + return this.translationService.getTranslation('continue'); + } + + get enterEmail() { + return this.translationService.getTranslation('enterEmail'); + } + + get authLoginTitle() { + return this.translationService.getTranslation('authLoginTitle'); + } + + get authLoginEnterPassword() { + return this.translationService.getTranslation('authLoginEnterPassword'); + } + + get commonPassword() { + return this.translationService.getTranslation('commonPassword'); + } + + get commonBack() { + return this.translationService.getTranslation('commonBack'); + } + + get authForgotPasswordTitle() { + return this.translationService.getTranslation('authForgotPasswordTitle'); + } + + get authForgotPasswordResetPassword() { + return this.translationService.getTranslation('authForgotPasswordResetPassword'); + } + + get createPassword() { + return this.translationService.getTranslation('createPassword'); + } + + get createAPassword() { + return this.translationService.getTranslation('createAPassword'); + } + + get authRegisterAlreadyHaveAccount() { + return this.translationService.getTranslation('authRegisterAlreadyHaveAccount'); + } + + get commonAppName() { + return this.translationService.getTranslation('commonAppName'); + } + + get authLoginEnterEmail() { + return this.translationService.getTranslation('authLoginEnterEmail'); + } + + get authRegisterTitle() { + return this.translationService.getTranslation('authRegisterTitle'); + } + + get monitorGetAll() { + return this.translationService.getTranslation('monitorGetAll'); + } + + get monitorGetById() { + return this.translationService.getTranslation('monitorGetById'); + } + + get monitorCreate() { + return this.translationService.getTranslation('monitorCreate'); + } + + get monitorEdit() { + return this.translationService.getTranslation('monitorEdit'); + } + + get monitorDelete() { + return this.translationService.getTranslation('monitorDelete'); + } + + get monitorPause() { + return this.translationService.getTranslation('monitorPause'); + } + + get monitorResume() { + return this.translationService.getTranslation('monitorResume'); + } + + get monitorDemoAdded() { + return this.translationService.getTranslation('monitorDemoAdded'); + } + + get monitorStatsById() { + return this.translationService.getTranslation('monitorStatsById'); + } + + get monitorCertificate() { + return this.translationService.getTranslation('monitorCertificate'); + } + + // Maintenance Window Messages + get maintenanceWindowCreate() { + return this.translationService.getTranslation('maintenanceWindowCreate'); + } + + get maintenanceWindowGetById() { + return this.translationService.getTranslation('maintenanceWindowGetById'); + } + + get maintenanceWindowGetByTeam() { + return this.translationService.getTranslation('maintenanceWindowGetByTeam'); + } + + get maintenanceWindowDelete() { + return this.translationService.getTranslation('maintenanceWindowDelete'); + } + + get maintenanceWindowEdit() { + return this.translationService.getTranslation('maintenanceWindowEdit'); + } + + // Error Messages + get unknownError() { + return this.translationService.getTranslation('unknownError'); + } + + get friendlyError() { + return this.translationService.getTranslation('friendlyError'); + } + + get authIncorrectPassword() { + return this.translationService.getTranslation('authIncorrectPassword'); + } + + get unauthorized() { + return this.translationService.getTranslation('unauthorized'); + } + + get authAdminExists() { + return this.translationService.getTranslation('authAdminExists'); + } + + get authInviteNotFound() { + return this.translationService.getTranslation('authInviteNotFound'); + } + + get unknownService() { + return this.translationService.getTranslation('unknownService'); + } + + get noAuthToken() { + return this.translationService.getTranslation('noAuthToken'); + } + + get invalidAuthToken() { + return this.translationService.getTranslation('invalidAuthToken'); + } + + get expiredAuthToken() { + return this.translationService.getTranslation('expiredAuthToken'); + } + + // Queue Messages + get queueGetMetrics() { + return this.translationService.getTranslation('queueGetMetrics'); + } + + get queueAddJob() { + return this.translationService.getTranslation('queueAddJob'); + } + + get queueObliterate() { + return this.translationService.getTranslation('queueObliterate'); + } + + // Job Queue Messages + get jobQueueDeleteJobSuccess() { + return this.translationService.getTranslation('jobQueueDeleteJobSuccess'); + } + + get jobQueuePauseJob() { + return this.translationService.getTranslation('jobQueuePauseJob'); + } + + get jobQueueResumeJob() { + return this.translationService.getTranslation('jobQueueResumeJob'); + } + + // Status Page Messages + get statusPageByUrl() { + return this.translationService.getTranslation('statusPageByUrl'); + } + + get statusPageCreate() { + return this.translationService.getTranslation('statusPageCreate'); + } + + get statusPageNotFound() { + return this.translationService.getTranslation('statusPageNotFound'); + } + + get statusPageUrlNotUnique() { + return this.translationService.getTranslation('statusPageUrlNotUnique'); + } + + // Docker Messages + get dockerFail() { + return this.translationService.getTranslation('dockerFail'); + } + + get dockerNotFound() { + return this.translationService.getTranslation('dockerNotFound'); + } + + get dockerSuccess() { + return this.translationService.getTranslation('dockerSuccess'); + } + + // Port Messages + get portFail() { + return this.translationService.getTranslation('portFail'); + } + + get portSuccess() { + return this.translationService.getTranslation('portSuccess'); + } + + // Alert Messages + get alertCreate() { + return this.translationService.getTranslation('alertCreate'); + } + + get alertGetByUser() { + return this.translationService.getTranslation('alertGetByUser'); + } + + get alertGetByMonitor() { + return this.translationService.getTranslation('alertGetByMonitor'); + } + + get alertGetById() { + return this.translationService.getTranslation('alertGetById'); + } + + get alertEdit() { + return this.translationService.getTranslation('alertEdit'); + } + + get alertDelete() { + return this.translationService.getTranslation('alertDelete'); + } + + getDeletedCount(count) { + return this.translationService.getTranslation('deletedCount') + .replace('{count}', count); + } + + get pingSuccess() { + return this.translationService.getTranslation('pingSuccess'); + } + + get getAppSettings() { + return this.translationService.getTranslation('getAppSettings'); + } + + get updateAppSettings() { + return this.translationService.getTranslation('updateAppSettings'); + } + + getDbFindMonitorById(monitorId) { + return this.translationService.getTranslation('dbFindMonitorById') + .replace('${monitorId}', monitorId); + } +} + +export default StringService; \ No newline at end of file diff --git a/Server/service/translationService.js b/Server/service/translationService.js index 5da13b45b..9c21d9b34 100644 --- a/Server/service/translationService.js +++ b/Server/service/translationService.js @@ -1,27 +1,34 @@ -import axios from 'axios'; import fs from 'fs'; import path from 'path'; -import { formattedKey } from '../utils/formattedKey.js'; class TranslationService { static SERVICE_NAME = 'TranslationService'; - constructor(logger) { + constructor(logger, networkService) { this.logger = logger; + this.networkService = networkService; this.translations = {}; - this.apiToken = process.env.POEDITOR_API_TOKEN; - this.projectId = process.env.POEDITOR_PROJECT_ID; - this.baseUrl = 'https://api.poeditor.com/v2'; + this._language = 'en'; this.localesDir = path.join(process.cwd(), 'locales'); } + setLanguage(language) { + this._language = language; + } + + get language() { + return this._language; + } + async initialize() { try { const loadedFromFiles = await this.loadFromFiles(); - if (!loadedFromFiles) { await this.loadTranslations(); } + + // Yeni eklenen terimleri POEditor'e gönder + await this.syncTermsWithPOEditor(); } catch (error) { this.logger.error({ message: error.message, @@ -70,16 +77,47 @@ class TranslationService { } async loadTranslations() { + let hasError = false; try { const languages = await this.getLanguages(); for (const language of languages) { - const translations = await this.exportTranslations(language); - this.translations[language] = translations; + try { + const translations = await this.exportTranslations(language); + this.translations[language] = translations; + } catch (error) { + hasError = true; + this.logger.error({ + message: `Failed to fetch translations from POEditor for language ${language}: ${error.message}`, + service: 'TranslationService', + method: 'loadTranslations', + stack: error.stack + }); + } + } + + if (hasError || Object.keys(this.translations[this._language]).length === 0) { + this.logger.error({ + message: 'Failed to fetch translations from POEditor, using locales_en.json', + service: 'TranslationService', + method: 'loadTranslations' + }); + + + // Load translations from locales_en.json in utils directory + const utilsPath = path.join(process.cwd(), 'utils'); + const utilsFilePath = path.join(utilsPath, 'locales_en.json'); + if (fs.existsSync(utilsFilePath)) { + const content = fs.readFileSync(utilsFilePath, 'utf8'); + this.translations['en'] = JSON.parse(content); + } else { + throw new Error('locales_en.json file not found'); + } } await this.saveTranslations(); } catch (error) { + hasError = true; this.logger.error({ message: error.message, service: 'TranslationService', @@ -91,17 +129,7 @@ class TranslationService { async getLanguages() { try { - const params = new URLSearchParams(); - params.append('api_token', this.apiToken); - params.append('id', this.projectId); - - const response = await axios.post(`${this.baseUrl}/languages/list`, params, { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }); - - return response.data.result.languages.map(lang => lang.code); + return await this.networkService.getPoEditorLanguages(); } catch (error) { this.logger.error({ message: error.message, @@ -115,22 +143,7 @@ class TranslationService { async exportTranslations(language) { try { - const params = new URLSearchParams(); - params.append('api_token', this.apiToken); - params.append('id', this.projectId); - params.append('language', language); - params.append('type', 'key_value_json'); - - const exportResponse = await axios.post(`${this.baseUrl}/projects/export`, params, { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }); - - const { url } = exportResponse.data.result; - - const translationsResponse = await axios.get(url); - return translationsResponse.data; + return await this.networkService.exportPoEditorTranslations(language); } catch (error) { this.logger.error({ message: error.message, @@ -153,6 +166,11 @@ class TranslationService { fs.writeFileSync(filePath, JSON.stringify(translations, null, 2)); } + const utilsPath = path.join(process.cwd(), 'utils'); + const enTranslations = this.translations['en'] || {}; + const utilsFilePath = path.join(utilsPath, 'locales_en.json'); + fs.writeFileSync(utilsFilePath, JSON.stringify(enTranslations, null, 2)); + this.logger.info({ message: 'Translations saved to files successfully', service: 'TranslationService', @@ -168,10 +186,11 @@ class TranslationService { } } - getTranslation(key, language = 'en') { - const formattedKeyText = formattedKey(key); + getTranslation(key) { + let language = this._language; + try { - return this.translations[language]?.[formattedKeyText] || this.translations['en']?.[formattedKeyText] || formattedKeyText; + return this.translations[language]?.[key] || this.translations['en']?.[key] || key; } catch (error) { this.logger.error({ message: error.message, @@ -182,6 +201,94 @@ class TranslationService { return key; } } + + async getTermsFromPOEditor() { + try { + return await this.networkService.getPoEditorTerms(); + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'getTermsFromPOEditor', + stack: error.stack + }); + return []; + } + } + + async addTermsToPOEditor(terms) { + try { + if (!terms.length) return; + + const response = await this.networkService.addPoEditorTerms(terms); + + if (response.response?.status === 'fail') { + throw new Error(response.response.message || 'Failed to add terms to POEditor'); + } + + this.logger.info({ + message: `${terms.length} new terms added to POEditor`, + service: 'TranslationService', + method: 'addTermsToPOEditor', + response: response + }); + + return response; + } catch (error) { + this.logger.error({ + message: `Failed to add terms to POEditor: ${error.message}`, + service: 'TranslationService', + method: 'addTermsToPOEditor', + stack: error.stack, + terms: terms + }); + throw error; + } + } + + async syncTermsWithPOEditor() { + try { + const utilsPath = path.join(process.cwd(), 'utils'); + const utilsFilePath = path.join(utilsPath, 'locales_en.json'); + const enTranslations = JSON.parse(fs.readFileSync(utilsFilePath, 'utf8')); + const localTerms = Object.keys(enTranslations) + .map(term => term); + + const poeditorTerms = await this.getTermsFromPOEditor(); + + const newTerms = localTerms?.filter(term => !poeditorTerms?.includes(term)); + + + this.logger.info({ + message: `Comparison results - New terms found: ${newTerms.length}`, + sampleNewTerms: newTerms.slice(0, 5), + service: 'TranslationService', + method: 'syncTermsWithPOEditor' + }); + + if (newTerms.length > 0) { + const formattedTerms = newTerms.map(term => ({ + [term]: enTranslations[term] || '', + })); + + await this.addTermsToPOEditor(formattedTerms); + + } else { + this.logger.info({ + message: 'No new terms found to synchronize', + service: 'TranslationService', + method: 'syncTermsWithPOEditor' + }); + } + } catch (error) { + this.logger.error({ + message: error.message, + service: 'TranslationService', + method: 'syncTermsWithPOEditor', + stack: error.stack + }); + } + } } export default TranslationService; \ No newline at end of file diff --git a/Server/tests/controllers/queueController.test.js b/Server/tests/controllers/queueController.test.js index b04504186..72450a0bd 100644 --- a/Server/tests/controllers/queueController.test.js +++ b/Server/tests/controllers/queueController.test.js @@ -8,10 +8,10 @@ import { import { successMessages } from "../../utils/messages.js"; import sinon from "sinon"; -describe("Queue Controller - getMetrics", function() { +describe("Queue Controller - getMetrics", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { headers: {}, params: {}, @@ -32,14 +32,14 @@ describe("Queue Controller - getMetrics", function() { sinon.restore(); }); - it("should throw an error if getMetrics throws an error", async function() { + it("should throw an error if getMetrics throws an error", async function () { req.jobQueue.getMetrics.throws(new Error("getMetrics error")); await getMetrics(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getMetrics error"); }); - it("should return a success message and data if getMetrics is successful", async function() { + it("should return a success message and data if getMetrics is successful", async function () { const data = { data: "metrics" }; req.jobQueue.getMetrics.returns(data); await getMetrics(req, res, next); @@ -52,10 +52,10 @@ describe("Queue Controller - getMetrics", function() { }); }); -describe("Queue Controller - getJobs", function() { +describe("Queue Controller - getJobs", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { headers: {}, params: {}, @@ -76,14 +76,14 @@ describe("Queue Controller - getJobs", function() { sinon.restore(); }); - it("should reject with an error if getJobs throws an error", async function() { + it("should reject with an error if getJobs throws an error", async function () { req.jobQueue.getJobStats.throws(new Error("getJobs error")); await getJobs(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getJobs error"); }); - it("should return a success message and data if getJobs is successful", async function() { + it("should return a success message and data if getJobs is successful", async function () { const data = { data: "jobs" }; req.jobQueue.getJobStats.returns(data); await getJobs(req, res, next); @@ -96,10 +96,10 @@ describe("Queue Controller - getJobs", function() { }); }); -describe("Queue Controller - addJob", function() { +describe("Queue Controller - addJob", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { headers: {}, params: {}, @@ -120,14 +120,14 @@ describe("Queue Controller - addJob", function() { sinon.restore(); }); - it("should reject with an error if addJob throws an error", async function() { + it("should reject with an error if addJob throws an error", async function () { req.jobQueue.addJob.throws(new Error("addJob error")); await addJob(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("addJob error"); }); - it("should return a success message if addJob is successful", async function() { + it("should return a success message if addJob is successful", async function () { req.jobQueue.addJob.resolves(); await addJob(req, res, next); expect(res.status.firstCall.args[0]).to.equal(200); @@ -138,10 +138,10 @@ describe("Queue Controller - addJob", function() { }); }); -describe("Queue Controller - obliterateQueue", function() { +describe("Queue Controller - obliterateQueue", function () { let req, res, next; - beforeEach(function() { + beforeEach(function () { req = { headers: {}, params: {}, @@ -162,14 +162,14 @@ describe("Queue Controller - obliterateQueue", function() { sinon.restore(); }); - it("should reject with an error if obliterateQueue throws an error", async function() { + it("should reject with an error if obliterateQueue throws an error", async function () { req.jobQueue.obliterate.throws(new Error("obliterateQueue error")); await obliterateQueue(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("obliterateQueue error"); }); - it("should return a success message if obliterateQueue is successful", async function() { + it("should return a success message if obliterateQueue is successful", async function () { req.jobQueue.obliterate.resolves(); await obliterateQueue(req, res, next); expect(res.status.firstCall.args[0]).to.equal(200); diff --git a/Server/utils/formattedKey.js b/Server/utils/formattedKey.js deleted file mode 100644 index b60e6783b..000000000 --- a/Server/utils/formattedKey.js +++ /dev/null @@ -1,14 +0,0 @@ -/** - * Converts a snake_case or SCREAMING_SNAKE_CASE key to camelCase - * Example: AUTH_INCORRECT_PASSWORD -> authIncorrectPassword - * @param {string} key - The key to format - * @returns {string} - The formatted key in camelCase - */ -export const formattedKey = (key) => { - return key.toLowerCase() - .split('_') - .map((word, index) => - index === 0 ? word : word.charAt(0).toUpperCase() + word.slice(1) - ) - .join(''); -}; \ No newline at end of file diff --git a/Server/utils/locales_en.json b/Server/utils/locales_en.json new file mode 100644 index 000000000..58eb0cf49 --- /dev/null +++ b/Server/utils/locales_en.json @@ -0,0 +1,143 @@ +{ + "dontHaveAccount": "Don't have account", + "email": "E-mail", + "forgotPassword": "Forgot Password", + "password": "password", + "signUp": "Sign up", + "submit": "Submit", + "title": "Title", + "continue": "Continue", + "enterEmail": "Enter your email", + "authLoginTitle": "Log In", + "authLoginEnterPassword": "Enter your password", + "commonPassword": "Password", + "commonBack": "Back", + "authForgotPasswordTitle": "Forgot password?", + "authForgotPasswordResetPassword": "Reset password", + "createPassword": "Create your password", + "createAPassword": "Create a password", + "authRegisterAlreadyHaveAccount": "Already have an account?", + "commonAppName": "BlueWave Uptime", + "authLoginEnterEmail": "Enter your email", + "authRegisterTitle": "Create an account", + "authRegisterStepOneTitle": "Create your account", + "authRegisterStepOneDescription": "Enter your details to get started", + "authRegisterStepTwoTitle": "Set up your profile", + "authRegisterStepTwoDescription": "Tell us more about yourself", + "authRegisterStepThreeTitle": "Almost done!", + "authRegisterStepThreeDescription": "Review your information", + "authForgotPasswordDescription": "No worries, we'll send you reset instructions.", + "authForgotPasswordSendInstructions": "Send instructions", + "authForgotPasswordBackTo": "Back to", + "authCheckEmailTitle": "Check your email", + "authCheckEmailDescription": "We sent a password reset link to {{email}}", + "authCheckEmailResendEmail": "Resend email", + "authCheckEmailBackTo": "Back to", + "goBackTo": "Go back to", + "authCheckEmailDidntReceiveEmail": "Didn't receive the email?", + "authCheckEmailClickToResend": "Click to resend", + "authSetNewPasswordTitle": "Set new password", + "authSetNewPasswordDescription": "Your new password must be different from previously used passwords.", + "authSetNewPasswordNewPassword": "New password", + "authSetNewPasswordConfirmPassword": "Confirm password", + "confirmPassword": "Confirm your password", + "authSetNewPasswordResetPassword": "Reset password", + "authSetNewPasswordBackTo": "Back to", + "authPasswordMustBeAtLeast": "Must be at least", + "authPasswordCharactersLong": "8 characters long", + "authPasswordMustContainAtLeast": "Must contain at least", + "authPasswordSpecialCharacter": "one special character", + "authPasswordOneNumber": "one number", + "authPasswordUpperCharacter": "one upper character", + "authPasswordLowerCharacter": "one lower character", + "authPasswordConfirmAndPassword": "Confirm password and password", + "authPasswordMustMatch": "must match", + "friendlyError": "Something went wrong...", + "unknownError": "An unknown error occurred", + "unauthorized": "Unauthorized access", + "authAdminExists": "Admin already exists", + "authInviteNotFound": "Invite not found", + "unknownService": "Unknown service", + "noAuthToken": "No auth token provided", + "invalidAuthToken": "Invalid auth token", + "expiredAuthToken": "Token expired", + "noRefreshToken": "No refresh token provided", + "invalidRefreshToken": "Invalid refresh token", + "expiredRefreshToken": "Refresh token expired", + "requestNewAccessToken": "Request new access token", + "invalidPayload": "Invalid payload", + "verifyOwnerNotFound": "Document not found", + "verifyOwnerUnauthorized": "Unauthorized access", + "insufficientPermissions": "Insufficient permissions", + "dbUserExists": "User already exists", + "dbUserNotFound": "User not found", + "dbTokenNotFound": "Token not found", + "dbResetPasswordBadMatch": "New password must be different from old password", + "dbFindMonitorById": "Monitor with id ${monitorId} not found", + "dbDeleteChecks": "No checks found for monitor with id ${monitorId}", + "authIncorrectPassword": "Incorrect password", + "authUnauthorized": "Unauthorized access", + "monitorGetById": "Monitor not found", + "monitorGetByUserId": "No monitors found for user", + "jobQueueWorkerClose": "Error closing worker", + "jobQueueDeleteJob": "Job not found in queue", + "jobQueueObliterate": "Error obliterating queue", + "pingCannotResolve": "No response", + "statusPageNotFound": "Status page not found", + "statusPageUrlNotUnique": "Status page url must be unique", + "dockerFail": "Failed to fetch Docker container information", + "dockerNotFound": "Docker container not found", + "portFail": "Failed to connect to port", + "alertCreate": "Alert created successfully", + "alertGetByUser": "Got alerts successfully", + "alertGetByMonitor": "Got alerts by Monitor successfully", + "alertGetById": "Got alert by Id successfully", + "alertEdit": "Alert edited successfully", + "alertDelete": "Alert deleted successfully", + "authCreateUser": "User created successfully", + "authLoginUser": "User logged in successfully", + "authLogoutUser": "User logged out successfully", + "authUpdateUser": "User updated successfully", + "authCreateRecoveryToken": "Recovery token created successfully", + "authVerifyRecoveryToken": "Recovery token verified successfully", + "authResetPassword": "Password reset successfully", + "authAdminCheck": "Admin check completed successfully", + "authDeleteUser": "User deleted successfully", + "authTokenRefreshed": "Auth token is refreshed", + "authGetAllUsers": "Got all users successfully", + "inviteIssued": "Invite sent successfully", + "inviteVerified": "Invite verified successfully", + "checkCreate": "Check created successfully", + "checkGet": "Got checks successfully", + "checkDelete": "Checks deleted successfully", + "checkUpdateTtl": "Checks TTL updated successfully", + "monitorGetAll": "Got all monitors successfully", + "monitorStatsById": "Got monitor stats by Id successfully", + "monitorGetByIdSuccess": "Got monitor by Id successfully", + "monitorGetByTeamId": "Got monitors by Team Id successfully", + "monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully", + "monitorCreate": "Monitor created successfully", + "monitorDelete": "Monitor deleted successfully", + "monitorEdit": "Monitor edited successfully", + "monitorCertificate": "Got monitor certificate successfully", + "monitorDemoAdded": "Successfully added demo monitors", + "queueGetMetrics": "Got metrics successfully", + "queueAddJob": "Job added successfully", + "queueObliterate": "Queue obliterated", + "jobQueueDeleteJobSuccess": "Job removed successfully", + "jobQueuePauseJob": "Job paused successfully", + "jobQueueResumeJob": "Job resumed successfully", + "maintenanceWindowGetById": "Got Maintenance Window by Id successfully", + "maintenanceWindowCreate": "Maintenance Window created successfully", + "maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully", + "maintenanceWindowDelete": "Maintenance Window deleted successfully", + "maintenanceWindowEdit": "Maintenance Window edited successfully", + "pingSuccess": "Success", + "getAppSettings": "Got app settings successfully", + "updateAppSettings": "Updated app settings successfully", + "statusPageByUrl": "Got status page by url successfully", + "statusPageCreate": "Status page created successfully", + "newTermsAdded": "New terms added to POEditor", + "dockerSuccess": "Docker container status fetched successfully", + "portSuccess": "Port connected successfully" +} \ No newline at end of file From 1c344e564a6c1c4e9189e847f3317cd4e6ecf7a3 Mon Sep 17 00:00:00 2001 From: cihatata Date: Wed, 29 Jan 2025 02:01:35 +0300 Subject: [PATCH 15/52] fix: revert dockerfile --- Docker/dist/docker-compose.yaml | 17 ++++------------- 1 file changed, 4 insertions(+), 13 deletions(-) diff --git a/Docker/dist/docker-compose.yaml b/Docker/dist/docker-compose.yaml index db8bc5c07..3a23f70b1 100644 --- a/Docker/dist/docker-compose.yaml +++ b/Docker/dist/docker-compose.yaml @@ -1,9 +1,6 @@ services: client: - build: - context: ../../ - dockerfile: Docker/dist/client.Dockerfile - restart: always + image: bluewaveuptime/uptime_client:latest environment: UPTIME_APP_API_BASE_URL: "http://localhost:5000/api/v1" UPTIME_STATUS_PAGE_SUBDOMAIN_PREFIX: "http://uptimegenie.com/" @@ -13,9 +10,7 @@ services: depends_on: - server server: - build: - context: ../../ - dockerfile: Docker/dist/server.Dockerfile + image: bluewaveuptime/uptime_server:latest restart: always ports: - "5000:5000" @@ -28,9 +23,7 @@ services: # volumes: # - /var/run/docker.sock:/var/run/docker.sock:ro redis: - build: - context: ../../ - dockerfile: Docker/dist/redis.Dockerfile + image: bluewaveuptime/uptime_redis:latest restart: always ports: - "6379:6379" @@ -43,9 +36,7 @@ services: retries: 5 start_period: 5s mongodb: - build: - context: ../../ - dockerfile: Docker/dist/mongoDB.Dockerfile + image: bluewaveuptime/uptime_database_mongo:latest restart: always volumes: - ./mongo/data:/data/db From 301208d7995ca8abb7aa2a18478eddb2a1404948 Mon Sep 17 00:00:00 2001 From: cihatata Date: Wed, 29 Jan 2025 02:02:29 +0300 Subject: [PATCH 16/52] fix: revert dockerfile --- Docker/dist/docker-compose.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/Docker/dist/docker-compose.yaml b/Docker/dist/docker-compose.yaml index 3a23f70b1..420941e7e 100644 --- a/Docker/dist/docker-compose.yaml +++ b/Docker/dist/docker-compose.yaml @@ -1,6 +1,7 @@ services: client: image: bluewaveuptime/uptime_client:latest + restart: always environment: UPTIME_APP_API_BASE_URL: "http://localhost:5000/api/v1" UPTIME_STATUS_PAGE_SUBDOMAIN_PREFIX: "http://uptimegenie.com/" From a98a3dd7a39d6a0c31fcc43c49a382989030864b Mon Sep 17 00:00:00 2001 From: cihatata Date: Wed, 29 Jan 2025 16:27:10 +0300 Subject: [PATCH 17/52] feat: authController i18n --- Server/controllers/authController.js | 24 +++++++++++++++--------- Server/index.js | 1 - 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index b5c21d619..332e3451f 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -8,20 +8,21 @@ import { newPasswordValidation, } from "../validation/joi.js"; import logger from "../utils/logger.js"; -import { errorMessages, successMessages } from "../utils/messages.js"; +import { successMessages } from "../utils/messages.js"; import jwt from "jsonwebtoken"; import { getTokenFromHeaders, tokenType } from "../utils/utils.js"; import crypto from "crypto"; import { handleValidationError, handleError } from "./controllerUtils.js"; const SERVICE_NAME = "authController"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; class AuthController { - constructor(db, settingsService, emailService, jobQueue, stringService) { + constructor(db, settingsService, emailService, jobQueue) { this.db = db; this.settingsService = settingsService; this.emailService = emailService; this.jobQueue = jobQueue; - this.stringService = stringService; } /** @@ -138,6 +139,7 @@ class AuthController { * @throws {Error} If there is an error during the process, especially if there is a validation error (422) or the password is incorrect. */ loginUser = async (req, res, next) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { await loginValidation.validateAsync(req.body); } catch (error) { @@ -154,7 +156,7 @@ class AuthController { // Compare password const match = await user.comparePassword(password); if (match !== true) { - const error = new Error(this.stringService.authIncorrectPassword); + const error = new Error(stringService.authIncorrectPassword); error.status = 401; next(error); return; @@ -201,13 +203,15 @@ class AuthController { * @throws {Error} If there is an error during the process such as any of the token is not received */ refreshAuthToken = async (req, res, next) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + try { // check for refreshToken const refreshToken = req.headers["x-refresh-token"]; if (!refreshToken) { // No refresh token provided - const error = new Error(this.stringService.noRefreshToken); + const error = new Error(stringService.noRefreshToken); error.status = 401; error.service = SERVICE_NAME; error.method = "refreshAuthToken"; @@ -222,8 +226,8 @@ class AuthController { // Invalid or expired refresh token, trigger logout const errorMessage = refreshErr.name === "TokenExpiredError" - ? this.stringService.expiredAuthToken - : this.stringService.invalidAuthToken; + ? stringService.expiredAuthToken + : stringService.invalidAuthToken; const error = new Error(errorMessage); error.status = 401; error.service = SERVICE_NAME; @@ -266,6 +270,8 @@ class AuthController { * @throws {Error} If there is an error during the process, especially if there is a validation error (422), the user is unauthorized (401), or the password is incorrect (403). */ editUser = async (req, res, next) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + try { await editUserParamValidation.validateAsync(req.params); await editUserBodyValidation.validateAsync(req.body); @@ -277,7 +283,7 @@ class AuthController { // TODO is this neccessary any longer? Verify ownership middleware should handle this if (req.params.userId !== req.user._id.toString()) { - const error = new Error(this.stringService.unauthorized); + const error = new Error(stringService.unauthorized); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -301,7 +307,7 @@ class AuthController { // If not a match, throw a 403 // 403 instead of 401 to avoid triggering axios interceptor if (!match) { - const error = new Error(this.stringService.authIncorrectPassword); + const error = new Error(stringService.authIncorrectPassword); error.status = 403; next(error); return; diff --git a/Server/index.js b/Server/index.js index 8bd4be260..7aa5124f6 100644 --- a/Server/index.js +++ b/Server/index.js @@ -220,7 +220,6 @@ const startApp = async () => { ServiceRegistry.get(SettingsService.SERVICE_NAME), ServiceRegistry.get(EmailService.SERVICE_NAME), ServiceRegistry.get(JobQueue.SERVICE_NAME), - ServiceRegistry.get(StringService.SERVICE_NAME) ); const monitorController = new MonitorController( From 88df88891ce1463dcce779cc45e545919445d2e3 Mon Sep 17 00:00:00 2001 From: cihatata Date: Thu, 30 Jan 2025 00:41:31 +0300 Subject: [PATCH 18/52] fix: use new structure --- Docker/dist/docker-compose.yaml | 2 + Server/controllers/authController.js | 51 +++++++++---------- Server/controllers/checkController.js | 16 +++--- Server/controllers/inviteController.js | 11 ++-- .../maintenanceWindowController.js | 17 ++++--- Server/controllers/monitorController.js | 32 ++++++------ Server/controllers/queueController.js | 12 +++-- Server/controllers/settingsController.js | 9 ++-- Server/controllers/statusPageController.js | 5 +- Server/db/mongo/modules/inviteModule.js | 13 +++-- Server/db/mongo/modules/monitorModule.js | 45 +++++++++------- Server/db/mongo/modules/recoveryModule.js | 11 ++-- Server/db/mongo/modules/statusPageModule.js | 2 +- Server/db/mongo/modules/userModule.js | 18 ++++--- Server/middleware/handleErrors.js | 8 +-- Server/middleware/isAllowed.js | 10 ++-- Server/middleware/verifyJWT.js | 18 ++++--- Server/middleware/verifyOwnership.js | 12 +++-- Server/middleware/verifySuperAdmin.js | 15 +++--- Server/service/jobQueue.js | 11 ++-- Server/service/networkService.js | 17 ++++--- Server/utils/locales_en.json | 4 +- 22 files changed, 193 insertions(+), 146 deletions(-) diff --git a/Docker/dist/docker-compose.yaml b/Docker/dist/docker-compose.yaml index 420941e7e..1bf579fbb 100644 --- a/Docker/dist/docker-compose.yaml +++ b/Docker/dist/docker-compose.yaml @@ -21,6 +21,8 @@ services: environment: - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db - REDIS_HOST=redis + - POEDITOR_API_TOKEN= + - POEDITOR_PROJECT_ID= # volumes: # - /var/run/docker.sock:/var/run/docker.sock:ro redis: diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index 332e3451f..ce4f7b785 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -8,7 +8,6 @@ import { newPasswordValidation, } from "../validation/joi.js"; import logger from "../utils/logger.js"; -import { successMessages } from "../utils/messages.js"; import jwt from "jsonwebtoken"; import { getTokenFromHeaders, tokenType } from "../utils/utils.js"; import crypto from "crypto"; @@ -23,6 +22,7 @@ class AuthController { this.settingsService = settingsService; this.emailService = emailService; this.jobQueue = jobQueue; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); } /** @@ -78,7 +78,7 @@ class AuthController { // If superAdmin exists, a token should be attached to all further register requests const superAdminExists = await this.db.checkSuperadmin(req, res); if (superAdminExists) { - await this.db.getInviteTokenAndDelete(inviteToken, req.language); + await this.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"); @@ -87,7 +87,7 @@ class AuthController { const newUser = await this.db.insertUser({ ...req.body }, req.file); logger.info({ - message: successMessages.AUTH_CREATE_USER(req.language), + message: this.stringService.authCreateUser, service: SERVICE_NAME, details: newUser._id, }); @@ -118,7 +118,7 @@ class AuthController { }); res.success({ - msg: successMessages.AUTH_CREATE_USER(req.language), + msg: this.stringService.authCreateUser, data: { user: newUser, token: token, refreshToken: refreshToken }, }); } catch (error) { @@ -139,7 +139,6 @@ class AuthController { * @throws {Error} If there is an error during the process, especially if there is a validation error (422) or the password is incorrect. */ loginUser = async (req, res, next) => { - const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { await loginValidation.validateAsync(req.body); } catch (error) { @@ -151,12 +150,12 @@ class AuthController { const { email, password } = req.body; // Check if user exists - const user = await this.db.getUserByEmail(email, req.language); + const user = await this.db.getUserByEmail(email); // Compare password const match = await user.comparePassword(password); if (match !== true) { - const error = new Error(stringService.authIncorrectPassword); + const error = new Error(this.stringService.authIncorrectPassword); error.status = 401; next(error); return; @@ -179,7 +178,7 @@ class AuthController { userWithoutPassword.avatarImage = user.avatarImage; return res.success({ - msg: successMessages.AUTH_LOGIN_USER(req.language), + msg: this.stringService.authLoginUser, data: { user: userWithoutPassword, token: token, @@ -203,7 +202,6 @@ class AuthController { * @throws {Error} If there is an error during the process such as any of the token is not received */ refreshAuthToken = async (req, res, next) => { - const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { // check for refreshToken @@ -211,7 +209,7 @@ class AuthController { if (!refreshToken) { // No refresh token provided - const error = new Error(stringService.noRefreshToken); + const error = new Error(this.stringService.noRefreshToken); error.status = 401; error.service = SERVICE_NAME; error.method = "refreshAuthToken"; @@ -226,8 +224,8 @@ class AuthController { // Invalid or expired refresh token, trigger logout const errorMessage = refreshErr.name === "TokenExpiredError" - ? stringService.expiredAuthToken - : stringService.invalidAuthToken; + ? this.stringService.expiredAuthToken + : this.stringService.invalidAuthToken; const error = new Error(errorMessage); error.status = 401; error.service = SERVICE_NAME; @@ -248,7 +246,7 @@ class AuthController { ); return res.success({ - msg: successMessages.AUTH_TOKEN_REFRESHED(req.language), + msg: this.stringService.authTokenRefreshed, data: { user: payloadData, token: newAuthToken, refreshToken: refreshToken }, }); } catch (error) { @@ -270,7 +268,6 @@ class AuthController { * @throws {Error} If there is an error during the process, especially if there is a validation error (422), the user is unauthorized (401), or the password is incorrect (403). */ editUser = async (req, res, next) => { - const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { await editUserParamValidation.validateAsync(req.params); @@ -283,7 +280,7 @@ class AuthController { // TODO is this neccessary any longer? Verify ownership middleware should handle this if (req.params.userId !== req.user._id.toString()) { - const error = new Error(stringService.unauthorized); + const error = new Error(this.stringService.unauthorized); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -301,13 +298,13 @@ class AuthController { // Add user email to body for DB operation req.body.email = email; // Get user - const user = await this.db.getUserByEmail(email, req.language); + const user = await this.db.getUserByEmail(email); // Compare passwords const match = await user.comparePassword(req.body.password); // If not a match, throw a 403 // 403 instead of 401 to avoid triggering axios interceptor if (!match) { - const error = new Error(stringService.authIncorrectPassword); + const error = new Error(this.stringService.authIncorrectPassword); error.status = 403; next(error); return; @@ -318,7 +315,7 @@ class AuthController { const updatedUser = await this.db.updateUser(req, res); res.success({ - msg: successMessages.AUTH_UPDATE_USER(req.language), + msg: this.stringService.authUpdateUser, data: updatedUser, }); } catch (error) { @@ -340,7 +337,7 @@ class AuthController { const superAdminExists = await this.db.checkSuperadmin(req, res); return res.success({ - msg: successMessages.AUTH_ADMIN_EXISTS(req.language), + msg: this.stringService.authAdminExists, data: superAdminExists, }); } catch (error) { @@ -369,7 +366,7 @@ class AuthController { try { const { email } = req.body; - const user = await this.db.getUserByEmail(email, req.language); + const user = await this.db.getUserByEmail(email); const recoveryToken = await this.db.requestRecoveryToken(req, res); const name = user.firstName; const { clientHost } = this.settingsService.getSettings(); @@ -386,7 +383,7 @@ class AuthController { ); return res.success({ - msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN(req.language), + msg: this.stringService.authCreateRecoveryToken, data: msgId, }); } catch (error) { @@ -417,7 +414,7 @@ class AuthController { await this.db.validateRecoveryToken(req, res); return res.success({ - msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN(req.language), + msg: this.stringService.authVerifyRecoveryToken, }); } catch (error) { next(handleError(error, SERVICE_NAME, "validateRecoveryTokenController")); @@ -450,7 +447,7 @@ class AuthController { const token = this.issueToken(user._doc, tokenType.ACCESS_TOKEN, appSettings); return res.success({ - msg: successMessages.AUTH_RESET_PASSWORD(req.language), + msg: this.stringService.authResetPassword, data: { user, token }, }); } catch (error) { @@ -474,7 +471,7 @@ class AuthController { const { email } = decodedToken; // Check if the user exists - const user = await this.db.getUserByEmail(email, req.language); + const user = await this.db.getUserByEmail(email); // 1. Find all the monitors associated with the team ID if superadmin const result = await this.db.getMonitorsByTeamId({ @@ -501,10 +498,10 @@ class AuthController { await this.db.deleteMonitorsByUserId(user._id); } // 6. Delete the user by id - await this.db.deleteUser(user._id, req.language); + await this.db.deleteUser(user._id); return res.success({ - msg: successMessages.AUTH_DELETE_USER(req.language), + msg: this.stringService.authDeleteUser, }); } catch (error) { next(handleError(error, SERVICE_NAME, "deleteUserController")); @@ -516,7 +513,7 @@ class AuthController { const allUsers = await this.db.getAllUsers(req, res); return res.success({ - msg: successMessages.AUTH_GET_ALL_USERS(req.language), + msg: this.stringService.authGetAllUsers, data: allUsers, }); } catch (error) { diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js index 0f532dea0..a345212f3 100644 --- a/Server/controllers/checkController.js +++ b/Server/controllers/checkController.js @@ -9,10 +9,11 @@ import { deleteChecksByTeamIdParamValidation, updateChecksTTLBodyValidation, } from "../validation/joi.js"; -import { successMessages } from "../utils/messages.js"; import jwt from "jsonwebtoken"; import { getTokenFromHeaders } from "../utils/utils.js"; import { handleValidationError, handleError } from "./controllerUtils.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; const SERVICE_NAME = "checkController"; @@ -20,6 +21,7 @@ class CheckController { constructor(db, settingsService) { this.db = db; this.settingsService = settingsService; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); } createCheck = async (req, res, next) => { @@ -36,7 +38,7 @@ class CheckController { const check = await this.db.createCheck(checkData); return res.success({ - msg: successMessages.CHECK_CREATE(req.language), + msg: this.stringService.checkCreate, data: check, }); } catch (error) { @@ -57,7 +59,7 @@ class CheckController { const result = await this.db.getChecksByMonitor(req); return res.success({ - msg: successMessages.CHECK_GET(req.language), + msg: this.stringService.checkGet, data: result, }); } catch (error) { @@ -77,7 +79,7 @@ class CheckController { const checkData = await this.db.getChecksByTeam(req); return res.success({ - msg: successMessages.CHECK_GET(req.language), + msg: this.stringService.checkGet, data: checkData, }); } catch (error) { @@ -97,7 +99,7 @@ class CheckController { const deletedCount = await this.db.deleteChecks(req.params.monitorId); return res.success({ - msg: successMessages.CHECK_DELETE(req.language), + msg: this.stringService.checkDelete, data: { deletedCount }, }); } catch (error) { @@ -117,7 +119,7 @@ class CheckController { const deletedCount = await this.db.deleteChecksByTeamId(req.params.teamId); return res.success({ - msg: successMessages.CHECK_DELETE(req.language), + msg: this.stringService.checkDelete, data: { deletedCount }, }); } catch (error) { @@ -144,7 +146,7 @@ class CheckController { await this.db.updateChecksTTL(teamId, ttl); return res.success({ - msg: successMessages.CHECK_UPDATE_TTL(req.language), + msg: this.stringService.checkUpdateTTL, }); } catch (error) { next(handleError(error, SERVICE_NAME, "updateTTL")); diff --git a/Server/controllers/inviteController.js b/Server/controllers/inviteController.js index 6c5592f69..146eb34c2 100644 --- a/Server/controllers/inviteController.js +++ b/Server/controllers/inviteController.js @@ -7,7 +7,9 @@ import logger from "../utils/logger.js"; import jwt from "jsonwebtoken"; import { handleError, handleValidationError } from "./controllerUtils.js"; import { getTokenFromHeaders } from "../utils/utils.js"; -import { successMessages } from "../utils/messages.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; + const SERVICE_NAME = "inviteController"; class InviteController { @@ -15,6 +17,7 @@ class InviteController { this.db = db; this.settingsService = settingsService; this.emailService = emailService; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); } /** @@ -66,7 +69,7 @@ class InviteController { }); return res.success({ - msg: successMessages.INVITE_ISSUED(req.language), + msg: this.stringService.inviteIssued, data: inviteToken, }); } catch (error) { @@ -83,10 +86,10 @@ class InviteController { } try { - const invite = await this.db.getInviteToken(req.body.token, req.language); + const invite = await this.db.getInviteToken(req.body.token); return res.success({ - msg: successMessages.INVITE_VERIFIED(req.language), + msg: this.stringService.inviteVerified, data: invite, }); } catch (error) { diff --git a/Server/controllers/maintenanceWindowController.js b/Server/controllers/maintenanceWindowController.js index f6de3d479..a1a3395c8 100644 --- a/Server/controllers/maintenanceWindowController.js +++ b/Server/controllers/maintenanceWindowController.js @@ -9,14 +9,17 @@ import { } from "../validation/joi.js"; import jwt from "jsonwebtoken"; import { getTokenFromHeaders } from "../utils/utils.js"; -import { successMessages } from "../utils/messages.js"; import { handleValidationError, handleError } from "./controllerUtils.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; + const SERVICE_NAME = "maintenanceWindowController"; class MaintenanceWindowController { constructor(db, settingsService) { this.db = db; this.settingsService = settingsService; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); } createMaintenanceWindows = async (req, res, next) => { @@ -45,7 +48,7 @@ class MaintenanceWindowController { await Promise.all(dbTransactions); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_CREATE(req.language), + msg: this.stringService.maintenanceWindowCreate, }); } catch (error) { next(handleError(error, SERVICE_NAME, "createMaintenanceWindow")); @@ -63,7 +66,7 @@ class MaintenanceWindowController { const maintenanceWindow = await this.db.getMaintenanceWindowById(req.params.id); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID(req.language), + msg: this.stringService.maintenanceWindowGetById, data: maintenanceWindow, }); } catch (error) { @@ -89,7 +92,7 @@ class MaintenanceWindowController { ); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM(req.language), + msg: this.stringService.maintenanceWindowGetByTeam, data: maintenanceWindows, }); } catch (error) { @@ -111,7 +114,7 @@ class MaintenanceWindowController { ); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_USER(req.language), + msg: this.stringService.maintenanceWindowGetByUser, data: maintenanceWindows, }); } catch (error) { @@ -129,7 +132,7 @@ class MaintenanceWindowController { try { await this.db.deleteMaintenanceWindowById(req.params.id); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_DELETE(req.language), + msg: this.stringService.maintenanceWindowDelete, }); } catch (error) { next(handleError(error, SERVICE_NAME, "deleteMaintenanceWindow")); @@ -150,7 +153,7 @@ class MaintenanceWindowController { req.body ); return res.success({ - msg: successMessages.MAINTENANCE_WINDOW_EDIT(req.language), + msg: this.stringService.maintenanceWindowEdit, data: editedMaintenanceWindow, }); } catch (error) { diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index a9d140745..66d68c74b 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -14,13 +14,14 @@ import { getHardwareDetailsByIdQueryValidation, } from "../validation/joi.js"; import sslChecker from "ssl-checker"; -import { successMessages } from "../utils/messages.js"; import jwt from "jsonwebtoken"; import { getTokenFromHeaders } from "../utils/utils.js"; import logger from "../utils/logger.js"; import { handleError, handleValidationError } from "./controllerUtils.js"; import axios from "axios"; import seedDb from "../db/mongo/utils/seedDb.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; const SERVICE_NAME = "monitorController"; class MonitorController { @@ -28,6 +29,7 @@ class MonitorController { this.db = db; this.settingsService = settingsService; this.jobQueue = jobQueue; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); } /** @@ -43,7 +45,7 @@ class MonitorController { try { const monitors = await this.db.getAllMonitors(); return res.success({ - msg: successMessages.MONITOR_GET_ALL(req.language), + msg: this.stringService.monitorGetAll, data: monitors, }); } catch (error) { @@ -64,7 +66,7 @@ class MonitorController { try { const monitors = await this.db.getAllMonitorsWithUptimeStats(); return res.success({ - msg: successMessages.MONITOR_GET_ALL(req.language), + msg: this.stringService.monitorGetAll, data: monitors, }); } catch (error) { @@ -76,7 +78,7 @@ class MonitorController { try { const monitor = await this.db.getUptimeDetailsById(req); return res.success({ - msg: successMessages.MONITOR_GET_BY_ID(req.language), + msg: this.stringService.monitorGetById, data: monitor, }); } catch (error) { @@ -105,7 +107,7 @@ class MonitorController { try { const monitorStats = await this.db.getMonitorStatsById(req); return res.success({ - msg: successMessages.MONITOR_STATS_BY_ID(req.language), + msg: this.stringService.monitorStatsById, data: monitorStats, }); } catch (error) { @@ -133,7 +135,7 @@ class MonitorController { try { const monitor = await this.db.getHardwareDetailsById(req); return res.success({ - msg: successMessages.MONITOR_GET_BY_ID(req.language), + msg: this.stringService.monitorGetById, data: monitor, }); } catch (error) { @@ -154,7 +156,7 @@ class MonitorController { const certificate = await fetchMonitorCertificate(sslChecker, monitor); return res.success({ - msg: successMessages.MONITOR_CERTIFICATE(req.language), + msg: this.stringService.monitorCertificate, data: { certificateDate: new Date(certificate.validTo), }, @@ -187,7 +189,7 @@ class MonitorController { try { const monitor = await this.db.getMonitorById(req.params.monitorId); return res.success({ - msg: successMessages.MONITOR_GET_BY_ID(req.language), + msg: this.stringService.monitorGetById, data: monitor, }); } catch (error) { @@ -231,7 +233,7 @@ class MonitorController { // Add monitor to job queue this.jobQueue.addJob(monitor._id, monitor); return res.success({ - msg: successMessages.MONITOR_CREATE(req.language), + msg: this.stringService.monitorCreate, data: monitor, }); } catch (error) { @@ -309,7 +311,7 @@ class MonitorController { stack: error.stack, }); } - return res.success({ msg: successMessages.MONITOR_DELETE(req.language) }); + return res.success({ msg: this.stringService.monitorDelete }); } catch (error) { next(handleError(error, SERVICE_NAME, "deleteMonitor")); } @@ -401,7 +403,7 @@ class MonitorController { // Add the new job back to the queue await this.jobQueue.addJob(editedMonitor._id, editedMonitor); return res.success({ - msg: successMessages.MONITOR_EDIT(req.language), + msg: this.stringService.monitorEdit, data: editedMonitor, }); } catch (error) { @@ -438,8 +440,8 @@ class MonitorController { monitor.save(); return res.success({ msg: monitor.isActive - ? successMessages.MONITOR_RESUME(req.language) - : successMessages.MONITOR_PAUSE(req.language), + ? this.stringService.monitorResume + : this.stringService.monitorPause, data: monitor, }); } catch (error) { @@ -469,7 +471,7 @@ class MonitorController { ); return res.success({ - msg: successMessages.MONITOR_DEMO_ADDED(req.language), + msg: this.stringService.monitorDemoAdded, data: demoMonitors.length, }); } catch (error) { @@ -488,7 +490,7 @@ class MonitorController { try { const monitors = await this.db.getMonitorsByTeamId(req); return res.success({ - msg: successMessages.MONITOR_GET_BY_TEAM_ID(req.language), + msg: this.stringService.monitorGetByTeamId, data: monitors, }); } catch (error) { diff --git a/Server/controllers/queueController.js b/Server/controllers/queueController.js index f43237821..bd33fa001 100644 --- a/Server/controllers/queueController.js +++ b/Server/controllers/queueController.js @@ -1,18 +1,20 @@ import { handleError } from "./controllerUtils.js"; -import { successMessages } from "../utils/messages.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; const SERVICE_NAME = "JobQueueController"; class JobQueueController { constructor(jobQueue) { this.jobQueue = jobQueue; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); } getMetrics = async (req, res, next) => { try { const metrics = await this.jobQueue.getMetrics(); res.success({ - msg: successMessages.QUEUE_GET_METRICS(req.language), + msg: this.stringService.queueGetMetrics, data: metrics, }); } catch (error) { @@ -25,7 +27,7 @@ class JobQueueController { try { const jobs = await this.jobQueue.getJobStats(); return res.success({ - msg: successMessages.QUEUE_GET_METRICS(req.language), + msg: this.stringService.queueGetMetrics, data: jobs, }); } catch (error) { @@ -38,7 +40,7 @@ class JobQueueController { try { await this.jobQueue.addJob(Math.random().toString(36).substring(7)); return res.success({ - msg: successMessages.QUEUE_ADD_JOB(req.language), + msg: this.stringService.queueAddJob, }); } catch (error) { next(handleError(error, SERVICE_NAME, "addJob")); @@ -50,7 +52,7 @@ class JobQueueController { try { await this.jobQueue.obliterate(); return res.success({ - msg: successMessages.QUEUE_OBLITERATE(req.language), + msg: this.stringService.queueObliterate, }); } catch (error) { next(handleError(error, SERVICE_NAME, "obliterateQueue")); diff --git a/Server/controllers/settingsController.js b/Server/controllers/settingsController.js index 209b43ebc..7e4018241 100644 --- a/Server/controllers/settingsController.js +++ b/Server/controllers/settingsController.js @@ -1,12 +1,15 @@ -import { successMessages } from "../utils/messages.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; import { updateAppSettingsBodyValidation } from "../validation/joi.js"; import { handleValidationError, handleError } from "./controllerUtils.js"; + const SERVICE_NAME = "SettingsController"; class SettingsController { constructor(db, settingsService) { this.db = db; this.settingsService = settingsService; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); } getAppSettings = async (req, res, next) => { @@ -14,7 +17,7 @@ class SettingsController { const settings = { ...(await this.settingsService.getSettings()) }; delete settings.jwtSecret; return res.success({ - msg: successMessages.GET_APP_SETTINGS(req.language), + msg: this.stringService.getAppSettings, data: settings, }); } catch (error) { @@ -35,7 +38,7 @@ class SettingsController { const updatedSettings = { ...(await this.settingsService.reloadSettings()) }; delete updatedSettings.jwtSecret; return res.success({ - msg: successMessages.UPDATE_APP_SETTINGS(req.language), + msg: this.stringService.updateAppSettings, data: updatedSettings, }); } catch (error) { diff --git a/Server/controllers/statusPageController.js b/Server/controllers/statusPageController.js index 5be9050f1..b38989115 100644 --- a/Server/controllers/statusPageController.js +++ b/Server/controllers/statusPageController.js @@ -12,6 +12,7 @@ const SERVICE_NAME = "statusPageController"; class StatusPageController { constructor(db) { this.db = db; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); } createStatusPage = async (req, res, next) => { @@ -26,7 +27,7 @@ class StatusPageController { try { const statusPage = await this.db.createStatusPage(req.body, req.file); return res.success({ - msg: successMessages.STATUS_PAGE_CREATE(req.language), + msg: this.stringService.statusPageCreate, data: statusPage, }); } catch (error) { @@ -63,7 +64,7 @@ class StatusPageController { try { const statusPage = await this.db.getStatusPage(); return res.success({ - msg: successMessages.STATUS_PAGE_BY_URL(req.language), + msg: this.stringService.statusPageByUrl, data: statusPage, }); } catch (error) { diff --git a/Server/db/mongo/modules/inviteModule.js b/Server/db/mongo/modules/inviteModule.js index 2249ff1fb..f5c960697 100644 --- a/Server/db/mongo/modules/inviteModule.js +++ b/Server/db/mongo/modules/inviteModule.js @@ -1,6 +1,7 @@ import InviteToken from "../../models/InviteToken.js"; import crypto from "crypto"; -import { errorMessages } from "../../../utils/messages.js"; +import ServiceRegistry from "../../../service/serviceRegistry.js"; +import StringService from "../../../service/stringService.js"; const SERVICE_NAME = "inviteModule"; /** @@ -41,13 +42,14 @@ const requestInviteToken = async (userData) => { * @returns {Promise} The invite token data. * @throws {Error} If the invite token is not found or there is another error. */ -const getInviteToken = async (token, language) => { +const getInviteToken = async (token) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { const invite = await InviteToken.findOne({ token, }); if (invite === null) { - throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND(language)); + throw new Error(stringService.authInviteNotFound); } return invite; } catch (error) { @@ -67,13 +69,14 @@ const getInviteToken = async (token, language) => { * @returns {Promise} The invite token data. * @throws {Error} If the invite token is not found or there is another error. */ -const getInviteTokenAndDelete = async (token, language) => { +const getInviteTokenAndDelete = async (token) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { const invite = await InviteToken.findOneAndDelete({ token, }); if (invite === null) { - throw new Error(errorMessages.AUTH_INVITE_NOT_FOUND(language)); + throw new Error(stringService.authInviteNotFound); } return invite; } catch (error) { diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index c64d41c8e..e217bc9e1 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -6,6 +6,8 @@ import DistributedUptimeCheck from "../../models/DistributedUptimeCheck.js"; import { errorMessages } from "../../../utils/messages.js"; import Notification from "../../models/Notification.js"; import { NormalizeData, NormalizeDataUptimeDetails } from "../../../utils/dataUtils.js"; +import ServiceRegistry from "../../../service/serviceRegistry.js"; +import StringService from "../../../service/stringService.js"; import fs from "fs"; import path from "path"; import { fileURLToPath } from "url"; @@ -326,11 +328,12 @@ const calculateGroupStats = (group) => { * @throws {Error} */ const getUptimeDetailsById = async (req) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { const { monitorId } = req.params; const monitor = await Monitor.findById(monitorId); if (monitor === null || monitor === undefined) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, req.language)); + throw new Error(stringService.dbFindMonitorById(monitorId)); } const { dateRange, normalize } = req.query; @@ -419,13 +422,14 @@ const getDistributedUptimeDetailsById = async (req) => { * @throws {Error} */ const getMonitorStatsById = async (req) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { const { monitorId } = req.params; // Get monitor, if we can't find it, abort with error const monitor = await Monitor.findById(monitorId); if (monitor === null || monitor === undefined) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + throw new Error(stringService.getDbFindMonitorById(monitorId)); } // Get query params @@ -516,10 +520,11 @@ const getHardwareDetailsById = async (req) => { * @throws {Error} */ const getMonitorById = async (monitorId) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { const monitor = await Monitor.findById(monitorId); if (monitor === null || monitor === undefined) { - const error = new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + const error = new Error(stringService.getDbFindMonitorById(monitorId)); error.status = 404; throw error; } @@ -676,23 +681,23 @@ const getMonitorsByTeamId = async (req) => { : []), ...(limit ? [ - { - $lookup: { - from: "distributeduptimechecks", - let: { monitorId: "$_id" }, - pipeline: [ - { - $match: { - $expr: { $eq: ["$monitorId", "$$monitorId"] }, - }, + { + $lookup: { + from: "distributeduptimechecks", + let: { monitorId: "$_id" }, + pipeline: [ + { + $match: { + $expr: { $eq: ["$monitorId", "$$monitorId"] }, }, - { $sort: { createdAt: -1 } }, - ...(limit ? [{ $limit: limit }] : []), - ], - as: "distributeduptimechecks", - }, + }, + { $sort: { createdAt: -1 } }, + ...(limit ? [{ $limit: limit }] : []), + ], + as: "distributeduptimechecks", }, - ] + }, + ] : []), { @@ -783,11 +788,13 @@ const createMonitor = async (req, res) => { * @throws {Error} */ const deleteMonitor = async (req, res) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + const monitorId = req.params.monitorId; try { const monitor = await Monitor.findByIdAndDelete(monitorId); if (!monitor) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, req.language)); + throw new Error(stringService.getDbFindMonitorById(monitorId)); } return monitor; } catch (error) { diff --git a/Server/db/mongo/modules/recoveryModule.js b/Server/db/mongo/modules/recoveryModule.js index 0efcca4e8..3b39e847c 100644 --- a/Server/db/mongo/modules/recoveryModule.js +++ b/Server/db/mongo/modules/recoveryModule.js @@ -1,7 +1,8 @@ import UserModel from "../../models/User.js"; import RecoveryToken from "../../models/RecoveryToken.js"; import crypto from "crypto"; -import { errorMessages } from "../../../utils/messages.js"; +import serviceRegistry from "../../../service/serviceRegistry.js"; +import StringService from "../../../service/stringService.js"; const SERVICE_NAME = "recoveryModule"; @@ -31,6 +32,7 @@ const requestRecoveryToken = async (req, res) => { }; const validateRecoveryToken = async (req, res) => { + const stringService = serviceRegistry.get(StringService.SERVICE_NAME); try { const candidateToken = req.body.recoveryToken; const recoveryToken = await RecoveryToken.findOne({ @@ -39,7 +41,7 @@ const validateRecoveryToken = async (req, res) => { if (recoveryToken !== null) { return recoveryToken; } else { - throw new Error(errorMessages.DB_TOKEN_NOT_FOUND(req.language)); + throw new Error(stringService.dbTokenNotFound); } } catch (error) { error.service = SERVICE_NAME; @@ -49,6 +51,7 @@ const validateRecoveryToken = async (req, res) => { }; const resetPassword = async (req, res) => { + const stringService = serviceRegistry.get(StringService.SERVICE_NAME); try { const newPassword = req.body.password; @@ -57,12 +60,12 @@ const resetPassword = async (req, res) => { const user = await UserModel.findOne({ email: recoveryToken.email }); if (user === null) { - throw new Error(errorMessages.DB_USER_NOT_FOUND(req.language)); + throw new Error(stringService.dbUserNotFound); } const match = await user.comparePassword(newPassword); if (match === true) { - throw new Error(errorMessages.DB_RESET_PASSWORD_BAD_MATCH(req.language)); + throw new Error(stringService.dbResetPasswordBadMatch); } user.password = newPassword; diff --git a/Server/db/mongo/modules/statusPageModule.js b/Server/db/mongo/modules/statusPageModule.js index 8687f06ed..bd2b47375 100644 --- a/Server/db/mongo/modules/statusPageModule.js +++ b/Server/db/mongo/modules/statusPageModule.js @@ -19,7 +19,7 @@ const createStatusPage = async (statusPageData, image) => { if (error?.code === 11000) { // Handle duplicate URL errors error.status = 400; - error.message = errorMessages.STATUS_PAGE_URL_NOT_UNIQUE; + error.message = stringService.statusPageUrlNotUnique; } error.service = SERVICE_NAME; error.method = "createStatusPage"; diff --git a/Server/db/mongo/modules/userModule.js b/Server/db/mongo/modules/userModule.js index 3460f7513..4069be8c6 100644 --- a/Server/db/mongo/modules/userModule.js +++ b/Server/db/mongo/modules/userModule.js @@ -1,10 +1,11 @@ import UserModel from "../../models/User.js"; import TeamModel from "../../models/Team.js"; -import { errorMessages } from "../../../utils/messages.js"; import { GenerateAvatarImage } from "../../../utils/imageProcessing.js"; const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key import { ParseBoolean } from "../../../utils/utils.js"; +import ServiceRegistry from "../../../service/serviceRegistry.js"; +import StringService from "../../../service/stringService.js"; const SERVICE_NAME = "userModule"; /** @@ -20,6 +21,7 @@ const insertUser = async ( imageFile, generateAvatarImage = GenerateAvatarImage ) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { if (imageFile) { // 1. Save the full size image @@ -50,7 +52,7 @@ const insertUser = async ( .select("-profileImage"); // .select() doesn't work with create, need to save then find } catch (error) { if (error.code === DUPLICATE_KEY_CODE) { - error.message = errorMessages.DB_USER_EXISTS; + error.message = stringService.dbUserExists; } error.service = SERVICE_NAME; error.method = "insertUser"; @@ -69,13 +71,15 @@ const insertUser = async ( * @returns {Promise} * @throws {Error} */ -const getUserByEmail = async (email, language) => { +const getUserByEmail = async (email) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + try { // Need the password to be able to compare, removed .select() // We can strip the hash before returning the user const user = await UserModel.findOne({ email: email }).select("-profileImage"); if (!user) { - throw new Error(errorMessages.DB_USER_NOT_FOUND(language)); + throw new Error(stringService.dbUserNotFound); } return user; } catch (error) { @@ -149,11 +153,13 @@ const updateUser = async ( * @returns {Promise} * @throws {Error} */ -const deleteUser = async (userId, language) => { +const deleteUser = async (userId) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + try { const deletedUser = await UserModel.findByIdAndDelete(userId); if (!deletedUser) { - throw new Error(errorMessages.DB_USER_NOT_FOUND(language)); + throw new Error(stringService.dbUserNotFound); } return deletedUser; } catch (error) { diff --git a/Server/middleware/handleErrors.js b/Server/middleware/handleErrors.js index fa9af4c0b..b64cda897 100644 --- a/Server/middleware/handleErrors.js +++ b/Server/middleware/handleErrors.js @@ -1,10 +1,12 @@ import logger from "../utils/logger.js"; -import { errorMessages } from "../utils/messages.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; const handleErrors = (error, req, res, next) => { const status = error.status || 500; - const message = error.message || errorMessages.FRIENDLY_ERROR; - const service = error.service || errorMessages.UNKNOWN_SERVICE; + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + const message = error.message || stringService.friendlyError; + const service = error.service || stringService.unknownService; logger.error({ message: message, service: service, diff --git a/Server/middleware/isAllowed.js b/Server/middleware/isAllowed.js index a4fbcbf64..03a6b9d16 100644 --- a/Server/middleware/isAllowed.js +++ b/Server/middleware/isAllowed.js @@ -2,17 +2,17 @@ import jwt from "jsonwebtoken"; const TOKEN_PREFIX = "Bearer "; const SERVICE_NAME = "allowedRoles"; import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; import SettingsService from "../service/settingsService.js"; -import { errorMessages } from "../utils/messages.js"; const isAllowed = (allowedRoles) => { return (req, res, next) => { const token = req.headers["authorization"]; - + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); // If no token is pressent, return an error if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN(req.language)); + const error = new Error(stringService.noAuthToken); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -21,7 +21,7 @@ const isAllowed = (allowedRoles) => { // If the token is improperly formatted, return an error if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN(req.language)); + const error = new Error(stringService.invalidAuthToken); error.status = 400; error.service = SERVICE_NAME; next(error); @@ -41,7 +41,7 @@ const isAllowed = (allowedRoles) => { next(); return; } else { - const error = new Error(errorMessages.INSUFFICIENT_PERMISSIONS(req.language)); + const error = new Error(stringService.insufficientPermissions); error.status = 401; error.service = SERVICE_NAME; next(error); diff --git a/Server/middleware/verifyJWT.js b/Server/middleware/verifyJWT.js index 08a55cd78..9ee42e809 100644 --- a/Server/middleware/verifyJWT.js +++ b/Server/middleware/verifyJWT.js @@ -1,7 +1,7 @@ import jwt from "jsonwebtoken"; -import { errorMessages } from "../utils/messages.js"; import ServiceRegistry from "../service/serviceRegistry.js"; import SettingsService from "../service/settingsService.js"; +import StringService from "../service/stringService.js"; const SERVICE_NAME = "verifyJWT"; const TOKEN_PREFIX = "Bearer "; @@ -14,10 +14,11 @@ const TOKEN_PREFIX = "Bearer "; * @returns {express.Response} */ const verifyJWT = (req, res, next) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); const token = req.headers["authorization"]; // Make sure a token is provided if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN(req.language)); + const error = new Error(stringService.noAuthToken); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -25,7 +26,7 @@ const verifyJWT = (req, res, next) => { } // Make sure it is properly formatted if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN(req.language)); // Instantiate a new Error object for improperly formatted token + const error = new Error(stringService.invalidAuthToken); // Instantiate a new Error object for improperly formatted token error.status = 400; error.service = SERVICE_NAME; error.method = "verifyJWT"; @@ -43,7 +44,7 @@ const verifyJWT = (req, res, next) => { handleExpiredJwtToken(req, res, next); } else { // Invalid token (signature or token altered or other issue) - const errorMessage = errorMessages.INVALID_AUTH_TOKEN(req.language); + const errorMessage = stringService.invalidAuthToken; return res.status(401).json({ success: false, msg: errorMessage }); } } else { @@ -55,12 +56,13 @@ const verifyJWT = (req, res, next) => { }; function handleExpiredJwtToken(req, res, next) { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); // check for refreshToken const refreshToken = req.headers["x-refresh-token"]; if (!refreshToken) { // No refresh token provided - const error = new Error(errorMessages.NO_REFRESH_TOKEN(req.language)); + const error = new Error(stringService.noRefreshToken); error.status = 401; error.service = SERVICE_NAME; error.method = "handleExpiredJwtToken"; @@ -76,8 +78,8 @@ function handleExpiredJwtToken(req, res, next) { // Invalid or expired refresh token, trigger logout const errorMessage = refreshErr.name === "TokenExpiredError" - ? errorMessages.EXPIRED_REFRESH_TOKEN - : errorMessages.INVALID_REFRESH_TOKEN; + ? stringService.expiredRefreshToken + : stringService.invalidRefreshToken; const error = new Error(errorMessage); error.status = 401; error.service = SERVICE_NAME; @@ -87,7 +89,7 @@ function handleExpiredJwtToken(req, res, next) { // Refresh token is valid and unexpired, request for new access token res.status(403).json({ success: false, - msg: errorMessages.REQUEST_NEW_ACCESS_TOKEN, + msg: stringService.requestNewAccessToken, }); }); } diff --git a/Server/middleware/verifyOwnership.js b/Server/middleware/verifyOwnership.js index 10de1f7b6..ca2476f54 100644 --- a/Server/middleware/verifyOwnership.js +++ b/Server/middleware/verifyOwnership.js @@ -1,8 +1,10 @@ import logger from "../utils/logger.js"; -import { errorMessages } from "../utils/messages.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; const SERVICE_NAME = "verifyOwnership"; const verifyOwnership = (Model, paramName) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); return async (req, res, next) => { const userId = req.user._id; const documentId = req.params[paramName]; @@ -11,11 +13,11 @@ const verifyOwnership = (Model, paramName) => { //If the document is not found, return a 404 error if (!doc) { logger.error({ - message: errorMessages.VERIFY_OWNER_NOT_FOUND, + message: stringService.verifyOwnerNotFound, service: SERVICE_NAME, method: "verifyOwnership", }); - const error = new Error(errorMessages.VERIFY_OWNER_NOT_FOUND(req.language)); + const error = new Error(stringService.verifyOwnerNotFound); error.status = 404; throw error; } @@ -23,7 +25,7 @@ const verifyOwnership = (Model, paramName) => { // Special case for User model, as it will not have a `userId` field as other docs will if (Model.modelName === "User") { if (userId.toString() !== doc._id.toString()) { - const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED(req.language)); + const error = new Error(stringService.verifyOwnerUnauthorized); error.status = 403; throw error; } @@ -33,7 +35,7 @@ const verifyOwnership = (Model, paramName) => { // If the userID does not match the document's userID, return a 403 error if (userId.toString() !== doc.userId.toString()) { - const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED(req.language)); + const error = new Error(stringService.verifyOwnerUnauthorized); error.status = 403; throw error; } diff --git a/Server/middleware/verifySuperAdmin.js b/Server/middleware/verifySuperAdmin.js index b4b764f5a..98c336fad 100644 --- a/Server/middleware/verifySuperAdmin.js +++ b/Server/middleware/verifySuperAdmin.js @@ -2,9 +2,9 @@ const jwt = require("jsonwebtoken"); const logger = require("../utils/logger"); const SERVICE_NAME = "verifyAdmin"; const TOKEN_PREFIX = "Bearer "; -const { errorMessages } = require("../utils/messages"); import ServiceRegistry from "../service/serviceRegistry.js"; import SettingsService from "../service/settingsService.js"; +import StringService from "../service/stringService.js"; /** * Verifies the JWT token * @function @@ -14,10 +14,11 @@ import SettingsService from "../service/settingsService.js"; * @returns {express.Response} */ const verifySuperAdmin = (req, res, next) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); const token = req.headers["authorization"]; // Make sure a token is provided if (!token) { - const error = new Error(errorMessages.NO_AUTH_TOKEN(req.language)); + const error = new Error(stringService.noAuthToken); error.status = 401; error.service = SERVICE_NAME; next(error); @@ -25,7 +26,7 @@ const verifySuperAdmin = (req, res, next) => { } // Make sure it is properly formatted if (!token.startsWith(TOKEN_PREFIX)) { - const error = new Error(errorMessages.INVALID_AUTH_TOKEN(req.language)); // Instantiate a new Error object for improperly formatted token + const error = new Error(stringService.invalidAuthToken); // Instantiate a new Error object for improperly formatted token error.status = 400; error.service = SERVICE_NAME; error.method = "verifySuperAdmin"; @@ -44,21 +45,21 @@ const verifySuperAdmin = (req, res, next) => { service: SERVICE_NAME, method: "verifySuperAdmin", stack: err.stack, - details: errorMessages.INVALID_AUTH_TOKEN, + details: stringService.invalidAuthToken, }); return res .status(401) - .json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN }); + .json({ success: false, msg: stringService.invalidAuthToken }); } if (decoded.role.includes("superadmin") === false) { logger.error({ - message: errorMessages.INVALID_AUTH_TOKEN, + message: stringService.invalidAuthToken, service: SERVICE_NAME, method: "verifySuperAdmin", stack: err.stack, }); - return res.status(401).json({ success: false, msg: errorMessages.UNAUTHORIZED }); + return res.status(401).json({ success: false, msg: stringService.unauthorized }); } next(); }); diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index bbcca6601..b4c91758e 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -12,7 +12,9 @@ const QUEUE_LOOKUP = { }; const getSchedulerId = (monitor) => `scheduler:${monitor.type}:${monitor._id}`; -import { successMessages, errorMessages } from "../utils/messages.js"; +import ServiceRegistry from "../service/serviceRegistry.js"; +import StringService from "../service/stringService.js"; + class NewJobQueue { static SERVICE_NAME = SERVICE_NAME; @@ -44,6 +46,7 @@ class NewJobQueue { this.settingsService = settingsService; this.logger = logger; this.Worker = Worker; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); QUEUE_NAMES.forEach((name) => { this.queues[name] = new Queue(name, { connection }); @@ -455,7 +458,7 @@ class NewJobQueue { if (wasDeleted === true) { this.logger.info({ - message: successMessages.JOB_QUEUE_DELETE_JOB('en'), + message: this.stringService.jobQueueDeleteJob, service: SERVICE_NAME, method: "deleteJob", details: `Deleted job ${monitor._id}`, @@ -464,7 +467,7 @@ class NewJobQueue { await this.scaleWorkers(workerStats, queue); } else { this.logger.error({ - message: errorMessages.JOB_QUEUE_DELETE_JOB, + message: this.stringService.jobQueueDeleteJob, service: SERVICE_NAME, method: "deleteJob", details: `Failed to delete job ${monitor._id}`, @@ -587,7 +590,7 @@ class NewJobQueue { const metrics = await this.getMetrics(); this.logger.info({ - message: successMessages.JOB_QUEUE_OBLITERATE('en'), + message: this.stringService.jobQueueObliterate, service: SERVICE_NAME, method: "obliterate", details: metrics, diff --git a/Server/service/networkService.js b/Server/service/networkService.js index f57abdd74..01694813a 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -1,4 +1,3 @@ -import { errorMessages, successMessages } from "../utils/messages.js"; const SERVICE_NAME = "NetworkService"; const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push"; @@ -32,9 +31,11 @@ class NetworkService { this.http = http; this.Docker = Docker; this.net = net; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); this.apiToken = process.env.POEDITOR_API_TOKEN; this.projectId = process.env.POEDITOR_PROJECT_ID; + this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); if (!this.apiToken || !this.projectId) { this.logger.error({ @@ -100,13 +101,13 @@ class NetworkService { if (error) { pingResponse.status = false; pingResponse.code = this.PING_ERROR; - pingResponse.message = errorMessages.PING_CANNOT_RESOLVE; + pingResponse.message = this.stringService.pingCannotResolve; return pingResponse; } pingResponse.code = 200; pingResponse.status = response.alive; - pingResponse.message = successMessages.PING_SUCCESS('en'); + pingResponse.message = this.stringService.pingSuccess; return pingResponse; } catch (error) { error.service = this.SERVICE_NAME; @@ -253,7 +254,7 @@ class NetworkService { const containers = await docker.listContainers({ all: true }); const containerExists = containers.some((c) => c.Id.startsWith(job.data.url)); if (!containerExists) { - throw new Error(errorMessages.DOCKER_NOT_FOUND('en')); + throw new Error(this.stringService.dockerNotFound); } const container = docker.getContainer(job.data.url); @@ -270,12 +271,12 @@ class NetworkService { if (error) { dockerResponse.status = false; dockerResponse.code = error.statusCode || this.NETWORK_ERROR; - dockerResponse.message = error.reason || errorMessages.DOCKER_FAIL; + dockerResponse.message = error.reason || this.stringService.dockerFail; return dockerResponse; } dockerResponse.status = response?.State?.Status === "running" ? true : false; dockerResponse.code = 200; - dockerResponse.message = successMessages.DOCKER_SUCCESS('en'); + dockerResponse.message = this.stringService.dockerSuccess; return dockerResponse; } catch (error) { error.service = this.SERVICE_NAME; @@ -323,13 +324,13 @@ class NetworkService { if (error) { portResponse.status = false; portResponse.code = this.NETWORK_ERROR; - portResponse.message = errorMessages.PORT_FAIL; + portResponse.message = this.stringService.portFail; return portResponse; } portResponse.status = response.success; portResponse.code = 200; - portResponse.message = successMessages.PORT_SUCCESS('en'); + portResponse.message = this.stringService.portSuccess; return portResponse; } catch (error) { error.service = this.SERVICE_NAME; diff --git a/Server/utils/locales_en.json b/Server/utils/locales_en.json index 58eb0cf49..a438e5fc8 100644 --- a/Server/utils/locales_en.json +++ b/Server/utils/locales_en.json @@ -139,5 +139,7 @@ "statusPageCreate": "Status page created successfully", "newTermsAdded": "New terms added to POEditor", "dockerSuccess": "Docker container status fetched successfully", - "portSuccess": "Port connected successfully" + "portSuccess": "Port connected successfully", + "monitorPause": "Monitor paused successfully", + "monitorResume": "Monitor resumed successfully" } \ No newline at end of file From dea8c4fdd85ebae545af91cdc83d90a0408615d9 Mon Sep 17 00:00:00 2001 From: cihatata Date: Thu, 30 Jan 2025 01:06:16 +0300 Subject: [PATCH 19/52] fix: use new structure --- Server/index.js | 10 ++++++---- Server/service/networkService.js | 10 ++++------ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/Server/index.js b/Server/index.js index 7aa5124f6..76bb60023 100644 --- a/Server/index.js +++ b/Server/index.js @@ -160,6 +160,11 @@ const startApp = async () => { } } + const networkService = new NetworkService(axios, ping, logger, http, Docker, net); + const translationService = new TranslationService(logger, networkService); + const stringService = new StringService(translationService); + ServiceRegistry.register(StringService.SERVICE_NAME, stringService); + // Create DB const db = new MongoDB(); await db.connect(); @@ -176,11 +181,9 @@ const startApp = async () => { nodemailer, logger ); - const networkService = new NetworkService(axios, ping, logger, http, Docker, net); const statusService = new StatusService(db, logger); const notificationService = new NotificationService(emailService, db, logger); - const translationService = new TranslationService(logger, networkService); - const stringService = new StringService(translationService); + const jobQueue = new JobQueue( db, @@ -202,7 +205,6 @@ const startApp = async () => { ServiceRegistry.register(StatusService.SERVICE_NAME, statusService); ServiceRegistry.register(NotificationService.SERVICE_NAME, notificationService); ServiceRegistry.register(TranslationService.SERVICE_NAME, translationService); - ServiceRegistry.register(StringService.SERVICE_NAME, stringService); await translationService.initialize(); diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 01694813a..119f2891b 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -31,11 +31,9 @@ class NetworkService { this.http = http; this.Docker = Docker; this.net = net; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); this.apiToken = process.env.POEDITOR_API_TOKEN; this.projectId = process.env.POEDITOR_PROJECT_ID; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); if (!this.apiToken || !this.projectId) { this.logger.error({ @@ -101,13 +99,13 @@ class NetworkService { if (error) { pingResponse.status = false; pingResponse.code = this.PING_ERROR; - pingResponse.message = this.stringService.pingCannotResolve; + pingResponse.message = "No response"; return pingResponse; } pingResponse.code = 200; pingResponse.status = response.alive; - pingResponse.message = this.stringService.pingSuccess; + pingResponse.message = "Success"; return pingResponse; } catch (error) { error.service = this.SERVICE_NAME; @@ -271,12 +269,12 @@ class NetworkService { if (error) { dockerResponse.status = false; dockerResponse.code = error.statusCode || this.NETWORK_ERROR; - dockerResponse.message = error.reason || this.stringService.dockerFail; + dockerResponse.message = error.reason || "Failed to fetch Docker container information"; return dockerResponse; } dockerResponse.status = response?.State?.Status === "running" ? true : false; dockerResponse.code = 200; - dockerResponse.message = this.stringService.dockerSuccess; + dockerResponse.message = "Docker container status fetched successfully"; return dockerResponse; } catch (error) { error.service = this.SERVICE_NAME; From 08933743f1a87d3e4419a8d4feb8da2d612380eb Mon Sep 17 00:00:00 2001 From: cihatata Date: Thu, 30 Jan 2025 01:11:55 +0300 Subject: [PATCH 20/52] fix: revert tests --- .../tests/controllers/authController.test.js | 201 ++++++------- .../tests/controllers/checkController.test.js | 89 +++--- .../maintenanceWindowController.test.js | 94 +++--- .../controllers/monitorController.test.js | 272 ++++++++--------- .../tests/controllers/queueController.test.js | 32 +- Server/tests/db/inviteModule.test.js | 33 +- Server/tests/db/monitorModule.test.js | 284 +++++++++--------- Server/tests/db/recoveryModule.test.js | 44 ++- Server/tests/db/statusPageModule.test.js | 29 +- Server/tests/db/userModule.test.js | 74 +++-- Server/tests/services/networkService.test.js | 92 +++--- Server/tests/utils/messages.test.js | 18 +- 12 files changed, 617 insertions(+), 645 deletions(-) diff --git a/Server/tests/controllers/authController.test.js b/Server/tests/controllers/authController.test.js index c96b77da3..3f45d1c42 100644 --- a/Server/tests/controllers/authController.test.js +++ b/Server/tests/controllers/authController.test.js @@ -18,16 +18,14 @@ import { getTokenFromHeaders, tokenType } from "../../utils/utils.js"; import logger from "../../utils/logger.js"; import e from "cors"; -const mockLanguage = 'en'; - -describe("Auth Controller - issueToken", function () { +describe("Auth Controller - issueToken", function() { let stub; - afterEach(function () { + afterEach(function() { sinon.restore(); // Restore stubs after each test }); - it("should reject with an error if jwt.sign fails", function () { + it("should reject with an error if jwt.sign fails", function() { const error = new Error("jwt.sign error"); stub = sinon.stub(jwt, "sign").throws(error); const payload = { id: "123" }; @@ -37,7 +35,7 @@ describe("Auth Controller - issueToken", function () { ); }); - it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", function () { + it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", function() { const payload = { id: "123" }; const appSettings = { jwtSecret: "my_secret" }; const expectedToken = "mockToken"; @@ -47,7 +45,7 @@ describe("Auth Controller - issueToken", function () { expect(token).to.equal(expectedToken); }); - it("should return a token if jwt.sign is successful and appSettings.jwtTTL is defined", function () { + it("should return a token if jwt.sign is successful and appSettings.jwtTTL is defined", function() { const payload = { id: "123" }; const appSettings = { jwtSecret: "my_secret", jwtTTL: "1s" }; const expectedToken = "mockToken"; @@ -57,7 +55,7 @@ describe("Auth Controller - issueToken", function () { expect(token).to.equal(expectedToken); }); - it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is not defined", function () { + it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is not defined", function() { const payload = {}; const appSettings = { refreshTokenSecret: "my_refresh_secret" }; const expectedToken = "mockRefreshToken"; @@ -67,7 +65,7 @@ describe("Auth Controller - issueToken", function () { expect(token).to.equal(expectedToken); }); - it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is defined", function () { + it("should return a refresh token if jwt.sign is successful and appSettings.refreshTokenTTL is defined", function() { const payload = {}; const appSettings = { refreshTokenSecret: "my_refresh_secret", @@ -81,10 +79,10 @@ describe("Auth Controller - issueToken", function () { }); }); -describe("Auth Controller - registerUser", function () { +describe("Auth Controller - registerUser", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { body: { firstName: "firstname", @@ -120,25 +118,25 @@ describe("Auth Controller - registerUser", function () { sinon.stub(logger, "error"); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if body validation fails", async function () { + it("should reject with an error if body validation fails", async function() { req.body = {}; await registerUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if checkSuperadmin fails", async function () { + it("should reject with an error if checkSuperadmin fails", async function() { req.db.checkSuperadmin.throws(new Error("checkSuperadmin error")); await registerUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); }); - it("should reject with an error if getInviteTokenAndDelete fails", async function () { + it("should reject with an error if getInviteTokenAndDelete fails", async function() { req.db.checkSuperadmin.returns(true); req.db.getInviteTokenAndDelete.throws(new Error("getInviteTokenAndDelete error")); await registerUser(req, res, next); @@ -146,7 +144,7 @@ describe("Auth Controller - registerUser", function () { expect(next.firstCall.args[0].message).to.equal("getInviteTokenAndDelete error"); }); - it("should reject with an error if updateAppSettings fails", async function () { + it("should reject with an error if updateAppSettings fails", async function() { req.db.checkSuperadmin.returns(false); req.db.updateAppSettings.throws(new Error("updateAppSettings error")); await registerUser(req, res, next); @@ -154,7 +152,7 @@ describe("Auth Controller - registerUser", function () { expect(next.firstCall.args[0].message).to.equal("updateAppSettings error"); }); - it("should reject with an error if insertUser fails", async function () { + it("should reject with an error if insertUser fails", async function() { req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); req.db.insertUser.rejects(new Error("insertUser error")); @@ -163,7 +161,7 @@ describe("Auth Controller - registerUser", function () { expect(next.firstCall.args[0].message).to.equal("insertUser error"); }); - it("should reject with an error if settingsService.getSettings fails", async function () { + it("should reject with an error if settingsService.getSettings fails", async function() { req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); req.db.insertUser.resolves({ _id: "123" }); @@ -175,7 +173,7 @@ describe("Auth Controller - registerUser", function () { expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error"); }); - it("should log an error if emailService.buildAndSendEmail fails", async function () { + it("should log an error if emailService.buildAndSendEmail fails", async function() { req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); req.db.insertUser.returns({ _id: "123" }); @@ -189,7 +187,7 @@ describe("Auth Controller - registerUser", function () { expect(logger.error.firstCall.args[0].message).to.equal("emailService error"); }); - it("should return a success message and data if all operations are successful", async function () { + it("should return a success message and data if all operations are successful", async function() { const user = { _id: "123" }; req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); @@ -204,14 +202,14 @@ describe("Auth Controller - registerUser", function () { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_CREATE_USER(mockLanguage), + msg: successMessages.AUTH_CREATE_USER, data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should return a success message and data if all operations are successful and superAdmin true", async function () { + it("should return a success message and data if all operations are successful and superAdmin true", async function() { const user = { _id: "123" }; req.db.checkSuperadmin.resolves(true); req.db.updateAppSettings.resolves(); @@ -226,7 +224,7 @@ describe("Auth Controller - registerUser", function () { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_CREATE_USER(mockLanguage), + msg: successMessages.AUTH_CREATE_USER, data: { user, token: sinon.match.string, refreshToken: sinon.match.string }, }) ).to.be.true; @@ -234,16 +232,15 @@ describe("Auth Controller - registerUser", function () { }); }); -describe("Auth Controller - loginUser", function () { +describe("Auth Controller - loginUser", function() { let req, res, next, user; - beforeEach(function () { + beforeEach(function() { req = { body: { email: "test@example.com", password: "Password123!" }, db: { getUserByEmail: sinon.stub(), }, - language: 'en', settingsService: { getSettings: sinon.stub().resolves({ jwtSecret: "my_secret", @@ -264,21 +261,21 @@ describe("Auth Controller - loginUser", function () { }; }); - it("should reject with an error if validation fails", async function () { + it("should reject with an error if validation fails", async function() { req.body = {}; await loginUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getUserByEmail fails", async function () { + it("should reject with an error if getUserByEmail fails", async function() { req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); await loginUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); }); - it("should login user successfully", async function () { + it("should login user successfully", async function() { req.db.getUserByEmail.resolves(user); user.comparePassword.resolves(true); await loginUser(req, res, next); @@ -286,7 +283,7 @@ describe("Auth Controller - loginUser", function () { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_LOGIN_USER(mockLanguage), + msg: successMessages.AUTH_LOGIN_USER, data: { user: { email: "test@example.com", @@ -300,7 +297,7 @@ describe("Auth Controller - loginUser", function () { expect(next.notCalled).to.be.true; }); - it("should reject a user with an incorrect password", async function () { + it("should reject a user with an incorrect password", async function() { req.body = { email: "test@test.com", password: "Password123!", @@ -310,15 +307,15 @@ describe("Auth Controller - loginUser", function () { await loginUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal( - errorMessages.AUTH_INCORRECT_PASSWORD(mockLanguage) + errorMessages.AUTH_INCORRECT_PASSWORD ); }); }); -describe("Auth Controller - refreshAuthToken", function () { +describe("Auth Controller - refreshAuthToken", function() { let req, res, next, issueTokenStub; - beforeEach(function () { + beforeEach(function() { req = { headers: { "x-refresh-token": "valid_refresh_token", @@ -342,39 +339,39 @@ describe("Auth Controller - refreshAuthToken", function () { sinon.replace({ issueToken }, "issueToken", issueTokenStub); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject if no refresh token is provided", async function () { + it("should reject if no refresh token is provided", async function() { delete req.headers["x-refresh-token"]; await refreshAuthToken(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.NO_REFRESH_TOKEN(req.language)); + expect(next.firstCall.args[0].message).to.equal(errorMessages.NO_REFRESH_TOKEN); expect(next.firstCall.args[0].status).to.equal(401); }); - it("should reject if the refresh token is invalid", async function () { + it("should reject if the refresh token is invalid", async function() { jwt.verify.yields(new Error("invalid token")); await refreshAuthToken(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.INVALID_REFRESH_TOKEN(req.language)); + expect(next.firstCall.args[0].message).to.equal(errorMessages.INVALID_REFRESH_TOKEN); expect(next.firstCall.args[0].status).to.equal(401); }); - it("should reject if the refresh token is expired", async function () { + it("should reject if the refresh token is expired", async function() { const error = new Error("Token expired"); error.name = "TokenExpiredError"; jwt.verify.yields(error); await refreshAuthToken(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); - expect(next.firstCall.args[0].message).to.equal(errorMessages.EXPIRED_REFRESH_TOKEN(req.language)); + expect(next.firstCall.args[0].message).to.equal(errorMessages.EXPIRED_REFRESH_TOKEN); expect(next.firstCall.args[0].status).to.equal(401); }); - it("should reject if settingsService.getSettings fails", async function () { + it("should reject if settingsService.getSettings fails", async function() { req.settingsService.getSettings.rejects( new Error("settingsService.getSettings error") ); @@ -384,7 +381,7 @@ describe("Auth Controller - refreshAuthToken", function () { expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error"); }); - it("should generate a new auth token if the refresh token is valid", async function () { + it("should generate a new auth token if the refresh token is valid", async function() { const decodedPayload = { expiresIn: "60" }; jwt.verify.callsFake(() => { return decodedPayload; @@ -395,7 +392,7 @@ describe("Auth Controller - refreshAuthToken", function () { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_TOKEN_REFRESHED(mockLanguage), + msg: successMessages.AUTH_TOKEN_REFRESHED, data: { user: decodedPayload, token: sinon.match.string, @@ -406,10 +403,10 @@ describe("Auth Controller - refreshAuthToken", function () { }); }); -describe("Auth Controller - editUser", function () { +describe("Auth Controller - editUser", function() { let req, res, next, stub, user; - beforeEach(function () { + beforeEach(function() { req = { params: { userId: "123" }, body: { password: "Password1!", newPassword: "Password2!" }, @@ -431,40 +428,40 @@ describe("Auth Controller - editUser", function () { stub = sinon.stub(jwt, "verify").returns({ email: "test@example.com" }); }); - afterEach(function () { + afterEach(function() { sinon.restore(); stub.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await editUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if body validation fails", async function () { + it("should reject with an error if body validation fails", async function() { req.body = { invalid: 1 }; await editUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if param.userId !== req.user._id", async function () { + it("should reject with an error if param.userId !== req.user._id", async function() { req.params = { userId: "456" }; await editUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(401); }); - it("should reject with an error if !req.body.password and getUserByEmail fails", async function () { + it("should reject with an error if !req.body.password and getUserByEmail fails", async function() { req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); await editUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); }); - it("should reject with an error if user.comparePassword fails", async function () { + it("should reject with an error if user.comparePassword fails", async function() { req.db.getUserByEmail.returns({ comparePassword: sinon.stub().rejects(new Error("Bad Password Match")), }); @@ -473,7 +470,7 @@ describe("Auth Controller - editUser", function () { expect(next.firstCall.args[0].message).to.equal("Bad Password Match"); }); - it("should reject with an error if user.comparePassword returns false", async function () { + it("should reject with an error if user.comparePassword returns false", async function() { req.db.getUserByEmail.returns({ comparePassword: sinon.stub().returns(false), }); @@ -481,11 +478,11 @@ describe("Auth Controller - editUser", function () { expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(401); expect(next.firstCall.args[0].message).to.equal( - errorMessages.AUTH_INCORRECT_PASSWORD(mockLanguage) + errorMessages.AUTH_INCORRECT_PASSWORD ); }); - it("should edit a user if it receives a proper request", async function () { + it("should edit a user if it receives a proper request", async function() { const user = { comparePassword: sinon.stub().resolves(true), }; @@ -500,14 +497,14 @@ describe("Auth Controller - editUser", function () { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_UPDATE_USER(mockLanguage), + msg: successMessages.AUTH_UPDATE_USER, data: { email: "test@example.com" }, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should edit a user if it receives a proper request and both password fields are undefined", async function () { + it("should edit a user if it receives a proper request and both password fields are undefined", async function() { req.body.password = undefined; req.body.newPassword = undefined; req.db.getUserByEmail.resolves(user); @@ -518,14 +515,14 @@ describe("Auth Controller - editUser", function () { expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_UPDATE_USER(mockLanguage), + msg: successMessages.AUTH_UPDATE_USER, data: { email: "test@example.com" }, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should reject an edit request if password format is incorrect", async function () { + it("should reject an edit request if password format is incorrect", async function() { req.body = { password: "bad_password", newPassword: "bad_password" }; const user = { comparePassword: sinon.stub().resolves(true), @@ -539,10 +536,10 @@ describe("Auth Controller - editUser", function () { }); }); -describe("Auth Controller - checkSuperadminExists", function () { +describe("Auth Controller - checkSuperadminExists", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { db: { checkSuperadmin: sinon.stub(), @@ -555,35 +552,35 @@ describe("Auth Controller - checkSuperadminExists", function () { next = sinon.stub(); }); - it("should reject with an error if checkSuperadmin fails", async function () { + it("should reject with an error if checkSuperadmin fails", async function() { req.db.checkSuperadmin.rejects(new Error("checkSuperadmin error")); await checkSuperadminExists(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("checkSuperadmin error"); }); - it("should return true if a superadmin exists", async function () { + it("should return true if a superadmin exists", async function() { req.db.checkSuperadmin.resolves(true); await checkSuperadminExists(req, res, next); expect(res.status.calledWith(200)).to.be.true; expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_SUPERADMIN_EXISTS(mockLanguage), + msg: successMessages.AUTH_SUPERADMIN_EXISTS, data: true, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should return false if a superadmin does not exist", async function () { + it("should return false if a superadmin does not exist", async function() { req.db.checkSuperadmin.resolves(false); await checkSuperadminExists(req, res, next); expect(res.status.calledWith(200)).to.be.true; expect( res.json.calledWith({ success: true, - msg: successMessages.AUTH_SUPERADMIN_EXISTS(mockLanguage), + msg: successMessages.AUTH_SUPERADMIN_EXISTS, data: false, }) ).to.be.true; @@ -591,10 +588,10 @@ describe("Auth Controller - checkSuperadminExists", function () { }); }); -describe("Auth Controller - requestRecovery", function () { +describe("Auth Controller - requestRecovery", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { body: { email: "test@test.com" }, db: { @@ -615,21 +612,21 @@ describe("Auth Controller - requestRecovery", function () { next = sinon.stub(); }); - it("should reject with an error if validation fails", async function () { + it("should reject with an error if validation fails", async function() { req.body = {}; await requestRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getUserByEmail fails", async function () { + it("should reject with an error if getUserByEmail fails", async function() { req.db.getUserByEmail.rejects(new Error("getUserByEmail error")); await requestRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getUserByEmail error"); }); - it("should throw an error if the user is not found", async function () { + it("should throw an error if the user is not found", async function() { req.db.getUserByEmail.resolves(null); await requestRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); @@ -638,14 +635,14 @@ describe("Auth Controller - requestRecovery", function () { // ); }); - it("should throw an error if the email is not provided", async function () { + it("should throw an error if the email is not provided", async function() { req.body = {}; await requestRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should return a success message if the email is provided", async function () { + it("should return a success message if the email is provided", async function() { const user = { firstName: "John" }; const recoveryToken = { token: "recovery-token" }; const msgId = "message-id"; @@ -671,7 +668,7 @@ describe("Auth Controller - requestRecovery", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN(mockLanguage), + msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN, data: msgId, }) ).to.be.true; @@ -679,10 +676,10 @@ describe("Auth Controller - requestRecovery", function () { }); }); -describe("Auth Controller - validateRecovery", function () { +describe("Auth Controller - validateRecovery", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { body: { recoveryToken: "recovery-token" }, db: { @@ -696,38 +693,38 @@ describe("Auth Controller - validateRecovery", function () { next = sinon.stub(); }); - it("should reject with an error if validation fails", async function () { + it("should reject with an error if validation fails", async function() { req.body = {}; await validateRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if validateRecoveryToken fails", async function () { + it("should reject with an error if validateRecoveryToken fails", async function() { req.db.validateRecoveryToken.rejects(new Error("validateRecoveryToken error")); await validateRecovery(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("validateRecoveryToken error"); }); - it("should return a success message if the token is valid", async function () { + it("should return a success message if the token is valid", async function() { req.db.validateRecoveryToken.resolves(); await validateRecovery(req, res, next); expect(res.status.calledOnceWith(200)).to.be.true; expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN(mockLanguage), + msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN, }) ).to.be.true; expect(next.notCalled).to.be.true; }); }); -describe("Auth Controller - resetPassword", function () { +describe("Auth Controller - resetPassword", function() { let req, res, next, newPasswordValidation, handleValidationError, handleError; - beforeEach(function () { + beforeEach(function() { req = { body: { recoveryToken: "recovery-token", @@ -752,14 +749,14 @@ describe("Auth Controller - resetPassword", function () { handleError = sinon.stub(); }); - it("should reject with an error if validation fails", async function () { + it("should reject with an error if validation fails", async function() { req.body = { password: "bad_password" }; await resetPassword(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if resetPassword fails", async function () { + it("should reject with an error if resetPassword fails", async function() { const error = new Error("resetPassword error"); newPasswordValidation.validateAsync.resolves(); req.db.resetPassword.rejects(error); @@ -768,7 +765,7 @@ describe("Auth Controller - resetPassword", function () { expect(next.firstCall.args[0].message).to.equal("resetPassword error"); }); - it("should reset password successfully", async function () { + it("should reset password successfully", async function() { const user = { _doc: {} }; const appSettings = { jwtSecret: "my_secret" }; const token = "token"; @@ -785,7 +782,7 @@ describe("Auth Controller - resetPassword", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_RESET_PASSWORD(mockLanguage), + msg: successMessages.AUTH_RESET_PASSWORD, data: { user: sinon.match.object, token: sinon.match.string }, }) ).to.be.true; @@ -793,10 +790,10 @@ describe("Auth Controller - resetPassword", function () { }); }); -describe("Auth Controller - deleteUser", function () { +describe("Auth Controller - deleteUser", function() { let req, res, next, handleError; - beforeEach(function () { + beforeEach(function() { req = { headers: { authorization: "Bearer token", @@ -828,24 +825,24 @@ describe("Auth Controller - deleteUser", function () { handleError = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should throw an error if user is not found", async function () { + it("should throw an error if user is not found", async function() { jwt.decode.returns({ email: "test@example.com" }); - req.db.getUserByEmail.throws(new Error(errorMessages.DB_USER_NOT_FOUND(req.language))); + req.db.getUserByEmail.throws(new Error(errorMessages.DB_USER_NOT_FOUND)); await deleteUser(req, res, next); expect(req.db.getUserByEmail.calledOnceWith("test@example.com")).to.be.true; expect(next.calledOnce).to.be.true; - expect(next.firstCall.args[0].message).to.equal(errorMessages.DB_USER_NOT_FOUND(req.language)); + expect(next.firstCall.args[0].message).to.equal(errorMessages.DB_USER_NOT_FOUND); expect(res.status.notCalled).to.be.true; expect(res.json.notCalled).to.be.true; }); - it("should delete user and associated data if user is superadmin", async function () { + it("should delete user and associated data if user is superadmin", async function() { const user = { _id: "user_id", email: "test@example.com", @@ -879,13 +876,13 @@ describe("Auth Controller - deleteUser", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_DELETE_USER(mockLanguage), + msg: successMessages.AUTH_DELETE_USER, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should delete user if user is not superadmin", async function () { + it("should delete user if user is not superadmin", async function() { const user = { _id: "user_id", email: "test@example.com", @@ -909,13 +906,13 @@ describe("Auth Controller - deleteUser", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.AUTH_DELETE_USER(mockLanguage), + msg: successMessages.AUTH_DELETE_USER, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should handle errors", async function () { + it("should handle errors", async function() { const error = new Error("Something went wrong"); const SERVICE_NAME = "AuthController"; jwt.decode.returns({ email: "test@example.com" }); @@ -928,10 +925,10 @@ describe("Auth Controller - deleteUser", function () { }); }); -describe("Auth Controller - getAllUsers", function () { +describe("Auth Controller - getAllUsers", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { db: { getAllUsers: sinon.stub(), @@ -944,11 +941,11 @@ describe("Auth Controller - getAllUsers", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); // Restore the original methods after each test }); - it("should return 200 and all users", async function () { + it("should return 200 and all users", async function() { const allUsers = [{ id: 1, name: "John Doe" }]; req.db.getAllUsers.resolves(allUsers); @@ -966,7 +963,7 @@ describe("Auth Controller - getAllUsers", function () { expect(next.notCalled).to.be.true; }); - it("should call next with error when an exception occurs", async function () { + it("should call next with error when an exception occurs", async function() { const error = new Error("Something went wrong"); req.db.getAllUsers.rejects(error); await getAllUsers(req, res, next); diff --git a/Server/tests/controllers/checkController.test.js b/Server/tests/controllers/checkController.test.js index 67207bd9b..0c00b9487 100644 --- a/Server/tests/controllers/checkController.test.js +++ b/Server/tests/controllers/checkController.test.js @@ -9,12 +9,11 @@ import { import jwt from "jsonwebtoken"; import { errorMessages, successMessages } from "../../utils/messages.js"; import sinon from "sinon"; -describe("Check Controller - createCheck", function () { +describe("Check Controller - createCheck", function() { let req, res, next, handleError; - beforeEach(function () { + beforeEach(function() { req = { - language: 'en', params: {}, body: {}, db: { @@ -29,17 +28,17 @@ describe("Check Controller - createCheck", function () { handleError = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); // Restore the original methods after each test }); - it("should reject with a validation if params are invalid", async function () { + it("should reject with a validation if params are invalid", async function() { await createCheck(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with a validation error if body is invalid", async function () { + it("should reject with a validation error if body is invalid", async function() { req.params = { monitorId: "monitorId", }; @@ -48,7 +47,7 @@ describe("Check Controller - createCheck", function () { expect(next.firstCall.args[0].status).to.equal(422); }); - it("should call next with error if data retrieval fails", async function () { + it("should call next with error if data retrieval fails", async function() { req.params = { monitorId: "monitorId", }; @@ -64,7 +63,7 @@ describe("Check Controller - createCheck", function () { expect(next.firstCall.args[0]).to.be.an("error"); }); - it("should return a success message if check is created", async function () { + it("should return a success message if check is created", async function() { req.params = { monitorId: "monitorId", }; @@ -81,7 +80,7 @@ describe("Check Controller - createCheck", function () { expect( res.json.calledWith({ success: true, - msg: successMessages.CHECK_CREATE(req.language), + msg: successMessages.CHECK_CREATE, data: { id: "123" }, }) ).to.be.true; @@ -89,10 +88,10 @@ describe("Check Controller - createCheck", function () { }); }); -describe("Check Controller - getChecks", function () { +describe("Check Controller - getChecks", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: {}, query: {}, @@ -108,17 +107,17 @@ describe("Check Controller - getChecks", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with a validation error if params are invalid", async function () { + it("should reject with a validation error if params are invalid", async function() { await getChecks(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should return a success message if checks are found", async function () { + it("should return a success message if checks are found", async function() { req.params = { monitorId: "monitorId", }; @@ -129,14 +128,14 @@ describe("Check Controller - getChecks", function () { expect( res.json.calledWith({ success: true, - msg: successMessages.CHECK_GET(req.language), + msg: successMessages.CHECK_GET, data: { checksCount: 1, checks: [{ id: "123" }] }, }) ).to.be.true; expect(next.notCalled).to.be.true; }); - it("should call next with error if data retrieval fails", async function () { + it("should call next with error if data retrieval fails", async function() { req.params = { monitorId: "monitorId", }; @@ -146,10 +145,10 @@ describe("Check Controller - getChecks", function () { }); }); -describe("Check Controller - getTeamChecks", function () { +describe("Check Controller - getTeamChecks", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: {}, query: {}, @@ -164,17 +163,17 @@ describe("Check Controller - getTeamChecks", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with a validation error if params are invalid", async function () { + it("should reject with a validation error if params are invalid", async function() { await getTeamChecks(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should return 200 and check data on successful validation and data retrieval", async function () { + it("should return 200 and check data on successful validation and data retrieval", async function() { req.params = { teamId: "1" }; const checkData = [{ id: 1, name: "Check 1" }]; req.db.getTeamChecks.resolves(checkData); @@ -185,13 +184,13 @@ describe("Check Controller - getTeamChecks", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.CHECK_GET(req.language), + msg: successMessages.CHECK_GET, data: checkData, }) ).to.be.true; }); - it("should call next with error if data retrieval fails", async function () { + it("should call next with error if data retrieval fails", async function() { req.params = { teamId: "1" }; req.db.getTeamChecks.rejects(new Error("Retrieval Error")); await getTeamChecks(req, res, next); @@ -202,10 +201,10 @@ describe("Check Controller - getTeamChecks", function () { }); }); -describe("Check Controller - deleteChecks", function () { +describe("Check Controller - deleteChecks", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: {}, db: { @@ -219,17 +218,17 @@ describe("Check Controller - deleteChecks", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { await deleteChecks(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should call next with error if data retrieval fails", async function () { + it("should call next with error if data retrieval fails", async function() { req.params = { monitorId: "1" }; req.db.deleteChecks.rejects(new Error("Deletion Error")); await deleteChecks(req, res, next); @@ -239,7 +238,7 @@ describe("Check Controller - deleteChecks", function () { expect(res.json.notCalled).to.be.true; }); - it("should delete checks successfully", async function () { + it("should delete checks successfully", async function() { req.params = { monitorId: "123" }; req.db.deleteChecks.resolves(1); await deleteChecks(req, res, next); @@ -248,17 +247,17 @@ describe("Check Controller - deleteChecks", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.CHECK_DELETE(req.language), + msg: successMessages.CHECK_DELETE, data: { deletedCount: 1 }, }) ).to.be.true; }); }); -describe("Check Controller - deleteChecksByTeamId", function () { +describe("Check Controller - deleteChecksByTeamId", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: {}, db: { @@ -272,17 +271,17 @@ describe("Check Controller - deleteChecksByTeamId", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { await deleteChecksByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should call next with error if data retrieval fails", async function () { + it("should call next with error if data retrieval fails", async function() { req.params = { teamId: "1" }; req.db.deleteChecksByTeamId.rejects(new Error("Deletion Error")); await deleteChecksByTeamId(req, res, next); @@ -292,7 +291,7 @@ describe("Check Controller - deleteChecksByTeamId", function () { expect(res.json.notCalled).to.be.true; }); - it("should delete checks successfully", async function () { + it("should delete checks successfully", async function() { req.params = { teamId: "123" }; req.db.deleteChecksByTeamId.resolves(1); await deleteChecksByTeamId(req, res, next); @@ -301,17 +300,17 @@ describe("Check Controller - deleteChecksByTeamId", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.CHECK_DELETE(req.language), + msg: successMessages.CHECK_DELETE, data: { deletedCount: 1 }, }) ).to.be.true; }); }); -describe("Check Controller - updateCheckTTL", function () { +describe("Check Controller - updateCheckTTL", function() { let stub, req, res, next; - beforeEach(function () { + beforeEach(function() { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -333,18 +332,18 @@ describe("Check Controller - updateCheckTTL", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); stub.restore(); }); - it("should reject if body validation fails", async function () { + it("should reject if body validation fails", async function() { await updateChecksTTL(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should throw a JwtError if verification fails", async function () { + it("should throw a JwtError if verification fails", async function() { stub.restore(); req.body = { ttl: 1, @@ -353,7 +352,7 @@ describe("Check Controller - updateCheckTTL", function () { expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); }); - it("should call next with error if data retrieval fails", async function () { + it("should call next with error if data retrieval fails", async function() { req.body = { ttl: 1, }; @@ -362,7 +361,7 @@ describe("Check Controller - updateCheckTTL", function () { expect(next.firstCall.args[0]).to.be.an("error"); }); - it("should update TTL successfully", async function () { + it("should update TTL successfully", async function() { req.body = { ttl: 1, }; @@ -373,7 +372,7 @@ describe("Check Controller - updateCheckTTL", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.CHECK_UPDATE_TTL(req.language), + msg: successMessages.CHECK_UPDATE_TTL, }) ).to.be.true; }); diff --git a/Server/tests/controllers/maintenanceWindowController.test.js b/Server/tests/controllers/maintenanceWindowController.test.js index c4b03b23b..974bc07be 100644 --- a/Server/tests/controllers/maintenanceWindowController.test.js +++ b/Server/tests/controllers/maintenanceWindowController.test.js @@ -11,12 +11,11 @@ import jwt from "jsonwebtoken"; import { successMessages } from "../../utils/messages.js"; import sinon from "sinon"; -describe("maintenanceWindowController - createMaintenanceWindows", function () { +describe("maintenanceWindowController - createMaintenanceWindows", function() { let req, res, next, stub; - beforeEach(function () { + beforeEach(function() { req = { - language: 'en', body: { monitors: ["66ff52e7c5911c61698ac724"], name: "window", @@ -42,11 +41,11 @@ describe("maintenanceWindowController - createMaintenanceWindows", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if body validation fails", async function () { + it("should reject with an error if body validation fails", async function() { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -57,14 +56,14 @@ describe("maintenanceWindowController - createMaintenanceWindows", function () { stub.restore(); }); - it("should reject with an error if jwt.verify fails", async function () { + it("should reject with an error if jwt.verify fails", async function() { stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); await createMaintenanceWindows(req, res, next); expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); stub.restore(); }); - it("should reject with an error DB operations fail", async function () { + it("should reject with an error DB operations fail", async function() { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -75,7 +74,7 @@ describe("maintenanceWindowController - createMaintenanceWindows", function () { stub.restore(); }); - it("should return success message if all operations are successful", async function () { + it("should return success message if all operations are successful", async function() { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -84,13 +83,13 @@ describe("maintenanceWindowController - createMaintenanceWindows", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE(req.language), + msg: successMessages.MAINTENANCE_WINDOW_CREATE, }) ).to.be.true; stub.restore(); }); - it("should return success message if all operations are successful with active set to undefined", async function () { + it("should return success message if all operations are successful with active set to undefined", async function() { req.body.active = undefined; stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; @@ -100,17 +99,17 @@ describe("maintenanceWindowController - createMaintenanceWindows", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_CREATE(req.language), + msg: successMessages.MAINTENANCE_WINDOW_CREATE, }) ).to.be.true; stub.restore(); }); }); -describe("maintenanceWindowController - getMaintenanceWindowById", function () { +describe("maintenanceWindowController - getMaintenanceWindowById", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { body: {}, params: { @@ -122,7 +121,6 @@ describe("maintenanceWindowController - getMaintenanceWindowById", function () { settingsService: { getSettings: sinon.stub().returns({ jwtSecret: "jwtSecret" }), }, - language: 'en', db: { getMaintenanceWindowById: sinon.stub(), }, @@ -134,38 +132,38 @@ describe("maintenanceWindowController - getMaintenanceWindowById", function () { next = sinon.stub(); }); - it("should reject if param validation fails", async function () { + it("should reject if param validation fails", async function() { req.params = {}; await getMaintenanceWindowById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject if DB operations fail", async function () { + it("should reject if DB operations fail", async function() { req.db.getMaintenanceWindowById.throws(new Error("DB error")); await getMaintenanceWindowById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message with data if all operations are successful", async function () { + it("should return success message with data if all operations are successful", async function() { req.db.getMaintenanceWindowById.returns({ id: "123" }); await getMaintenanceWindowById(req, res, next); expect(res.status.firstCall.args[0]).to.equal(200); expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID(req.language), + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID, data: { id: "123" }, }) ).to.be.true; }); }); -describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function () { +describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function() { let req, res, next, stub; - beforeEach(function () { + beforeEach(function() { req = { body: {}, params: {}, @@ -179,7 +177,6 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function db: { getMaintenanceWindowsByTeamId: sinon.stub(), }, - language: 'en', }; res = { status: sinon.stub().returnsThis(), @@ -188,7 +185,7 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function next = sinon.stub(); }); - it("should reject if query validation fails", async function () { + it("should reject if query validation fails", async function() { req.query = { invalid: 1, }; @@ -197,14 +194,14 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject if jwt.verify fails", async function () { + it("should reject if jwt.verify fails", async function() { stub = sinon.stub(jwt, "verify").throws(new jwt.JsonWebTokenError()); await getMaintenanceWindowsByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); stub.restore(); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -215,7 +212,7 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function stub.restore(); }); - it("should return success message with data if all operations are successful", async function () { + it("should return success message with data if all operations are successful", async function() { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -225,7 +222,7 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM(req.language), + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM, data: [{ id: jwt.verify().teamId }], }) ).to.be.true; @@ -233,10 +230,10 @@ describe("maintenanceWindowController - getMaintenanceWindowsByTeamId", function }); }); -describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", function () { +describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { body: {}, params: { @@ -260,25 +257,25 @@ describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", funct next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject if param validation fails", async function () { + it("should reject if param validation fails", async function() { req.params = {}; await getMaintenanceWindowsByMonitorId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.getMaintenanceWindowsByMonitorId.throws(new Error("DB error")); await getMaintenanceWindowsByMonitorId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message with data if all operations are successful", async function () { + it("should return success message with data if all operations are successful", async function() { const data = [{ monitorId: "123" }]; req.db.getMaintenanceWindowsByMonitorId.returns(data); await getMaintenanceWindowsByMonitorId(req, res, next); @@ -287,17 +284,17 @@ describe("maintenanceWindowController - getMaintenanceWindowsByMonitorId", funct expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_GET_BY_MONITOR(req.language), + msg: successMessages.MAINTENANCE_WINDOW_GET_BY_MONITOR, data: data, }) ).to.be.true; }); }); -describe("maintenanceWindowController - deleteMaintenanceWindow", function () { +describe("maintenanceWindowController - deleteMaintenanceWindow", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { body: {}, params: { @@ -321,47 +318,46 @@ describe("maintenanceWindowController - deleteMaintenanceWindow", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject if param validation fails", async function () { + it("should reject if param validation fails", async function() { req.params = {}; await deleteMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.deleteMaintenanceWindowById.throws(new Error("DB error")); await deleteMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message if all operations are successful", async function () { + it("should return success message if all operations are successful", async function() { await deleteMaintenanceWindow(req, res, next); expect(req.db.deleteMaintenanceWindowById.calledOnceWith(req.params.id)); expect(res.status.firstCall.args[0]).to.equal(200); expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_DELETE(req.language), + msg: successMessages.MAINTENANCE_WINDOW_DELETE, }) ).to.be.true; }); }); -describe("maintenanceWindowController - editMaintenanceWindow", function () { +describe("maintenanceWindowController - editMaintenanceWindow", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { body: { active: true, name: "test", }, - language: 'en', params: { id: "123", }, @@ -383,32 +379,32 @@ describe("maintenanceWindowController - editMaintenanceWindow", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject if param validation fails", async function () { + it("should reject if param validation fails", async function() { req.params = {}; await editMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject if body validation fails", async function () { + it("should reject if body validation fails", async function() { req.body = { invalid: 1 }; await editMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.editMaintenanceWindowById.throws(new Error("DB error")); await editMaintenanceWindow(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message with data if all operations are successful", async function () { + it("should return success message with data if all operations are successful", async function() { const data = { id: "123" }; req.db.editMaintenanceWindowById.returns(data); @@ -418,7 +414,7 @@ describe("maintenanceWindowController - editMaintenanceWindow", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MAINTENANCE_WINDOW_EDIT(req.language), + msg: successMessages.MAINTENANCE_WINDOW_EDIT, data: data, }) ).to.be.true; diff --git a/Server/tests/controllers/monitorController.test.js b/Server/tests/controllers/monitorController.test.js index ea35abac4..33440d897 100644 --- a/Server/tests/controllers/monitorController.test.js +++ b/Server/tests/controllers/monitorController.test.js @@ -19,16 +19,16 @@ import sinon from "sinon"; import { successMessages } from "../../utils/messages.js"; import logger from "../../utils/logger.js"; import axios from "axios"; +const SERVICE_NAME = "monitorController"; -describe("Monitor Controller - getAllMonitors", function () { +describe("Monitor Controller - getAllMonitors", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: {}, query: {}, body: {}, - language: 'en', db: { getAllMonitors: sinon.stub(), }, @@ -40,18 +40,18 @@ describe("Monitor Controller - getAllMonitors", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.getAllMonitors.throws(new Error("DB error")); await getAllMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function () { + it("should return success message and data if all operations succeed", async function() { const data = [{ monitor: "data" }]; req.db.getAllMonitors.returns(data); await getAllMonitors(req, res, next); @@ -59,21 +59,20 @@ describe("Monitor Controller - getAllMonitors", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_ALL(req.language), + msg: successMessages.MONITOR_GET_ALL, data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getAllMonitorsWithUptimeStats", function () { +describe("Monitor Controller - getAllMonitorsWithUptimeStats", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: {}, query: {}, body: {}, - language: 'en', db: { getAllMonitorsWithUptimeStats: sinon.stub(), }, @@ -85,18 +84,18 @@ describe("Monitor Controller - getAllMonitorsWithUptimeStats", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.getAllMonitorsWithUptimeStats.throws(new Error("DB error")); await getAllMonitorsWithUptimeStats(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function () { + it("should return success message and data if all operations succeed", async function() { const data = [{ monitor: "data" }]; req.db.getAllMonitorsWithUptimeStats.returns(data); await getAllMonitorsWithUptimeStats(req, res, next); @@ -104,24 +103,23 @@ describe("Monitor Controller - getAllMonitorsWithUptimeStats", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_ALL(req.language), + msg: successMessages.MONITOR_GET_ALL, data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getMonitorStatsById", function () { +describe("Monitor Controller - getMonitorStatsById", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: { monitorId: "123", }, query: {}, body: {}, - language: 'en', db: { getMonitorStatsById: sinon.stub(), }, @@ -133,32 +131,32 @@ describe("Monitor Controller - getMonitorStatsById", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await getMonitorStatsById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if query validation fails", async function () { + it("should reject with an error if query validation fails", async function() { req.query = { invalid: 1 }; await getMonitorStatsById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.getMonitorStatsById.throws(new Error("DB error")); await getMonitorStatsById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function () { + it("should return success message and data if all operations succeed", async function() { const data = [{ monitorStats: "data" }]; req.db.getMonitorStatsById.returns(data); await getMonitorStatsById(req, res, next); @@ -166,24 +164,23 @@ describe("Monitor Controller - getMonitorStatsById", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_STATS_BY_ID(req.language), + msg: successMessages.MONITOR_STATS_BY_ID, data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getMonitorCertificate", function () { +describe("Monitor Controller - getMonitorCertificate", function() { let req, res, next, fetchMonitorCertificate; - beforeEach(function () { + beforeEach(function() { req = { params: { monitorId: "123", }, query: {}, body: {}, - language: 'en', db: { getMonitorById: sinon.stub(), }, @@ -196,25 +193,25 @@ describe("Monitor Controller - getMonitorCertificate", function () { fetchMonitorCertificate = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await getMonitorCertificate(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getMonitorById operation fails", async function () { + it("should reject with an error if getMonitorById operation fails", async function() { req.db.getMonitorById.throws(new Error("DB error")); await getMonitorCertificate(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed with a valid cert", async function () { + it("should return success message and data if all operations succeed with a valid cert", async function() { req.db.getMonitorById.returns({ url: "https://www.google.com" }); const data = { certificate: "cert", validTo: "2024/08/08" }; fetchMonitorCertificate.returns(data); @@ -223,13 +220,13 @@ describe("Monitor Controller - getMonitorCertificate", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_CERTIFICATE(req.language), + msg: successMessages.MONITOR_CERTIFICATE, data: { certificateDate: new Date(data.validTo) }, }) ).to.be.true; }); - it("should return an error if fetchMonitorCertificate fails", async function () { + it("should return an error if fetchMonitorCertificate fails", async function() { req.db.getMonitorById.returns({ url: "https://www.google.com" }); fetchMonitorCertificate.throws(new Error("Certificate error")); await getMonitorCertificate(req, res, next, fetchMonitorCertificate); @@ -238,17 +235,16 @@ describe("Monitor Controller - getMonitorCertificate", function () { }); }); -describe("Monitor Controller - getMonitorById", function () { +describe("Monitor Controller - getMonitorById", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: { monitorId: "123", }, query: {}, body: {}, - language: 'en', db: { getMonitorById: sinon.stub(), }, @@ -260,32 +256,32 @@ describe("Monitor Controller - getMonitorById", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await getMonitorById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if query param validation fails", async function () { + it("should reject with an error if query param validation fails", async function() { req.query = { invalid: 1 }; await getMonitorById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.getMonitorById.throws(new Error("DB error")); await getMonitorById(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return 404 if a monitor is not found", async function () { + it("should return 404 if a monitor is not found", async function() { const error = new Error("Monitor not found"); error.status = 404; req.db.getMonitorById.throws(error); @@ -294,7 +290,7 @@ describe("Monitor Controller - getMonitorById", function () { expect(next.firstCall.args[0].status).to.equal(404); }); - it("should return success message and data if all operations succeed", async function () { + it("should return success message and data if all operations succeed", async function() { const data = { monitor: "data" }; req.db.getMonitorById.returns(data); await getMonitorById(req, res, next); @@ -302,24 +298,23 @@ describe("Monitor Controller - getMonitorById", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_BY_ID(req.language), + msg: successMessages.MONITOR_GET_BY_ID, data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function () { +describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: { teamId: "123", }, query: {}, body: {}, - language: 'en', db: { getMonitorsAndSummaryByTeamId: sinon.stub(), }, @@ -331,32 +326,32 @@ describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await getMonitorsAndSummaryByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if query validation fails", async function () { + it("should reject with an error if query validation fails", async function() { req.query = { invalid: 1 }; await getMonitorsAndSummaryByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.getMonitorsAndSummaryByTeamId.throws(new Error("DB error")); await getMonitorsAndSummaryByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function () { + it("should return success message and data if all operations succeed", async function() { const data = { monitors: "data", summary: "data" }; req.db.getMonitorsAndSummaryByTeamId.returns(data); await getMonitorsAndSummaryByTeamId(req, res, next); @@ -364,24 +359,23 @@ describe("Monitor Controller - getMonitorsAndSummaryByTeamId", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId, req.language), + msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - getMonitorsByTeamId", function () { +describe("Monitor Controller - getMonitorsByTeamId", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: { teamId: "123", }, query: {}, body: {}, - language: 'en', db: { getMonitorsByTeamId: sinon.stub(), }, @@ -393,32 +387,32 @@ describe("Monitor Controller - getMonitorsByTeamId", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await getMonitorsByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if query validation fails", async function () { + it("should reject with an error if query validation fails", async function() { req.query = { invalid: 1 }; await getMonitorsByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB operations fail", async function () { + it("should reject with an error if DB operations fail", async function() { req.db.getMonitorsByTeamId.throws(new Error("DB error")); await getMonitorsByTeamId(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should return success message and data if all operations succeed", async function () { + it("should return success message and data if all operations succeed", async function() { const data = { monitors: "data" }; req.db.getMonitorsByTeamId.returns(data); await getMonitorsByTeamId(req, res, next); @@ -426,17 +420,17 @@ describe("Monitor Controller - getMonitorsByTeamId", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId, req.language), + msg: successMessages.MONITOR_GET_BY_USER_ID(req.params.teamId), data: data, }) ).to.be.true; }); }); -describe("Monitor Controller - createMonitor", function () { +describe("Monitor Controller - createMonitor", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: {}, query: {}, @@ -449,7 +443,6 @@ describe("Monitor Controller - createMonitor", function () { url: "https://example.com", notifications: [{ email: "example@example.com" }], }, - language: 'en', db: { createMonitor: sinon.stub(), createNotification: sinon.stub(), @@ -465,25 +458,25 @@ describe("Monitor Controller - createMonitor", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if body validation fails", async function () { + it("should reject with an error if body validation fails", async function() { req.body = {}; await createMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB createMonitor operation fail", async function () { + it("should reject with an error if DB createMonitor operation fail", async function() { req.db.createMonitor.throws(new Error("DB error")); await createMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if DB createNotification operation fail", async function () { + it("should reject with an error if DB createNotification operation fail", async function() { req.db.createNotification.throws(new Error("DB error")); req.db.createMonitor.returns({ _id: "123" }); await createMonitor(req, res, next); @@ -491,7 +484,7 @@ describe("Monitor Controller - createMonitor", function () { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if monitor.save operation fail", async function () { + it("should reject with an error if monitor.save operation fail", async function() { req.db.createMonitor.returns({ _id: "123", save: sinon.stub().throws(new Error("Monitor save error")), @@ -501,7 +494,7 @@ describe("Monitor Controller - createMonitor", function () { expect(next.firstCall.args[0].message).to.equal("Monitor save error"); }); - it("should throw an error if addJob operation fails", async function () { + it("should throw an error if addJob operation fails", async function() { req.db.createMonitor.returns({ _id: "123", save: sinon.stub() }); req.jobQueue.addJob.throws(new Error("Job error")); await createMonitor(req, res, next); @@ -509,7 +502,7 @@ describe("Monitor Controller - createMonitor", function () { expect(next.firstCall.args[0].message).to.equal("Job error"); }); - it("should return success message and data if all operations succeed", async function () { + it("should return success message and data if all operations succeed", async function() { const monitor = { _id: "123", save: sinon.stub() }; req.db.createMonitor.returns(monitor); await createMonitor(req, res, next); @@ -517,28 +510,28 @@ describe("Monitor Controller - createMonitor", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_CREATE(req.language), + msg: successMessages.MONITOR_CREATE, data: monitor, }) ).to.be.true; }); }); -describe("Monitor Controller - checkEndpointResolution", function () { +describe("Monitor Controller - checkEndpointResolution", function() { let req, res, next, axiosGetStub; - beforeEach(function () { + beforeEach(function() { req = { query: { monitorURL: "https://example.com" } }; res = { status: sinon.stub().returnsThis(), json: sinon.stub() }; next = sinon.stub(); axiosGetStub = sinon.stub(axios, "get"); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should resolve the URL successfully", async function () { + it("should resolve the URL successfully", async function() { axiosGetStub.resolves({ status: 200, statusText: "OK" }); await checkEndpointResolution(req, res, next); expect(res.status.calledWith(200)).to.be.true; @@ -553,7 +546,7 @@ describe("Monitor Controller - checkEndpointResolution", function () { expect(next.called).to.be.false; }); - it("should return an error if endpoint resolution fails", async function () { + it("should return an error if endpoint resolution fails", async function() { const axiosError = new Error("resolution failed"); axiosError.code = "ENOTFOUND"; axiosGetStub.rejects(axiosError); @@ -566,7 +559,7 @@ describe("Monitor Controller - checkEndpointResolution", function () { expect(errorPassedToNext.status).to.equal(500); }); - it("should reject with an error if query validation fails", async function () { + it("should reject with an error if query validation fails", async function() { req.query.monitorURL = "invalid-url"; await checkEndpointResolution(req, res, next); expect(next.calledOnce).to.be.true; @@ -577,17 +570,16 @@ describe("Monitor Controller - checkEndpointResolution", function () { }); }); -describe("Monitor Controller - deleteMonitor", function () { +describe("Monitor Controller - deleteMonitor", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { params: { monitorId: "123", }, query: {}, body: {}, - language: 'en', db: { deleteMonitor: sinon.stub(), deleteChecks: sinon.stub(), @@ -606,25 +598,25 @@ describe("Monitor Controller - deleteMonitor", function () { sinon.stub(logger, "error"); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await deleteMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if DB deleteMonitor operation fail", async function () { + it("should reject with an error if DB deleteMonitor operation fail", async function() { req.db.deleteMonitor.throws(new Error("DB error")); await deleteMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should log an error if deleteJob throws an error", async function () { + it("should log an error if deleteJob throws an error", async function() { const error = new Error("Job error"); const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); @@ -636,7 +628,7 @@ describe("Monitor Controller - deleteMonitor", function () { ); }); - it("should log an error if deleteChecks throws an error", async function () { + it("should log an error if deleteChecks throws an error", async function() { const error = new Error("Checks error"); const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); @@ -648,7 +640,7 @@ describe("Monitor Controller - deleteMonitor", function () { ); }); - it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function () { + it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function() { const error = new Error("PageSpeed error"); const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); @@ -660,7 +652,7 @@ describe("Monitor Controller - deleteMonitor", function () { ); }); - it("should log an error if deleteNotificationsByMonitorId throws an error", async function () { + it("should log an error if deleteNotificationsByMonitorId throws an error", async function() { const error = new Error("Notifications error"); const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); @@ -672,7 +664,7 @@ describe("Monitor Controller - deleteMonitor", function () { ); }); - it("should return success message if all operations succeed", async function () { + it("should return success message if all operations succeed", async function() { const monitor = { name: "test_monitor", _id: "123" }; req.db.deleteMonitor.returns(monitor); await deleteMonitor(req, res, next); @@ -680,16 +672,16 @@ describe("Monitor Controller - deleteMonitor", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_DELETE(req.language), + msg: successMessages.MONITOR_DELETE, }) ).to.be.true; }); }); -describe("Monitor Controller - deleteAllMonitors", function () { +describe("Monitor Controller - deleteAllMonitors", function() { let req, res, next, stub; - beforeEach(function () { + beforeEach(function() { stub = sinon.stub(jwt, "verify").callsFake(() => { return { teamId: "123" }; }); @@ -702,7 +694,6 @@ describe("Monitor Controller - deleteAllMonitors", function () { }, query: {}, body: {}, - language: 'en', db: { deleteAllMonitors: sinon.stub(), deleteChecks: sinon.stub(), @@ -724,12 +715,12 @@ describe("Monitor Controller - deleteAllMonitors", function () { sinon.stub(logger, "error"); }); - afterEach(function () { + afterEach(function() { sinon.restore(); stub.restore(); }); - it("should reject with an error if getTokenFromHeaders throws an error", async function () { + it("should reject with an error if getTokenFromHeaders throws an error", async function() { req.headers = {}; await deleteAllMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); @@ -737,14 +728,14 @@ describe("Monitor Controller - deleteAllMonitors", function () { expect(next.firstCall.args[0].status).to.equal(500); }); - it("should reject with an error if token validation fails", async function () { + it("should reject with an error if token validation fails", async function() { stub.restore(); req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); await deleteAllMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); }); - it("should reject with an error if DB deleteAllMonitors operation fail", async function () { + it("should reject with an error if DB deleteAllMonitors operation fail", async function() { req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.deleteAllMonitors.throws(new Error("DB error")); await deleteAllMonitors(req, res, next); @@ -752,7 +743,7 @@ describe("Monitor Controller - deleteAllMonitors", function () { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should log an error if deleteChecks throws an error", async function () { + it("should log an error if deleteChecks throws an error", async function() { const monitors = [{ name: "test_monitor", _id: "123" }]; req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); @@ -765,7 +756,7 @@ describe("Monitor Controller - deleteAllMonitors", function () { ); }); - it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function () { + it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function() { const monitors = [{ name: "test_monitor", _id: "123" }]; req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); @@ -778,7 +769,7 @@ describe("Monitor Controller - deleteAllMonitors", function () { ); }); - it("should log an error if deleteNotificationsByMonitorId throws an error", async function () { + it("should log an error if deleteNotificationsByMonitorId throws an error", async function() { const monitors = [{ name: "test_monitor", _id: "123" }]; req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.deleteAllMonitors.returns({ monitors, deletedCount: 1 }); @@ -791,25 +782,27 @@ describe("Monitor Controller - deleteAllMonitors", function () { ); }); - it("should return success message if all operations succeed", async function () { + it("should return success message if all operations succeed", async function() { req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); - const { monitors, deletedCount } = { monitors: [{ name: "test_monitor", _id: "123" }], deletedCount: 1 }; - req.db.deleteAllMonitors.returns({ monitors, deletedCount }); + req.db.deleteAllMonitors.returns({ + monitors: [{ name: "test_monitor", _id: "123" }], + deletedCount: 1, + }); await deleteAllMonitors(req, res, next); expect(res.status.firstCall.args[0]).to.equal(200); expect( res.json.calledOnceWith({ success: true, - msg: `Deleted ${deletedCount} monitors`, + msg: "Deleted 1 monitors", }) ).to.be.true; }); }); -describe("Monitor Controller - editMonitor", function () { +describe("Monitor Controller - editMonitor", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { headers: {}, params: { @@ -819,7 +812,6 @@ describe("Monitor Controller - editMonitor", function () { body: { notifications: [{ email: "example@example.com" }], }, - language: 'en', db: { getMonitorById: sinon.stub(), editMonitor: sinon.stub(), @@ -841,32 +833,32 @@ describe("Monitor Controller - editMonitor", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await editMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if body validation fails", async function () { + it("should reject with an error if body validation fails", async function() { req.body = { invalid: 1 }; await editMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getMonitorById operation fails", async function () { + it("should reject with an error if getMonitorById operation fails", async function() { req.db.getMonitorById.throws(new Error("DB error")); await editMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if editMonitor operation fails", async function () { + it("should reject with an error if editMonitor operation fails", async function() { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.throws(new Error("DB error")); await editMonitor(req, res, next); @@ -874,7 +866,7 @@ describe("Monitor Controller - editMonitor", function () { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if deleteNotificationsByMonitorId operation fails", async function () { + it("should reject with an error if deleteNotificationsByMonitorId operation fails", async function() { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns({ _id: "123" }); req.db.deleteNotificationsByMonitorId.throws(new Error("DB error")); @@ -883,7 +875,7 @@ describe("Monitor Controller - editMonitor", function () { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if createNotification operation fails", async function () { + it("should reject with an error if createNotification operation fails", async function() { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns({ _id: "123" }); req.db.createNotification.throws(new Error("DB error")); @@ -892,7 +884,7 @@ describe("Monitor Controller - editMonitor", function () { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if deleteJob operation fails", async function () { + it("should reject with an error if deleteJob operation fails", async function() { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns({ _id: "123" }); req.jobQueue.deleteJob.throws(new Error("Job error")); @@ -901,7 +893,7 @@ describe("Monitor Controller - editMonitor", function () { expect(next.firstCall.args[0].message).to.equal("Job error"); }); - it("should reject with an error if addJob operation fails", async function () { + it("should reject with an error if addJob operation fails", async function() { req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns({ _id: "123" }); req.jobQueue.addJob.throws(new Error("Add Job error")); @@ -910,7 +902,7 @@ describe("Monitor Controller - editMonitor", function () { expect(next.firstCall.args[0].message).to.equal("Add Job error"); }); - it("should return success message with data if all operations succeed", async function () { + it("should return success message with data if all operations succeed", async function() { const monitor = { _id: "123" }; req.db.getMonitorById.returns({ teamId: "123" }); req.db.editMonitor.returns(monitor); @@ -919,17 +911,17 @@ describe("Monitor Controller - editMonitor", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_EDIT(req.language), + msg: successMessages.MONITOR_EDIT, data: monitor, }) ).to.be.true; }); }); -describe("Monitor Controller - pauseMonitor", function () { +describe("Monitor Controller - pauseMonitor", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { headers: {}, params: { @@ -937,7 +929,6 @@ describe("Monitor Controller - pauseMonitor", function () { }, query: {}, body: {}, - language: 'en', db: { getMonitorById: sinon.stub(), }, @@ -956,25 +947,25 @@ describe("Monitor Controller - pauseMonitor", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should reject with an error if param validation fails", async function () { + it("should reject with an error if param validation fails", async function() { req.params = {}; await pauseMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].status).to.equal(422); }); - it("should reject with an error if getMonitorById operation fails", async function () { + it("should reject with an error if getMonitorById operation fails", async function() { req.db.getMonitorById.throws(new Error("DB error")); await pauseMonitor(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if deleteJob operation fails", async function () { + it("should reject with an error if deleteJob operation fails", async function() { const monitor = { _id: req.params.monitorId, isActive: true }; req.db.getMonitorById.returns(monitor); req.jobQueue.deleteJob.throws(new Error("Delete Job error")); @@ -983,7 +974,7 @@ describe("Monitor Controller - pauseMonitor", function () { expect(next.firstCall.args[0].message).to.equal("Delete Job error"); }); - it("should reject with an error if addJob operation fails", async function () { + it("should reject with an error if addJob operation fails", async function() { const monitor = { _id: req.params.monitorId, isActive: false }; req.db.getMonitorById.returns(monitor); req.jobQueue.addJob.throws(new Error("Add Job error")); @@ -992,7 +983,7 @@ describe("Monitor Controller - pauseMonitor", function () { expect(next.firstCall.args[0].message).to.equal("Add Job error"); }); - it("should reject with an error if monitor.save operation fails", async function () { + it("should reject with an error if monitor.save operation fails", async function() { const monitor = { _id: req.params.monitorId, active: false, @@ -1004,7 +995,7 @@ describe("Monitor Controller - pauseMonitor", function () { expect(next.firstCall.args[0].message).to.equal("Save error"); }); - it("should return success pause message with data if all operations succeed with inactive monitor", async function () { + it("should return success pause message with data if all operations succeed with inactive monitor", async function() { const monitor = { _id: req.params.monitorId, isActive: false, @@ -1016,13 +1007,13 @@ describe("Monitor Controller - pauseMonitor", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_PAUSE(req.language), + msg: successMessages.MONITOR_PAUSE, data: monitor, }) ).to.be.true; }); - it("should return success resume message with data if all operations succeed with active monitor", async function () { + it("should return success resume message with data if all operations succeed with active monitor", async function() { const monitor = { _id: req.params.monitorId, isActive: true, @@ -1034,17 +1025,17 @@ describe("Monitor Controller - pauseMonitor", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_RESUME(req.language), + msg: successMessages.MONITOR_RESUME, data: monitor, }) ).to.be.true; }); }); -describe("Monitor Controller - addDemoMonitors", function () { +describe("Monitor Controller - addDemoMonitors", function() { let req, res, next, stub; - beforeEach(function () { + beforeEach(function() { stub = sinon.stub(jwt, "verify").callsFake(() => { return { _id: "123", teamId: "123" }; }); @@ -1055,7 +1046,6 @@ describe("Monitor Controller - addDemoMonitors", function () { params: {}, query: {}, body: {}, - language: 'en', db: { addDemoMonitors: sinon.stub(), }, @@ -1073,12 +1063,12 @@ describe("Monitor Controller - addDemoMonitors", function () { next = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); stub.restore(); }); - it("should reject with an error if getTokenFromHeaders fails", async function () { + it("should reject with an error if getTokenFromHeaders fails", async function() { req.headers = {}; await addDemoMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); @@ -1086,21 +1076,21 @@ describe("Monitor Controller - addDemoMonitors", function () { expect(next.firstCall.args[0].status).to.equal(500); }); - it("should reject with an error if getting settings fails", async function () { + it("should reject with an error if getting settings fails", async function() { req.settingsService.getSettings.throws(new Error("Settings error")); await addDemoMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("Settings error"); }); - it("should reject with an error if JWT validation fails", async function () { + it("should reject with an error if JWT validation fails", async function() { stub.restore(); req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); await addDemoMonitors(req, res, next); expect(next.firstCall.args[0]).to.be.instanceOf(jwt.JsonWebTokenError); }); - it("should reject with an error if addDemoMonitors operation fails", async function () { + it("should reject with an error if addDemoMonitors operation fails", async function() { req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.addDemoMonitors.throws(new Error("DB error")); await addDemoMonitors(req, res, next); @@ -1108,7 +1098,7 @@ describe("Monitor Controller - addDemoMonitors", function () { expect(next.firstCall.args[0].message).to.equal("DB error"); }); - it("should reject with an error if addJob operation fails", async function () { + it("should reject with an error if addJob operation fails", async function() { req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.addDemoMonitors.returns([{ _id: "123" }]); req.jobQueue.addJob.throws(new Error("Add Job error")); @@ -1117,7 +1107,7 @@ describe("Monitor Controller - addDemoMonitors", function () { expect(next.firstCall.args[0].message).to.equal("Add Job error"); }); - it("should return success message with data if all operations succeed", async function () { + it("should return success message with data if all operations succeed", async function() { const monitors = [{ _id: "123" }]; req.settingsService.getSettings.returns({ jwtSecret: "my_secret" }); req.db.addDemoMonitors.returns(monitors); @@ -1126,7 +1116,7 @@ describe("Monitor Controller - addDemoMonitors", function () { expect( res.json.calledOnceWith({ success: true, - msg: successMessages.MONITOR_DEMO_ADDED(req.language), + msg: successMessages.MONITOR_DEMO_ADDED, data: monitors.length, }) ).to.be.true; diff --git a/Server/tests/controllers/queueController.test.js b/Server/tests/controllers/queueController.test.js index 72450a0bd..b04504186 100644 --- a/Server/tests/controllers/queueController.test.js +++ b/Server/tests/controllers/queueController.test.js @@ -8,10 +8,10 @@ import { import { successMessages } from "../../utils/messages.js"; import sinon from "sinon"; -describe("Queue Controller - getMetrics", function () { +describe("Queue Controller - getMetrics", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { headers: {}, params: {}, @@ -32,14 +32,14 @@ describe("Queue Controller - getMetrics", function () { sinon.restore(); }); - it("should throw an error if getMetrics throws an error", async function () { + it("should throw an error if getMetrics throws an error", async function() { req.jobQueue.getMetrics.throws(new Error("getMetrics error")); await getMetrics(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getMetrics error"); }); - it("should return a success message and data if getMetrics is successful", async function () { + it("should return a success message and data if getMetrics is successful", async function() { const data = { data: "metrics" }; req.jobQueue.getMetrics.returns(data); await getMetrics(req, res, next); @@ -52,10 +52,10 @@ describe("Queue Controller - getMetrics", function () { }); }); -describe("Queue Controller - getJobs", function () { +describe("Queue Controller - getJobs", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { headers: {}, params: {}, @@ -76,14 +76,14 @@ describe("Queue Controller - getJobs", function () { sinon.restore(); }); - it("should reject with an error if getJobs throws an error", async function () { + it("should reject with an error if getJobs throws an error", async function() { req.jobQueue.getJobStats.throws(new Error("getJobs error")); await getJobs(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("getJobs error"); }); - it("should return a success message and data if getJobs is successful", async function () { + it("should return a success message and data if getJobs is successful", async function() { const data = { data: "jobs" }; req.jobQueue.getJobStats.returns(data); await getJobs(req, res, next); @@ -96,10 +96,10 @@ describe("Queue Controller - getJobs", function () { }); }); -describe("Queue Controller - addJob", function () { +describe("Queue Controller - addJob", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { headers: {}, params: {}, @@ -120,14 +120,14 @@ describe("Queue Controller - addJob", function () { sinon.restore(); }); - it("should reject with an error if addJob throws an error", async function () { + it("should reject with an error if addJob throws an error", async function() { req.jobQueue.addJob.throws(new Error("addJob error")); await addJob(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("addJob error"); }); - it("should return a success message if addJob is successful", async function () { + it("should return a success message if addJob is successful", async function() { req.jobQueue.addJob.resolves(); await addJob(req, res, next); expect(res.status.firstCall.args[0]).to.equal(200); @@ -138,10 +138,10 @@ describe("Queue Controller - addJob", function () { }); }); -describe("Queue Controller - obliterateQueue", function () { +describe("Queue Controller - obliterateQueue", function() { let req, res, next; - beforeEach(function () { + beforeEach(function() { req = { headers: {}, params: {}, @@ -162,14 +162,14 @@ describe("Queue Controller - obliterateQueue", function () { sinon.restore(); }); - it("should reject with an error if obliterateQueue throws an error", async function () { + it("should reject with an error if obliterateQueue throws an error", async function() { req.jobQueue.obliterate.throws(new Error("obliterateQueue error")); await obliterateQueue(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("obliterateQueue error"); }); - it("should return a success message if obliterateQueue is successful", async function () { + it("should return a success message if obliterateQueue is successful", async function() { req.jobQueue.obliterate.resolves(); await obliterateQueue(req, res, next); expect(res.status.firstCall.args[0]).to.equal(200); diff --git a/Server/tests/db/inviteModule.test.js b/Server/tests/db/inviteModule.test.js index febaf6ab7..164840826 100644 --- a/Server/tests/db/inviteModule.test.js +++ b/Server/tests/db/inviteModule.test.js @@ -7,33 +7,32 @@ import { } from "../../db/mongo/modules/inviteModule.js"; import { errorMessages } from "../../utils/messages.js"; -describe("Invite Module", function () { +describe("Invite Module", function() { const mockUserData = { email: "test@test.com", teamId: "123", role: ["admin"], token: "123", }; - const mockLanguage = 'en'; const mockInviteToken = { _id: 123, time: 123 }; let inviteTokenDeleteManyStub, inviteTokenSaveStub, inviteTokenFindOneStub, inviteTokenFindOneAndDeleteStub; - beforeEach(function () { + beforeEach(function() { inviteTokenDeleteManyStub = sinon.stub(InviteToken, "deleteMany"); inviteTokenSaveStub = sinon.stub(InviteToken.prototype, "save"); inviteTokenFindOneStub = sinon.stub(InviteToken, "findOne"); inviteTokenFindOneAndDeleteStub = sinon.stub(InviteToken, "findOneAndDelete"); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - describe("requestInviteToken", function () { - it("should return a new invite token", async function () { + describe("requestInviteToken", function() { + it("should return a new invite token", async function() { inviteTokenDeleteManyStub.resolves(); inviteTokenSaveStub.resolves(); const inviteToken = await requestInviteToken(mockUserData); @@ -42,7 +41,7 @@ describe("Invite Module", function () { expect(inviteToken.token).to.exist; }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("test error"); inviteTokenDeleteManyStub.rejects(err); try { @@ -53,23 +52,23 @@ describe("Invite Module", function () { }); }); - describe("getInviteToken", function () { - it("should return an invite token", async function () { + describe("getInviteToken", function() { + it("should return an invite token", async function() { inviteTokenFindOneStub.resolves(mockInviteToken); const inviteToken = await getInviteToken(mockUserData.token); expect(inviteToken).to.deep.equal(mockInviteToken); }); - it("should handle a token not found", async function () { + it("should handle a token not found", async function() { inviteTokenFindOneStub.resolves(null); try { await getInviteToken(mockUserData.token); } catch (error) { - expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND(mockLanguage)); + expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND); } }); - it("should handle DB errors", async function () { + it("should handle DB errors", async function() { const err = new Error("test error"); inviteTokenFindOneStub.rejects(err); try { @@ -81,23 +80,23 @@ describe("Invite Module", function () { }); }); - describe("getInviteTokenAndDelete", function () { - it("should return a deleted invite", async function () { + describe("getInviteTokenAndDelete", function() { + it("should return a deleted invite", async function() { inviteTokenFindOneAndDeleteStub.resolves(mockInviteToken); const deletedInvite = await getInviteTokenAndDelete(mockUserData.token); expect(deletedInvite).to.deep.equal(mockInviteToken); }); - it("should handle a token not found", async function () { + it("should handle a token not found", async function() { inviteTokenFindOneAndDeleteStub.resolves(null); try { await getInviteTokenAndDelete(mockUserData.token); } catch (error) { - expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND(mockLanguage)); + expect(error.message).to.equal(errorMessages.AUTH_INVITE_NOT_FOUND); } }); - it("should handle DB errors", async function () { + it("should handle DB errors", async function() { const err = new Error("test error"); inviteTokenFindOneAndDeleteStub.rejects(err); try { diff --git a/Server/tests/db/monitorModule.test.js b/Server/tests/db/monitorModule.test.js index e60688f3f..50782494a 100644 --- a/Server/tests/db/monitorModule.test.js +++ b/Server/tests/db/monitorModule.test.js @@ -31,7 +31,7 @@ import { calculateGroupStats, } from "../../db/mongo/modules/monitorModule.js"; -describe("monitorModule", function () { +describe("monitorModule", function() { let monitorFindStub, monitorFindByIdStub, monitorFindByIdAndUpdateStub, @@ -43,7 +43,7 @@ describe("monitorModule", function () { pageSpeedCheckFindStub, hardwareCheckFindStub; - beforeEach(function () { + beforeEach(function() { monitorFindStub = sinon.stub(Monitor, "find"); monitorFindByIdStub = sinon.stub(Monitor, "findById"); monitorFindByIdAndUpdateStub = sinon.stub(Monitor, "findByIdAndUpdate"); @@ -63,12 +63,12 @@ describe("monitorModule", function () { }); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - describe("getAllMonitors", function () { - it("should return all monitors", async function () { + describe("getAllMonitors", function() { + it("should return all monitors", async function() { const mockMonitors = [ { _id: "1", name: "Monitor 1", url: "test1.com" }, { _id: "2", name: "Monitor 2", url: "test2.com" }, @@ -81,13 +81,13 @@ describe("monitorModule", function () { expect(monitorFindStub.firstCall.args).to.deep.equal([]); }); - it("should handle empty results", async function () { + it("should handle empty results", async function() { monitorFindStub.returns([]); const result = await getAllMonitors(); expect(result).to.be.an("array").that.is.empty; }); - it("should throw error when database fails", async function () { + it("should throw error when database fails", async function() { // Arrange const error = new Error("Database error"); error.service = "MonitorModule"; @@ -105,8 +105,8 @@ describe("monitorModule", function () { }); }); - describe("getAllMonitorsWithUptimeStats", function () { - it("should return monitors with uptime stats for different time periods", async function () { + describe("getAllMonitorsWithUptimeStats", function() { + it("should return monitors with uptime stats for different time periods", async function() { // Mock data const mockMonitors = [ { @@ -152,7 +152,7 @@ describe("monitorModule", function () { expect(monitor["90"]).to.equal(75); }); - it("should return monitors with stats for pagespeed type", async function () { + it("should return monitors with stats for pagespeed type", async function() { // Mock data const mockMonitors = [ { @@ -198,7 +198,7 @@ describe("monitorModule", function () { expect(monitor["90"]).to.equal(75); }); - it("should return monitors with stats for hardware type", async function () { + it("should return monitors with stats for hardware type", async function() { // Mock data const mockMonitors = [ { @@ -244,7 +244,7 @@ describe("monitorModule", function () { expect(monitor["90"]).to.equal(75); }); - it("should handle errors appropriately", async function () { + it("should handle errors appropriately", async function() { // Setup stub to throw error monitorFindStub.rejects(new Error("Database error")); @@ -258,7 +258,7 @@ describe("monitorModule", function () { } }); - it("should handle empty monitor list", async function () { + it("should handle empty monitor list", async function() { monitorFindStub.resolves([]); const result = await getAllMonitorsWithUptimeStats(); @@ -267,7 +267,7 @@ describe("monitorModule", function () { expect(result).to.have.lengthOf(0); }); - it("should handle monitor with no checks", async function () { + it("should handle monitor with no checks", async function() { const mockMonitors = [ { _id: "monitor1", @@ -292,28 +292,28 @@ describe("monitorModule", function () { }); }); - describe("calculateUptimeDuration", function () { + describe("calculateUptimeDuration", function() { let clock; const NOW = new Date("2024-01-01T12:00:00Z").getTime(); - beforeEach(function () { + beforeEach(function() { // Fix the current time clock = sinon.useFakeTimers(NOW); }); - afterEach(function () { + afterEach(function() { clock.restore(); }); - it("should return 0 when checks array is empty", function () { + it("should return 0 when checks array is empty", function() { expect(calculateUptimeDuration([])).to.equal(0); }); - it("should return 0 when checks array is null", function () { + it("should return 0 when checks array is null", function() { expect(calculateUptimeDuration(null)).to.equal(0); }); - it("should calculate uptime from last down check to most recent check", function () { + it("should calculate uptime from last down check to most recent check", function() { const checks = [ { status: true, createdAt: "2024-01-01T11:00:00Z" }, // Most recent { status: true, createdAt: "2024-01-01T10:00:00Z" }, @@ -325,7 +325,7 @@ describe("monitorModule", function () { expect(calculateUptimeDuration(checks)).to.equal(7200000); }); - it("should calculate uptime from first check when no down checks exist", function () { + it("should calculate uptime from first check when no down checks exist", function() { const checks = [ { status: true, createdAt: "2024-01-01T11:00:00Z" }, { status: true, createdAt: "2024-01-01T10:00:00Z" }, @@ -337,28 +337,28 @@ describe("monitorModule", function () { }); }); - describe("getLastChecked", function () { + describe("getLastChecked", function() { let clock; const NOW = new Date("2024-01-01T12:00:00Z").getTime(); - beforeEach(function () { + beforeEach(function() { // Fix the current time clock = sinon.useFakeTimers(NOW); }); - afterEach(function () { + afterEach(function() { clock.restore(); }); - it("should return 0 when checks array is empty", function () { + it("should return 0 when checks array is empty", function() { expect(getLastChecked([])).to.equal(0); }); - it("should return 0 when checks array is null", function () { + it("should return 0 when checks array is null", function() { expect(getLastChecked(null)).to.equal(0); }); - it("should return time difference between now and most recent check", function () { + it("should return time difference between now and most recent check", function() { const checks = [ { createdAt: "2024-01-01T11:30:00Z" }, // 30 minutes ago { createdAt: "2024-01-01T11:00:00Z" }, @@ -369,7 +369,7 @@ describe("monitorModule", function () { expect(getLastChecked(checks)).to.equal(1800000); }); - it("should handle checks from different days", function () { + it("should handle checks from different days", function() { const checks = [ { createdAt: "2023-12-31T12:00:00Z" }, // 24 hours ago { createdAt: "2023-12-30T12:00:00Z" }, @@ -380,16 +380,16 @@ describe("monitorModule", function () { }); }); - describe("getLatestResponseTime", function () { - it("should return 0 when checks array is empty", function () { + describe("getLatestResponseTime", function() { + it("should return 0 when checks array is empty", function() { expect(getLatestResponseTime([])).to.equal(0); }); - it("should return 0 when checks array is null", function () { + it("should return 0 when checks array is null", function() { expect(getLatestResponseTime(null)).to.equal(0); }); - it("should return response time from most recent check", function () { + it("should return response time from most recent check", function() { const checks = [ { responseTime: 150, createdAt: "2024-01-01T11:30:00Z" }, // Most recent { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }, @@ -399,7 +399,7 @@ describe("monitorModule", function () { expect(getLatestResponseTime(checks)).to.equal(150); }); - it("should handle missing responseTime in checks", function () { + it("should handle missing responseTime in checks", function() { const checks = [ { createdAt: "2024-01-01T11:30:00Z" }, { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }, @@ -409,16 +409,16 @@ describe("monitorModule", function () { }); }); - describe("getAverageResponseTime", function () { - it("should return 0 when checks array is empty", function () { + describe("getAverageResponseTime", function() { + it("should return 0 when checks array is empty", function() { expect(getAverageResponseTime([])).to.equal(0); }); - it("should return 0 when checks array is null", function () { + it("should return 0 when checks array is null", function() { expect(getAverageResponseTime(null)).to.equal(0); }); - it("should calculate average response time from all checks", function () { + it("should calculate average response time from all checks", function() { const checks = [ { responseTime: 100, createdAt: "2024-01-01T11:30:00Z" }, { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }, @@ -429,7 +429,7 @@ describe("monitorModule", function () { expect(getAverageResponseTime(checks)).to.equal(200); }); - it("should handle missing responseTime in some checks", function () { + it("should handle missing responseTime in some checks", function() { const checks = [ { responseTime: 100, createdAt: "2024-01-01T11:30:00Z" }, { createdAt: "2024-01-01T11:00:00Z" }, @@ -440,7 +440,7 @@ describe("monitorModule", function () { expect(getAverageResponseTime(checks)).to.equal(200); }); - it("should return 0 when no checks have responseTime", function () { + it("should return 0 when no checks have responseTime", function() { const checks = [ { createdAt: "2024-01-01T11:30:00Z" }, { createdAt: "2024-01-01T11:00:00Z" }, @@ -450,26 +450,26 @@ describe("monitorModule", function () { }); }); - describe("getUptimePercentage", function () { - it("should return 0 when checks array is empty", function () { + describe("getUptimePercentage", function() { + it("should return 0 when checks array is empty", function() { expect(getUptimePercentage([])).to.equal(0); }); - it("should return 0 when checks array is null", function () { + it("should return 0 when checks array is null", function() { expect(getUptimePercentage(null)).to.equal(0); }); - it("should return 100 when all checks are up", function () { + it("should return 100 when all checks are up", function() { const checks = [{ status: true }, { status: true }, { status: true }]; expect(getUptimePercentage(checks)).to.equal(100); }); - it("should return 0 when all checks are down", function () { + it("should return 0 when all checks are down", function() { const checks = [{ status: false }, { status: false }, { status: false }]; expect(getUptimePercentage(checks)).to.equal(0); }); - it("should calculate correct percentage for mixed status checks", function () { + it("should calculate correct percentage for mixed status checks", function() { const checks = [ { status: true }, { status: false }, @@ -480,33 +480,33 @@ describe("monitorModule", function () { expect(getUptimePercentage(checks)).to.equal(75); }); - it("should handle undefined status values", function () { + it("should handle undefined status values", function() { const checks = [{ status: true }, { status: undefined }, { status: true }]; // 2 up out of 3 total ≈ 66.67% expect(getUptimePercentage(checks)).to.equal((2 / 3) * 100); }); }); - describe("getIncidents", function () { - it("should return 0 when checks array is empty", function () { + describe("getIncidents", function() { + it("should return 0 when checks array is empty", function() { expect(getIncidents([])).to.equal(0); }); - it("should return 0 when checks array is null", function () { + it("should return 0 when checks array is null", function() { expect(getIncidents(null)).to.equal(0); }); - it("should return 0 when all checks are up", function () { + it("should return 0 when all checks are up", function() { const checks = [{ status: true }, { status: true }, { status: true }]; expect(getIncidents(checks)).to.equal(0); }); - it("should count all incidents when all checks are down", function () { + it("should count all incidents when all checks are down", function() { const checks = [{ status: false }, { status: false }, { status: false }]; expect(getIncidents(checks)).to.equal(3); }); - it("should count correct number of incidents for mixed status checks", function () { + it("should count correct number of incidents for mixed status checks", function() { const checks = [ { status: true }, { status: false }, @@ -517,7 +517,7 @@ describe("monitorModule", function () { expect(getIncidents(checks)).to.equal(2); }); - it("should handle undefined status values", function () { + it("should handle undefined status values", function() { const checks = [ { status: true }, { status: undefined }, @@ -529,10 +529,10 @@ describe("monitorModule", function () { }); }); - describe("getMonitorChecks", function () { + describe("getMonitorChecks", function() { let mockModel; - beforeEach(function () { + beforeEach(function() { // Create a mock model with chainable methods const mockChecks = [ { monitorId: "123", createdAt: new Date("2024-01-01") }, @@ -546,11 +546,11 @@ describe("monitorModule", function () { }; }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should return all checks and date-ranged checks", async function () { + it("should return all checks and date-ranged checks", async function() { // Arrange const monitorId = "123"; const dateRange = { @@ -579,7 +579,7 @@ describe("monitorModule", function () { }); }); - it("should handle empty results", async function () { + it("should handle empty results", async function() { // Arrange const emptyModel = { find: sinon.stub().returns({ @@ -603,7 +603,7 @@ describe("monitorModule", function () { expect(result.checksForDateRange).to.be.an("array").that.is.empty; }); - it("should maintain sort order", async function () { + it("should maintain sort order", async function() { // Arrange const sortedChecks = [ { monitorId: "123", createdAt: new Date("2024-01-02") }, @@ -637,39 +637,39 @@ describe("monitorModule", function () { }); }); - describe("processChecksForDisplay", function () { + describe("processChecksForDisplay", function() { let normalizeStub; - beforeEach(function () { + beforeEach(function() { normalizeStub = sinon.stub(); }); - it("should return original checks when numToDisplay is not provided", function () { + it("should return original checks when numToDisplay is not provided", function() { const checks = [1, 2, 3, 4, 5]; const result = processChecksForDisplay(normalizeStub, checks); expect(result).to.deep.equal(checks); }); - it("should return original checks when numToDisplay is greater than checks length", function () { + it("should return original checks when numToDisplay is greater than checks length", function() { const checks = [1, 2, 3]; const result = processChecksForDisplay(normalizeStub, checks, 5); expect(result).to.deep.equal(checks); }); - it("should filter checks based on numToDisplay", function () { + it("should filter checks based on numToDisplay", function() { const checks = [1, 2, 3, 4, 5, 6]; const result = processChecksForDisplay(normalizeStub, checks, 3); // Should return [1, 3, 5] as n = ceil(6/3) = 2 expect(result).to.deep.equal([1, 3, 5]); }); - it("should handle empty checks array", function () { + it("should handle empty checks array", function() { const checks = []; const result = processChecksForDisplay(normalizeStub, checks, 3); expect(result).to.be.an("array").that.is.empty; }); - it("should call normalizeData when normalize is true", function () { + it("should call normalizeData when normalize is true", function() { const checks = [1, 2, 3]; normalizeStub.returns([10, 20, 30]); @@ -679,7 +679,7 @@ describe("monitorModule", function () { expect(result).to.deep.equal([10, 20, 30]); }); - it("should handle both filtering and normalization", function () { + it("should handle both filtering and normalization", function() { const checks = [1, 2, 3, 4, 5, 6]; normalizeStub.returns([10, 30, 50]); @@ -690,7 +690,7 @@ describe("monitorModule", function () { }); }); - describe("groupChecksByTime", function () { + describe("groupChecksByTime", function() { const mockChecks = [ { createdAt: "2024-01-15T10:30:45Z" }, { createdAt: "2024-01-15T10:45:15Z" }, @@ -698,7 +698,7 @@ describe("monitorModule", function () { { createdAt: "2024-01-16T10:30:00Z" }, ]; - it("should group checks by hour when dateRange is 'day'", function () { + it("should group checks by hour when dateRange is 'day'", function() { const result = groupChecksByTime(mockChecks, "day"); // Get timestamps for 10:00 and 11:00 on Jan 15 @@ -713,7 +713,7 @@ describe("monitorModule", function () { expect(result[time3].checks).to.have.lengthOf(1); }); - it("should group checks by day when dateRange is not 'day'", function () { + it("should group checks by day when dateRange is not 'day'", function() { const result = groupChecksByTime(mockChecks, "week"); expect(Object.keys(result)).to.have.lengthOf(2); @@ -721,12 +721,12 @@ describe("monitorModule", function () { expect(result["2024-01-16"].checks).to.have.lengthOf(1); }); - it("should handle empty checks array", function () { + it("should handle empty checks array", function() { const result = groupChecksByTime([], "day"); expect(result).to.deep.equal({}); }); - it("should handle single check", function () { + it("should handle single check", function() { const singleCheck = [{ createdAt: "2024-01-15T10:30:45Z" }]; const result = groupChecksByTime(singleCheck, "day"); @@ -735,7 +735,7 @@ describe("monitorModule", function () { expect(result[expectedTime].checks).to.have.lengthOf(1); }); - it("should skip invalid dates and process valid ones", function () { + it("should skip invalid dates and process valid ones", function() { const checksWithInvalidDate = [ { createdAt: "invalid-date" }, { createdAt: "2024-01-15T10:30:45Z" }, @@ -752,7 +752,7 @@ describe("monitorModule", function () { expect(result[expectedTime].checks[0].createdAt).to.equal("2024-01-15T10:30:45Z"); }); - it("should handle checks in same time group", function () { + it("should handle checks in same time group", function() { const checksInSameHour = [ { createdAt: "2024-01-15T10:15:00Z" }, { createdAt: "2024-01-15T10:45:00Z" }, @@ -766,16 +766,16 @@ describe("monitorModule", function () { }); }); - describe("calculateGroupStats", function () { + describe("calculateGroupStats", function() { // Mock getUptimePercentage function let uptimePercentageStub; - beforeEach(function () { + beforeEach(function() { uptimePercentageStub = sinon.stub(); uptimePercentageStub.returns(95); // Default return value }); - it("should calculate stats correctly for a group of checks", function () { + it("should calculate stats correctly for a group of checks", function() { const mockGroup = { time: "2024-01-15", checks: [ @@ -796,7 +796,7 @@ describe("monitorModule", function () { }); }); - it("should handle empty checks array", function () { + it("should handle empty checks array", function() { const mockGroup = { time: "2024-01-15", checks: [], @@ -813,7 +813,7 @@ describe("monitorModule", function () { }); }); - it("should handle missing responseTime values", function () { + it("should handle missing responseTime values", function() { const mockGroup = { time: "2024-01-15", checks: [ @@ -834,7 +834,7 @@ describe("monitorModule", function () { }); }); - it("should handle all checks with status false", function () { + it("should handle all checks with status false", function() { const mockGroup = { time: "2024-01-15", checks: [ @@ -855,7 +855,7 @@ describe("monitorModule", function () { }); }); - it("should handle all checks with status true", function () { + it("should handle all checks with status true", function() { const mockGroup = { time: "2024-01-15", checks: [ @@ -877,7 +877,7 @@ describe("monitorModule", function () { }); }); - describe("getMonitorStatsById", function () { + describe("getMonitorStatsById", function() { const now = new Date(); const oneHourAgo = new Date(now - 3600000); const twoHoursAgo = new Date(now - 7200000); @@ -974,18 +974,18 @@ describe("monitorModule", function () { }, }; - beforeEach(function () { + beforeEach(function() { checkFindStub.returns({ sort: () => checkDocs, }); monitorFindByIdStub.returns(mockMonitor); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should return monitor stats with calculated values, sort order desc", async function () { + it("should return monitor stats with calculated values, sort order desc", async function() { req.query.sortOrder = "desc"; const result = await getMonitorStatsById(req); expect(result).to.include.keys([ @@ -1009,7 +1009,7 @@ describe("monitorModule", function () { expect(result.aggregateData).to.be.an("array"); }); - it("should return monitor stats with calculated values, ping type", async function () { + it("should return monitor stats with calculated values, ping type", async function() { monitorFindByIdStub.returns(mockMonitorPing); req.query.sortOrder = "desc"; const result = await getMonitorStatsById(req); @@ -1034,7 +1034,7 @@ describe("monitorModule", function () { expect(result.aggregateData).to.be.an("array"); }); - it("should return monitor stats with calculated values, docker type", async function () { + it("should return monitor stats with calculated values, docker type", async function() { monitorFindByIdStub.returns(mockMonitorDocker); req.query.sortOrder = "desc"; const result = await getMonitorStatsById(req); @@ -1059,7 +1059,7 @@ describe("monitorModule", function () { expect(result.aggregateData).to.be.an("array"); }); - it("should return monitor stats with calculated values", async function () { + it("should return monitor stats with calculated values", async function() { req.query.sortOrder = "asc"; const result = await getMonitorStatsById(req); expect(result).to.include.keys([ @@ -1083,7 +1083,7 @@ describe("monitorModule", function () { expect(result.aggregateData).to.be.an("array"); }); - it("should throw error when monitor is not found", async function () { + it("should throw error when monitor is not found", async function() { monitorFindByIdStub.returns(Promise.resolve(null)); const req = { @@ -1101,21 +1101,21 @@ describe("monitorModule", function () { }); }); - describe("getMonitorById", function () { + describe("getMonitorById", function() { let notificationFindStub; let monitorSaveStub; - beforeEach(function () { + beforeEach(function() { // Create stubs notificationFindStub = sinon.stub(Notification, "find"); monitorSaveStub = sinon.stub().resolves(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should return monitor with notifications when found", async function () { + it("should return monitor with notifications when found", async function() { // Arrange const monitorId = "123"; const mockMonitor = { @@ -1139,10 +1139,9 @@ describe("monitorModule", function () { expect(monitorSaveStub.calledOnce).to.be.true; }); - it("should throw 404 error when monitor not found", async function () { + it("should throw 404 error when monitor not found", async function() { // Arrange const monitorId = "nonexistent"; - const mockLanguage = 'en'; monitorFindByIdStub.resolves(null); // Act & Assert @@ -1150,14 +1149,14 @@ describe("monitorModule", function () { await getMonitorById(monitorId); expect.fail("Should have thrown an error"); } catch (error) { - expect(error.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, mockLanguage)); + expect(error.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); expect(error.status).to.equal(404); expect(error.service).to.equal("monitorModule"); expect(error.method).to.equal("getMonitorById"); } }); - it("should handle database errors properly", async function () { + it("should handle database errors properly", async function() { // Arrange const monitorId = "123"; const dbError = new Error("Database connection failed"); @@ -1174,7 +1173,7 @@ describe("monitorModule", function () { } }); - it("should handle notification fetch errors", async function () { + it("should handle notification fetch errors", async function() { // Arrange const monitorId = "123"; const mockMonitor = { @@ -1198,7 +1197,7 @@ describe("monitorModule", function () { } }); - it("should handle monitor save errors", async function () { + it("should handle monitor save errors", async function() { // Arrange const monitorId = "123"; const mockMonitor = { @@ -1223,8 +1222,8 @@ describe("monitorModule", function () { }); }); - describe("getMonitorsAndSummaryByTeamId", function () { - it("should return monitors and correct summary counts", async function () { + describe("getMonitorsAndSummaryByTeamId", function() { + it("should return monitors and correct summary counts", async function() { // Arrange const teamId = "team123"; const type = "http"; @@ -1250,7 +1249,7 @@ describe("monitorModule", function () { expect(monitorFindStub.calledOnceWith({ teamId, type })).to.be.true; }); - it("should return empty results for non-existent team", async function () { + it("should return empty results for non-existent team", async function() { // Arrange monitorFindStub.resolves([]); @@ -1267,7 +1266,7 @@ describe("monitorModule", function () { }); }); - it("should handle database errors", async function () { + it("should handle database errors", async function() { // Arrange const error = new Error("Database error"); error.service = "MonitorModule"; @@ -1286,8 +1285,8 @@ describe("monitorModule", function () { }); }); - describe("getMonitorsByTeamId", function () { - beforeEach(function () { + describe("getMonitorsByTeamId", function() { + beforeEach(function() { // Chain stubs for Monitor.find().skip().limit().sort() // Stub for CHECK_MODEL_LOOKUP model find @@ -1298,11 +1297,11 @@ describe("monitorModule", function () { }); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should return monitors with basic query parameters", async function () { + it("should return monitors with basic query parameters", async function() { const mockMonitors = [ { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) }, { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) }, @@ -1335,7 +1334,7 @@ describe("monitorModule", function () { expect(result).to.have.property("monitorCount", 2); }); - it("should return monitors with basic query parameters", async function () { + it("should return monitors with basic query parameters", async function() { const mockMonitors = [ { _id: "1", type: "http", toObject: () => ({ _id: "1", type: "http" }) }, { _id: "2", type: "ping", toObject: () => ({ _id: "2", type: "ping" }) }, @@ -1368,7 +1367,7 @@ describe("monitorModule", function () { expect(result).to.have.property("monitorCount", 2); }); - it("should handle type filter with array input", async function () { + it("should handle type filter with array input", async function() { const req = { params: { teamId: "team123" }, query: { @@ -1393,7 +1392,7 @@ describe("monitorModule", function () { }); }); - it("should handle text search filter", async function () { + it("should handle text search filter", async function() { const req = { params: { teamId: "team123" }, query: { @@ -1421,7 +1420,7 @@ describe("monitorModule", function () { }); }); - it("should handle pagination parameters", async function () { + it("should handle pagination parameters", async function() { const req = { params: { teamId: "team123" }, query: { @@ -1446,7 +1445,7 @@ describe("monitorModule", function () { }); }); - it("should handle sorting parameters", async function () { + it("should handle sorting parameters", async function() { const req = { params: { teamId: "team123" }, query: { @@ -1473,7 +1472,7 @@ describe("monitorModule", function () { }); }); - it("should return early when limit is -1", async function () { + it("should return early when limit is -1", async function() { // Arrange const req = { params: { teamId: "team123" }, @@ -1507,7 +1506,7 @@ describe("monitorModule", function () { }); }); - it("should normalize checks when normalize parameter is provided", async function () { + it("should normalize checks when normalize parameter is provided", async function() { const req = { params: { teamId: "team123" }, query: { normalize: "true" }, @@ -1532,7 +1531,7 @@ describe("monitorModule", function () { expect(result.monitors).to.have.lengthOf(2); }); - it("should handle database errors", async function () { + it("should handle database errors", async function() { const req = { params: { teamId: "team123" }, query: {}, @@ -1558,8 +1557,8 @@ describe("monitorModule", function () { }); }); - describe("createMonitor", function () { - it("should create a monitor without notifications", async function () { + describe("createMonitor", function() { + it("should create a monitor without notifications", async function() { let monitorSaveStub = sinon.stub(Monitor.prototype, "save").resolves(); const req = { @@ -1584,7 +1583,7 @@ describe("monitorModule", function () { expect(result.url).to.equal(expectedMonitor.url); }); - it("should handle database errors", async function () { + it("should handle database errors", async function() { const req = { body: { name: "Test Monitor", @@ -1601,8 +1600,8 @@ describe("monitorModule", function () { }); }); - describe("deleteMonitor", function () { - it("should delete a monitor successfully", async function () { + describe("deleteMonitor", function() { + it("should delete a monitor successfully", async function() { const monitorId = "123456789"; const mockMonitor = { _id: monitorId, @@ -1622,11 +1621,10 @@ describe("monitorModule", function () { sinon.assert.calledWith(monitorFindByIdAndDeleteStub, monitorId); }); - it("should throw error when monitor not found", async function () { + it("should throw error when monitor not found", async function() { const monitorId = "nonexistent123"; const req = { params: { monitorId }, - language: 'en', }; monitorFindByIdAndDeleteStub.resolves(null); @@ -1635,13 +1633,13 @@ describe("monitorModule", function () { await deleteMonitor(req); expect.fail("Should have thrown an error"); } catch (err) { - expect(err.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, req.language)); + expect(err.message).to.equal(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); expect(err.service).to.equal("monitorModule"); expect(err.method).to.equal("deleteMonitor"); } }); - it("should handle database errors", async function () { + it("should handle database errors", async function() { const monitorId = "123456789"; const req = { params: { monitorId }, @@ -1661,8 +1659,8 @@ describe("monitorModule", function () { }); }); - describe("deleteAllMonitors", function () { - it("should delete all monitors for a team successfully", async function () { + describe("deleteAllMonitors", function() { + it("should delete all monitors for a team successfully", async function() { const teamId = "team123"; const mockMonitors = [ { _id: "1", name: "Monitor 1", teamId }, @@ -1682,7 +1680,7 @@ describe("monitorModule", function () { sinon.assert.calledWith(monitorDeleteManyStub, { teamId }); }); - it("should return empty array when no monitors found", async function () { + it("should return empty array when no monitors found", async function() { const teamId = "emptyTeam"; monitorFindStub.resolves([]); @@ -1698,7 +1696,7 @@ describe("monitorModule", function () { sinon.assert.calledWith(monitorDeleteManyStub, { teamId }); }); - it("should handle database errors", async function () { + it("should handle database errors", async function() { const teamId = "team123"; const dbError = new Error("Database connection error"); monitorFindStub.rejects(dbError); @@ -1712,7 +1710,7 @@ describe("monitorModule", function () { } }); - it("should handle deleteMany errors", async function () { + it("should handle deleteMany errors", async function() { const teamId = "team123"; monitorFindStub.resolves([{ _id: "1", name: "Monitor 1" }]); monitorDeleteManyStub.rejects(new Error("Delete operation failed")); @@ -1727,14 +1725,14 @@ describe("monitorModule", function () { }); }); - describe("deleteMonitorsByUserId", function () { - beforeEach(function () { }); + describe("deleteMonitorsByUserId", function() { + beforeEach(function() {}); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should delete all monitors for a user successfully", async function () { + it("should delete all monitors for a user successfully", async function() { // Arrange const userId = "user123"; const mockResult = { @@ -1752,7 +1750,7 @@ describe("monitorModule", function () { sinon.assert.calledWith(monitorDeleteManyStub, { userId: userId }); }); - it("should return zero deletedCount when no monitors found", async function () { + it("should return zero deletedCount when no monitors found", async function() { // Arrange const userId = "nonexistentUser"; const mockResult = { @@ -1770,7 +1768,7 @@ describe("monitorModule", function () { sinon.assert.calledWith(monitorDeleteManyStub, { userId: userId }); }); - it("should handle database errors", async function () { + it("should handle database errors", async function() { // Arrange const userId = "user123"; const dbError = new Error("Database connection error"); @@ -1788,8 +1786,8 @@ describe("monitorModule", function () { }); }); - describe("editMonitor", function () { - it("should edit a monitor successfully", async function () { + describe("editMonitor", function() { + it("should edit a monitor successfully", async function() { // Arrange const candidateId = "monitor123"; const candidateMonitor = { @@ -1828,7 +1826,7 @@ describe("monitorModule", function () { ); }); - it("should return null when monitor not found", async function () { + it("should return null when monitor not found", async function() { // Arrange const candidateId = "nonexistent123"; const candidateMonitor = { @@ -1850,7 +1848,7 @@ describe("monitorModule", function () { ); }); - it("should remove notifications from update data", async function () { + it("should remove notifications from update data", async function() { // Arrange const candidateId = "monitor123"; const candidateMonitor = { @@ -1882,7 +1880,7 @@ describe("monitorModule", function () { ); }); - it("should handle database errors", async function () { + it("should handle database errors", async function() { // Arrange const candidateId = "monitor123"; const candidateMonitor = { @@ -1904,8 +1902,8 @@ describe("monitorModule", function () { }); }); - describe("addDemoMonitors", function () { - it("should add demo monitors successfully", async function () { + describe("addDemoMonitors", function() { + it("should add demo monitors successfully", async function() { // Arrange const userId = "user123"; const teamId = "team123"; @@ -1914,7 +1912,7 @@ describe("monitorModule", function () { expect(result).to.deep.equal([{ _id: "123" }]); }); - it("should handle database errors", async function () { + it("should handle database errors", async function() { const userId = "user123"; const teamId = "team123"; diff --git a/Server/tests/db/recoveryModule.test.js b/Server/tests/db/recoveryModule.test.js index 1c6d1d43c..9d2978dd3 100644 --- a/Server/tests/db/recoveryModule.test.js +++ b/Server/tests/db/recoveryModule.test.js @@ -43,7 +43,7 @@ const createQueryChain = (finalResult, comparePasswordResult = false) => ({ save: sinon.stub().resolves(), }); -describe("recoveryModule", function () { +describe("recoveryModule", function() { let deleteManyStub, saveStub, findOneStub, @@ -52,10 +52,9 @@ describe("recoveryModule", function () { userFindOneStub; let req, res; - beforeEach(function () { + beforeEach(function() { req = { body: { email: "test@test.com" }, - language: 'en', }; deleteManyStub = sinon.stub(RecoveryToken, "deleteMany"); saveStub = sinon.stub(RecoveryToken.prototype, "save"); @@ -65,19 +64,19 @@ describe("recoveryModule", function () { userFindOneStub = sinon.stub().resolves(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - describe("requestRecoveryToken", function () { - it("should return a recovery token", async function () { + describe("requestRecoveryToken", function() { + it("should return a recovery token", async function() { deleteManyStub.resolves(); saveStub.resolves(mockRecoveryToken); const result = await requestRecoveryToken(req, res); expect(result.email).to.equal(mockRecoveryToken.email); }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("Test error"); deleteManyStub.rejects(err); try { @@ -89,24 +88,24 @@ describe("recoveryModule", function () { }); }); - describe("validateRecoveryToken", function () { - it("should return a recovery token if found", async function () { + describe("validateRecoveryToken", function() { + it("should return a recovery token if found", async function() { findOneStub.resolves(mockRecoveryToken); const result = await validateRecoveryToken(req, res); expect(result).to.deep.equal(mockRecoveryToken); }); - it("should thrown an error if a token is not found", async function () { + it("should thrown an error if a token is not found", async function() { findOneStub.resolves(null); try { await validateRecoveryToken(req, res); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND(req.language)); + expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND); } }); - it("should handle DB errors", async function () { + it("should handle DB errors", async function() { const err = new Error("Test error"); findOneStub.rejects(err); try { @@ -118,41 +117,40 @@ describe("recoveryModule", function () { }); }); - describe("resetPassword", function () { - beforeEach(function () { + describe("resetPassword", function() { + beforeEach(function() { req.body = { password: "test", newPassword: "test1", }; - req.language = 'en'; }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should thrown an error if a recovery token is not found", async function () { + it("should thrown an error if a recovery token is not found", async function() { findOneStub.resolves(null); try { await resetPassword(req, res); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND(req.language)); + expect(error.message).to.equal(errorMessages.DB_TOKEN_NOT_FOUND); } }); - it("should throw an error if a user is not found", async function () { + it("should throw an error if a user is not found", async function() { findOneStub.resolves(mockRecoveryToken); userFindOneStub = sinon.stub(User, "findOne").resolves(null); try { await resetPassword(req, res); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND(req.language)); + expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); } }); - it("should throw an error if the passwords match", async function () { + it("should throw an error if the passwords match", async function() { findOneStub.resolves(mockRecoveryToken); saveStub.resolves(); userFindOneStub = sinon @@ -162,11 +160,11 @@ describe("recoveryModule", function () { await resetPassword(req, res); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_RESET_PASSWORD_BAD_MATCH(req.language)); + expect(error.message).to.equal(errorMessages.DB_RESET_PASSWORD_BAD_MATCH); } }); - it("should return a user without password if successful", async function () { + it("should return a user without password if successful", async function() { findOneStub.resolves(mockRecoveryToken); saveStub.resolves(); userFindOneStub = sinon diff --git a/Server/tests/db/statusPageModule.test.js b/Server/tests/db/statusPageModule.test.js index 0793dc6b9..3d5d5e9d0 100644 --- a/Server/tests/db/statusPageModule.test.js +++ b/Server/tests/db/statusPageModule.test.js @@ -6,32 +6,31 @@ import { import StatusPage from "../../db/models/StatusPage.js"; import { errorMessages } from "../../utils/messages.js"; -describe("statusPageModule", function () { - let statusPageFindOneStub, statusPageSaveStub, statusPageFindStub, mockLanguage; +describe("statusPageModule", function() { + let statusPageFindOneStub, statusPageSaveStub, statusPageFindStub; - beforeEach(function () { - mockLanguage = 'en'; + beforeEach(function() { statusPageSaveStub = sinon.stub(StatusPage.prototype, "save"); statusPageFindOneStub = sinon.stub(StatusPage, "findOne"); statusPageFindStub = sinon.stub(StatusPage, "find"); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - describe("createStatusPage", function () { - it("should throw an error if a non-unique url is provided", async function () { + describe("createStatusPage", function() { + it("should throw an error if a non-unique url is provided", async function() { statusPageFindOneStub.resolves(true); try { await createStatusPage({ url: "test" }); } catch (error) { expect(error.status).to.equal(400); - expect(error.message).to.equal(errorMessages.STATUS_PAGE_URL_NOT_UNIQUE(mockLanguage)); + expect(error.message).to.equal(errorMessages.STATUS_PAGE_URL_NOT_UNIQUE); } }); - it("should handle duplicate URL errors", async function () { + it("should handle duplicate URL errors", async function() { const err = new Error("test"); err.code = 11000; statusPageSaveStub.rejects(err); @@ -42,7 +41,7 @@ describe("statusPageModule", function () { } }); - it("should return a status page if a unique url is provided", async function () { + it("should return a status page if a unique url is provided", async function() { statusPageFindOneStub.resolves(null); statusPageFindStub.resolves([]); const mockStatusPage = { url: "test" }; @@ -52,21 +51,21 @@ describe("statusPageModule", function () { }); }); - describe("getStatusPageByUrl", function () { - it("should throw an error if a status page is not found", async function () { + describe("getStatusPageByUrl", function() { + it("should throw an error if a status page is not found", async function() { statusPageFindOneStub.resolves(null); try { await getStatusPageByUrl("test"); } catch (error) { expect(error.status).to.equal(404); - expect(error.message).to.equal(errorMessages.STATUS_PAGE_NOT_FOUND(mockLanguage)); + expect(error.message).to.equal(errorMessages.STATUS_PAGE_NOT_FOUND); } }); - it("should return a status page if a status page is found", async function () { + it("should return a status page if a status page is found", async function() { const mockStatusPage = { url: "test" }; statusPageFindOneStub.resolves(mockStatusPage); - const statusPage = await getStatusPageByUrl(mockStatusPage.url, mockLanguage); + const statusPage = await getStatusPageByUrl(mockStatusPage.url); expect(statusPage).to.exist; expect(statusPage).to.deep.equal(mockStatusPage); }); diff --git a/Server/tests/db/userModule.test.js b/Server/tests/db/userModule.test.js index 0bf1fd067..b52ba1880 100644 --- a/Server/tests/db/userModule.test.js +++ b/Server/tests/db/userModule.test.js @@ -27,9 +27,7 @@ const imageFile = { image: 1, }; -const mockLanguage = 'en'; - -describe("userModule", function () { +describe("userModule", function() { let teamSaveStub, teamFindByIdAndDeleteStub, userSaveStub, @@ -42,7 +40,7 @@ describe("userModule", function () { generateAvatarImageStub, parseBooleanStub; - beforeEach(function () { + beforeEach(function() { teamSaveStub = sinon.stub(TeamModel.prototype, "save"); teamFindByIdAndDeleteStub = sinon.stub(TeamModel, "findByIdAndDelete"); userSaveStub = sinon.stub(UserModel.prototype, "save"); @@ -56,12 +54,12 @@ describe("userModule", function () { parseBooleanStub = sinon.stub().returns(true); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - describe("insertUser", function () { - it("should insert a regular user", async function () { + describe("insertUser", function() { + it("should insert a regular user", async function() { userSaveStub.resolves(mockUser); userFindOneStub.returns({ select: sinon.stub().returns({ @@ -72,7 +70,7 @@ describe("userModule", function () { expect(result).to.deep.equal(mockUser); }); - it("should insert a superadmin user", async function () { + it("should insert a superadmin user", async function() { userSaveStub.resolves(mockSuperUser); userFindOneStub.returns({ select: sinon.stub().returns({ @@ -83,7 +81,7 @@ describe("userModule", function () { expect(result).to.deep.equal(mockSuperUser); }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("test error"); userSaveStub.rejects(err); try { @@ -94,7 +92,7 @@ describe("userModule", function () { } }); - it("should handle a duplicate key error", async function () { + it("should handle a duplicate key error", async function() { const err = new Error("test error"); err.code = 11000; userSaveStub.rejects(err); @@ -107,8 +105,8 @@ describe("userModule", function () { }); }); - describe("getUserByEmail", function () { - it("should return a user", async function () { + describe("getUserByEmail", function() { + it("should return a user", async function() { userFindOneStub.returns({ select: sinon.stub().resolves(mockUser), }); @@ -117,23 +115,23 @@ describe("userModule", function () { }); }); - describe("getUserByEmail", function () { - it("throw an error if a user is not found", async function () { + describe("getUserByEmail", function() { + it("throw an error if a user is not found", async function() { userFindOneStub.returns({ select: sinon.stub().resolves(null), }); try { await getUserByEmail(mockUser.email); } catch (error) { - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND(mockLanguage)); + expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); } }); }); - describe("updateUser", function () { + describe("updateUser", function() { let req, res; - beforeEach(function () { + beforeEach(function() { req = { params: { userId: "testId", @@ -150,9 +148,9 @@ describe("userModule", function () { res = {}; }); - afterEach(function () { }); + afterEach(function() {}); - it("should update a user", async function () { + it("should update a user", async function() { parseBooleanStub.returns(false); userFindByIdAndUpdateStub.returns({ select: sinon.stub().returns({ @@ -168,7 +166,7 @@ describe("userModule", function () { expect(result).to.deep.equal(mockUser); }); - it("should delete a user profile image", async function () { + it("should delete a user profile image", async function() { req.body.deleteProfileImage = "true"; userFindByIdAndUpdateStub.returns({ select: sinon.stub().returns({ @@ -184,7 +182,7 @@ describe("userModule", function () { expect(result).to.deep.equal(mockUser); }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("test error"); userFindByIdAndUpdateStub.throws(err); try { @@ -196,23 +194,23 @@ describe("userModule", function () { }); }); - describe("deleteUser", function () { - it("should return a deleted user", async function () { + describe("deleteUser", function() { + it("should return a deleted user", async function() { userFindByIdAndDeleteStub.resolves(mockUser); const result = await deleteUser("testId"); expect(result).to.deep.equal(mockUser); }); - it("should throw an error if a user is not found", async function () { + it("should throw an error if a user is not found", async function() { try { await deleteUser("testId"); } catch (error) { expect(error).to.exist; - expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND(mockLanguage)); + expect(error.message).to.equal(errorMessages.DB_USER_NOT_FOUND); } }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("test error"); userFindByIdAndDeleteStub.throws(err); try { @@ -224,14 +222,14 @@ describe("userModule", function () { }); }); - describe("deleteTeam", function () { - it("should return true if team deleted", async function () { + describe("deleteTeam", function() { + it("should return true if team deleted", async function() { teamFindByIdAndDeleteStub.resolves(); const result = await deleteTeam("testId"); expect(result).to.equal(true); }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("test error"); teamFindByIdAndDeleteStub.throws(err); try { @@ -243,14 +241,14 @@ describe("userModule", function () { }); }); - describe("deleteAllOtherUsers", function () { - it("should return true if all other users deleted", async function () { + describe("deleteAllOtherUsers", function() { + it("should return true if all other users deleted", async function() { userDeleteManyStub.resolves(true); const result = await deleteAllOtherUsers(); expect(result).to.equal(true); }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("test error"); userDeleteManyStub.throws(err); try { @@ -262,8 +260,8 @@ describe("userModule", function () { }); }); - describe("getAllUsers", function () { - it("should return all users", async function () { + describe("getAllUsers", function() { + it("should return all users", async function() { userFindStub.returns({ select: sinon.stub().returns({ select: sinon.stub().resolves([mockUser]), @@ -273,7 +271,7 @@ describe("userModule", function () { expect(result).to.deep.equal([mockUser]); }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("test error"); userFindStub.throws(err); try { @@ -285,14 +283,14 @@ describe("userModule", function () { }); }); - describe("logoutUser", function () { - it("should return true if user logged out", async function () { + describe("logoutUser", function() { + it("should return true if user logged out", async function() { userUpdateOneStub.resolves(true); const result = await logoutUser("testId"); expect(result).to.equal(true); }); - it("should handle an error", async function () { + it("should handle an error", async function() { const err = new Error("test error"); userUpdateOneStub.throws(err); try { diff --git a/Server/tests/services/networkService.test.js b/Server/tests/services/networkService.test.js index f20b2628d..a5a59c938 100644 --- a/Server/tests/services/networkService.test.js +++ b/Server/tests/services/networkService.test.js @@ -3,10 +3,10 @@ import NetworkService from "../../service/networkService.js"; import { expect } from "chai"; import http from "http"; import { errorMessages } from "../../utils/messages.js"; -describe("Network Service", function () { +describe("Network Service", function() { let axios, ping, Docker, logger, networkService; - beforeEach(function () { + beforeEach(function() { axios = { get: sinon.stub().resolves({ data: { foo: "bar" }, @@ -35,22 +35,22 @@ describe("Network Service", function () { networkService = new NetworkService(axios, ping, logger, http, Docker); }); - describe("constructor", function () { - it("should create a new NetworkService instance", function () { + describe("constructor", function() { + it("should create a new NetworkService instance", function() { const networkService = new NetworkService(); expect(networkService).to.be.an.instanceOf(NetworkService); }); }); - describe("timeRequest", function () { - it("should time an asynchronous operation", async function () { + describe("timeRequest", function() { + it("should time an asynchronous operation", async function() { const operation = sinon.stub().resolves("success"); const { response, responseTime } = await networkService.timeRequest(operation); expect(response).to.equal("success"); expect(responseTime).to.be.a("number"); }); - it("should handle errors if operation throws error", async function () { + it("should handle errors if operation throws error", async function() { const error = new Error("Test error"); const operation = sinon.stub().throws(error); const { response, responseTime } = await networkService.timeRequest(operation); @@ -60,8 +60,8 @@ describe("Network Service", function () { }); }); - describe("requestPing", function () { - it("should return a response object if ping successful", async function () { + describe("requestPing", function() { + it("should return a response object if ping successful", async function() { const pingResult = await networkService.requestPing({ data: { url: "http://test.com", _id: "123" }, }); @@ -71,7 +71,7 @@ describe("Network Service", function () { expect(pingResult.status).to.be.true; }); - it("should return a response object if ping unsuccessful", async function () { + it("should return a response object if ping unsuccessful", async function() { const error = new Error("Test error"); networkService.timeRequest = sinon .stub() @@ -86,7 +86,7 @@ describe("Network Service", function () { expect(pingResult.code).to.equal(networkService.PING_ERROR); }); - it("should throw an error if ping cannot resolve", async function () { + it("should throw an error if ping cannot resolve", async function() { const error = new Error("test error"); networkService.timeRequest = sinon.stub().throws(error); try { @@ -100,8 +100,8 @@ describe("Network Service", function () { }); }); - describe("requestHttp", function () { - it("should return a response object if http successful", async function () { + describe("requestHttp", function() { + it("should return a response object if http successful", async function() { const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; const httpResult = await networkService.requestHttp(job); expect(httpResult.monitorId).to.equal("123"); @@ -110,7 +110,7 @@ describe("Network Service", function () { expect(httpResult.status).to.be.true; }); - it("should return a response object if http unsuccessful", async function () { + it("should return a response object if http unsuccessful", async function() { const error = new Error("Test error"); error.response = { status: 404 }; networkService.timeRequest = sinon @@ -125,7 +125,7 @@ describe("Network Service", function () { expect(httpResult.code).to.equal(404); }); - it("should return a response object if http unsuccessful with unknown code", async function () { + it("should return a response object if http unsuccessful with unknown code", async function() { const error = new Error("Test error"); error.response = {}; networkService.timeRequest = sinon @@ -140,7 +140,7 @@ describe("Network Service", function () { expect(httpResult.code).to.equal(networkService.NETWORK_ERROR); }); - it("should throw an error if an error occurs", async function () { + it("should throw an error if an error occurs", async function() { const error = new Error("test error"); networkService.timeRequest = sinon.stub().throws(error); try { @@ -154,8 +154,8 @@ describe("Network Service", function () { }); }); - describe("requestPagespeed", function () { - it("should return a response object if pagespeed successful", async function () { + describe("requestPagespeed", function() { + it("should return a response object if pagespeed successful", async function() { const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; const pagespeedResult = await networkService.requestPagespeed(job); expect(pagespeedResult.monitorId).to.equal("123"); @@ -164,7 +164,7 @@ describe("Network Service", function () { expect(pagespeedResult.status).to.be.true; }); - it("should return a response object if pagespeed unsuccessful", async function () { + it("should return a response object if pagespeed unsuccessful", async function() { const error = new Error("Test error"); error.response = { status: 404 }; networkService.timeRequest = sinon @@ -179,7 +179,7 @@ describe("Network Service", function () { expect(pagespeedResult.code).to.equal(404); }); - it("should return a response object if pagespeed unsuccessful with an unknown code", async function () { + it("should return a response object if pagespeed unsuccessful with an unknown code", async function() { const error = new Error("Test error"); error.response = {}; networkService.timeRequest = sinon @@ -194,7 +194,7 @@ describe("Network Service", function () { expect(pagespeedResult.code).to.equal(networkService.NETWORK_ERROR); }); - it("should throw an error if pagespeed cannot resolve", async function () { + it("should throw an error if pagespeed cannot resolve", async function() { const error = new Error("test error"); networkService.timeRequest = sinon.stub().throws(error); try { @@ -208,8 +208,8 @@ describe("Network Service", function () { }); }); - describe("requestHardware", function () { - it("should return a response object if hardware successful", async function () { + describe("requestHardware", function() { + it("should return a response object if hardware successful", async function() { const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; const httpResult = await networkService.requestHardware(job); expect(httpResult.monitorId).to.equal("123"); @@ -218,7 +218,7 @@ describe("Network Service", function () { expect(httpResult.status).to.be.true; }); - it("should return a response object if hardware successful and job has a secret", async function () { + it("should return a response object if hardware successful and job has a secret", async function() { const job = { data: { url: "http://test.com", @@ -234,7 +234,7 @@ describe("Network Service", function () { expect(httpResult.status).to.be.true; }); - it("should return a response object if hardware unsuccessful", async function () { + it("should return a response object if hardware unsuccessful", async function() { const error = new Error("Test error"); error.response = { status: 404 }; networkService.timeRequest = sinon @@ -249,7 +249,7 @@ describe("Network Service", function () { expect(httpResult.code).to.equal(404); }); - it("should return a response object if hardware unsuccessful with unknown code", async function () { + it("should return a response object if hardware unsuccessful with unknown code", async function() { const error = new Error("Test error"); error.response = {}; networkService.timeRequest = sinon @@ -264,7 +264,7 @@ describe("Network Service", function () { expect(httpResult.code).to.equal(networkService.NETWORK_ERROR); }); - it("should throw an error if hardware cannot resolve", async function () { + it("should throw an error if hardware cannot resolve", async function() { const error = new Error("test error"); networkService.timeRequest = sinon.stub().throws(error); try { @@ -278,8 +278,8 @@ describe("Network Service", function () { }); }); - describe("requestDocker", function () { - it("should return a response object if docker successful", async function () { + describe("requestDocker", function() { + it("should return a response object if docker successful", async function() { const job = { data: { url: "http://test.com", _id: "123", type: "docker" } }; const dockerResult = await networkService.requestDocker(job); expect(dockerResult.monitorId).to.equal("123"); @@ -288,7 +288,7 @@ describe("Network Service", function () { expect(dockerResult.status).to.be.true; }); - it("should return a response object with status false if container not running", async function () { + it("should return a response object with status false if container not running", async function() { Docker = class { listContainers = sinon.stub().resolves([ { @@ -307,7 +307,7 @@ describe("Network Service", function () { expect(dockerResult.code).to.equal(200); }); - it("should handle an error when fetching the container", async function () { + it("should handle an error when fetching the container", async function() { Docker = class { listContainers = sinon.stub().resolves([ { @@ -326,7 +326,7 @@ describe("Network Service", function () { expect(dockerResult.code).to.equal(networkService.NETWORK_ERROR); }); - it("should throw an error if operations fail", async function () { + it("should throw an error if operations fail", async function() { Docker = class { listContainers = sinon.stub().resolves([ { @@ -345,7 +345,7 @@ describe("Network Service", function () { } }); - it("should throw an error if no matching images found", async function () { + it("should throw an error if no matching images found", async function() { Docker = class { listContainers = sinon.stub().resolves([]); getContainer = sinon.stub().throws(new Error("test error")); @@ -355,13 +355,13 @@ describe("Network Service", function () { try { await networkService.requestDocker(job); } catch (error) { - expect(error.message).to.equal(errorMessages.DOCKER_NOT_FOUND(mockLanguage)); + expect(error.message).to.equal(errorMessages.DOCKER_NOT_FOUND); } }); }); - describe("getStatus", function () { - beforeEach(function () { + describe("getStatus", function() { + beforeEach(function() { networkService.requestPing = sinon.stub(); networkService.requestHttp = sinon.stub(); networkService.requestPagespeed = sinon.stub(); @@ -369,11 +369,11 @@ describe("Network Service", function () { networkService.requestDocker = sinon.stub(); }); - afterEach(function () { + afterEach(function() { sinon.restore(); }); - it("should call requestPing if type is ping", async function () { + it("should call requestPing if type is ping", async function() { await networkService.getStatus({ data: { type: "ping" } }); expect(networkService.requestPing.calledOnce).to.be.true; expect(networkService.requestDocker.notCalled).to.be.true; @@ -381,7 +381,7 @@ describe("Network Service", function () { expect(networkService.requestPagespeed.notCalled).to.be.true; }); - it("should call requestHttp if type is http", async function () { + it("should call requestHttp if type is http", async function() { await networkService.getStatus({ data: { type: "http" } }); expect(networkService.requestPing.notCalled).to.be.true; expect(networkService.requestDocker.notCalled).to.be.true; @@ -389,7 +389,7 @@ describe("Network Service", function () { expect(networkService.requestPagespeed.notCalled).to.be.true; }); - it("should call requestPagespeed if type is pagespeed", async function () { + it("should call requestPagespeed if type is pagespeed", async function() { await networkService.getStatus({ data: { type: "pagespeed" } }); expect(networkService.requestPing.notCalled).to.be.true; expect(networkService.requestDocker.notCalled).to.be.true; @@ -397,7 +397,7 @@ describe("Network Service", function () { expect(networkService.requestPagespeed.calledOnce).to.be.true; }); - it("should call requestHardware if type is hardware", async function () { + it("should call requestHardware if type is hardware", async function() { await networkService.getStatus({ data: { type: "hardware" } }); expect(networkService.requestHardware.calledOnce).to.be.true; expect(networkService.requestDocker.notCalled).to.be.true; @@ -405,7 +405,7 @@ describe("Network Service", function () { expect(networkService.requestPagespeed.notCalled).to.be.true; }); - it("should call requestDocker if type is Docker", async function () { + it("should call requestDocker if type is Docker", async function() { await networkService.getStatus({ data: { type: "docker" } }); expect(networkService.requestDocker.calledOnce).to.be.true; expect(networkService.requestHardware.notCalled).to.be.true; @@ -413,7 +413,7 @@ describe("Network Service", function () { expect(networkService.requestPagespeed.notCalled).to.be.true; }); - it("should throw an error if an unknown type is provided", async function () { + it("should throw an error if an unknown type is provided", async function() { try { await networkService.getStatus({ data: { type: "unknown" } }); } catch (error) { @@ -423,7 +423,7 @@ describe("Network Service", function () { } }); - it("should throw an error if job type is undefined", async function () { + it("should throw an error if job type is undefined", async function() { try { await networkService.getStatus({ data: { type: undefined } }); } catch (error) { @@ -433,7 +433,7 @@ describe("Network Service", function () { } }); - it("should throw an error if job is empty", async function () { + it("should throw an error if job is empty", async function() { try { await networkService.getStatus({}); } catch (error) { @@ -442,7 +442,7 @@ describe("Network Service", function () { } }); - it("should throw an error if job is null", async function () { + it("should throw an error if job is null", async function() { try { await networkService.getStatus(null); } catch (error) { diff --git a/Server/tests/utils/messages.test.js b/Server/tests/utils/messages.test.js index a36fc2a4a..7ece332c4 100644 --- a/Server/tests/utils/messages.test.js +++ b/Server/tests/utils/messages.test.js @@ -1,25 +1,23 @@ import { errorMessages, successMessages } from "../../utils/messages.js"; -describe("Messages", function () { - describe("messages - errorMessages", function () { - it("should have a DB_FIND_MONITOR_BY_ID function", function () { +describe("Messages", function() { + describe("messages - errorMessages", function() { + it("should have a DB_FIND_MONITOR_BY_ID function", function() { const monitorId = "12345"; - const mockLanguage = 'en'; - expect(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId, mockLanguage)).to.equal( + expect(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)).to.equal( `Monitor with id ${monitorId} not found` ); }); - it("should have a DB_DELETE_CHECKS function", function () { + it("should have a DB_DELETE_CHECKS function", function() { const monitorId = "12345"; - const mockLanguage = 'en'; - expect(errorMessages.DB_DELETE_CHECKS(monitorId, mockLanguage)).to.equal( + expect(errorMessages.DB_DELETE_CHECKS(monitorId)).to.equal( `No checks found for monitor with id ${monitorId}` ); }); }); - describe("messages - successMessages", function () { - it("should have a MONITOR_GET_BY_USER_ID function", function () { + describe("messages - successMessages", function() { + it("should have a MONITOR_GET_BY_USER_ID function", function() { const userId = "12345"; expect(successMessages.MONITOR_GET_BY_USER_ID(userId)).to.equal( `Got monitor for ${userId} successfully"` From d0e09fe77fe293030fb101e7ab50de195b40aa05 Mon Sep 17 00:00:00 2001 From: cihatata Date: Thu, 30 Jan 2025 02:22:15 +0300 Subject: [PATCH 21/52] fix: load file --- Server/service/translationService.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/Server/service/translationService.js b/Server/service/translationService.js index 9c21d9b34..652df340c 100644 --- a/Server/service/translationService.js +++ b/Server/service/translationService.js @@ -22,10 +22,7 @@ class TranslationService { async initialize() { try { - const loadedFromFiles = await this.loadFromFiles(); - if (!loadedFromFiles) { - await this.loadTranslations(); - } + await this.loadTranslations(); // Yeni eklenen terimleri POEditor'e gönder await this.syncTermsWithPOEditor(); From c6b3932677fa49dbb8df5cb185e93fa19d0cc396 Mon Sep 17 00:00:00 2001 From: cihat Date: Thu, 30 Jan 2025 08:37:42 +0300 Subject: [PATCH 22/52] fix: constructure --- Server/controllers/authController.js | 6 ++--- Server/controllers/checkController.js | 6 ++--- Server/controllers/inviteController.js | 6 ++--- .../maintenanceWindowController.js | 6 ++--- Server/controllers/monitorController.js | 6 ++--- Server/controllers/queueController.js | 6 ++--- Server/controllers/settingsController.js | 6 ++--- Server/controllers/statusPageController.js | 4 ++-- Server/index.js | 24 +++++++++++++------ 9 files changed, 33 insertions(+), 37 deletions(-) diff --git a/Server/controllers/authController.js b/Server/controllers/authController.js index ce4f7b785..94952e3c7 100644 --- a/Server/controllers/authController.js +++ b/Server/controllers/authController.js @@ -13,16 +13,14 @@ import { getTokenFromHeaders, tokenType } from "../utils/utils.js"; import crypto from "crypto"; import { handleValidationError, handleError } from "./controllerUtils.js"; const SERVICE_NAME = "authController"; -import ServiceRegistry from "../service/serviceRegistry.js"; -import StringService from "../service/stringService.js"; class AuthController { - constructor(db, settingsService, emailService, jobQueue) { + constructor(db, settingsService, emailService, jobQueue, stringService) { this.db = db; this.settingsService = settingsService; this.emailService = emailService; this.jobQueue = jobQueue; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; } /** diff --git a/Server/controllers/checkController.js b/Server/controllers/checkController.js index a345212f3..2addd7359 100644 --- a/Server/controllers/checkController.js +++ b/Server/controllers/checkController.js @@ -12,16 +12,14 @@ import { import jwt from "jsonwebtoken"; import { getTokenFromHeaders } from "../utils/utils.js"; import { handleValidationError, handleError } from "./controllerUtils.js"; -import ServiceRegistry from "../service/serviceRegistry.js"; -import StringService from "../service/stringService.js"; const SERVICE_NAME = "checkController"; class CheckController { - constructor(db, settingsService) { + constructor(db, settingsService, stringService) { this.db = db; this.settingsService = settingsService; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; } createCheck = async (req, res, next) => { diff --git a/Server/controllers/inviteController.js b/Server/controllers/inviteController.js index 146eb34c2..40d045432 100644 --- a/Server/controllers/inviteController.js +++ b/Server/controllers/inviteController.js @@ -7,17 +7,15 @@ import logger from "../utils/logger.js"; import jwt from "jsonwebtoken"; import { handleError, handleValidationError } from "./controllerUtils.js"; import { getTokenFromHeaders } from "../utils/utils.js"; -import ServiceRegistry from "../service/serviceRegistry.js"; -import StringService from "../service/stringService.js"; const SERVICE_NAME = "inviteController"; class InviteController { - constructor(db, settingsService, emailService) { + constructor(db, settingsService, emailService, stringService) { this.db = db; this.settingsService = settingsService; this.emailService = emailService; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; } /** diff --git a/Server/controllers/maintenanceWindowController.js b/Server/controllers/maintenanceWindowController.js index a1a3395c8..a11f957ed 100644 --- a/Server/controllers/maintenanceWindowController.js +++ b/Server/controllers/maintenanceWindowController.js @@ -10,16 +10,14 @@ import { import jwt from "jsonwebtoken"; import { getTokenFromHeaders } from "../utils/utils.js"; import { handleValidationError, handleError } from "./controllerUtils.js"; -import ServiceRegistry from "../service/serviceRegistry.js"; -import StringService from "../service/stringService.js"; const SERVICE_NAME = "maintenanceWindowController"; class MaintenanceWindowController { - constructor(db, settingsService) { + constructor(db, settingsService, stringService) { this.db = db; this.settingsService = settingsService; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; } createMaintenanceWindows = async (req, res, next) => { diff --git a/Server/controllers/monitorController.js b/Server/controllers/monitorController.js index 66d68c74b..f65113cd7 100644 --- a/Server/controllers/monitorController.js +++ b/Server/controllers/monitorController.js @@ -20,16 +20,14 @@ import logger from "../utils/logger.js"; import { handleError, handleValidationError } from "./controllerUtils.js"; import axios from "axios"; import seedDb from "../db/mongo/utils/seedDb.js"; -import ServiceRegistry from "../service/serviceRegistry.js"; -import StringService from "../service/stringService.js"; const SERVICE_NAME = "monitorController"; class MonitorController { - constructor(db, settingsService, jobQueue) { + constructor(db, settingsService, jobQueue, stringService) { this.db = db; this.settingsService = settingsService; this.jobQueue = jobQueue; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; } /** diff --git a/Server/controllers/queueController.js b/Server/controllers/queueController.js index bd33fa001..330ade532 100644 --- a/Server/controllers/queueController.js +++ b/Server/controllers/queueController.js @@ -1,13 +1,11 @@ import { handleError } from "./controllerUtils.js"; -import ServiceRegistry from "../service/serviceRegistry.js"; -import StringService from "../service/stringService.js"; const SERVICE_NAME = "JobQueueController"; class JobQueueController { - constructor(jobQueue) { + constructor(jobQueue, stringService) { this.jobQueue = jobQueue; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; } getMetrics = async (req, res, next) => { diff --git a/Server/controllers/settingsController.js b/Server/controllers/settingsController.js index 7e4018241..54643dfbb 100644 --- a/Server/controllers/settingsController.js +++ b/Server/controllers/settingsController.js @@ -1,15 +1,13 @@ -import ServiceRegistry from "../service/serviceRegistry.js"; -import StringService from "../service/stringService.js"; import { updateAppSettingsBodyValidation } from "../validation/joi.js"; import { handleValidationError, handleError } from "./controllerUtils.js"; const SERVICE_NAME = "SettingsController"; class SettingsController { - constructor(db, settingsService) { + constructor(db, settingsService, stringService) { this.db = db; this.settingsService = settingsService; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; } getAppSettings = async (req, res, next) => { diff --git a/Server/controllers/statusPageController.js b/Server/controllers/statusPageController.js index b38989115..6c1192b95 100644 --- a/Server/controllers/statusPageController.js +++ b/Server/controllers/statusPageController.js @@ -10,9 +10,9 @@ import { successMessages, errorMessages } from "../utils/messages.js"; const SERVICE_NAME = "statusPageController"; class StatusPageController { - constructor(db) { + constructor(db, stringService) { this.db = db; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; } createStatusPage = async (req, res, next) => { diff --git a/Server/index.js b/Server/index.js index 76bb60023..8eefabf30 100644 --- a/Server/index.js +++ b/Server/index.js @@ -222,39 +222,49 @@ const startApp = async () => { ServiceRegistry.get(SettingsService.SERVICE_NAME), ServiceRegistry.get(EmailService.SERVICE_NAME), ServiceRegistry.get(JobQueue.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) ); const monitorController = new MonitorController( ServiceRegistry.get(MongoDB.SERVICE_NAME), ServiceRegistry.get(SettingsService.SERVICE_NAME), - ServiceRegistry.get(JobQueue.SERVICE_NAME) + ServiceRegistry.get(JobQueue.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) ); const settingsController = new SettingsController( ServiceRegistry.get(MongoDB.SERVICE_NAME), - ServiceRegistry.get(SettingsService.SERVICE_NAME) + ServiceRegistry.get(SettingsService.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) ); const checkController = new CheckController( ServiceRegistry.get(MongoDB.SERVICE_NAME), - ServiceRegistry.get(SettingsService.SERVICE_NAME) + ServiceRegistry.get(SettingsService.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) ); const inviteController = new InviteController( ServiceRegistry.get(MongoDB.SERVICE_NAME), ServiceRegistry.get(SettingsService.SERVICE_NAME), - ServiceRegistry.get(EmailService.SERVICE_NAME) + ServiceRegistry.get(EmailService.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) ); const maintenanceWindowController = new MaintenanceWindowController( ServiceRegistry.get(MongoDB.SERVICE_NAME), - ServiceRegistry.get(SettingsService.SERVICE_NAME) + ServiceRegistry.get(SettingsService.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) ); - const queueController = new QueueController(ServiceRegistry.get(JobQueue.SERVICE_NAME)); + const queueController = new QueueController( + ServiceRegistry.get(JobQueue.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) + ); const statusPageController = new StatusPageController( - ServiceRegistry.get(MongoDB.SERVICE_NAME) + ServiceRegistry.get(MongoDB.SERVICE_NAME), + ServiceRegistry.get(StringService.SERVICE_NAME) ); const distributedUptimeController = new DistributedUptimeController( From 158a3f14ea083ae99d556242e20dddb228b829ad Mon Sep 17 00:00:00 2001 From: cihat Date: Fri, 31 Jan 2025 09:55:21 +0300 Subject: [PATCH 23/52] fix: use default.json --- Server/index.js | 1 + Server/locales/default.json | 145 +++++++++++++++++++++++++++ Server/nodemon.json | 12 +++ Server/service/translationService.js | 24 ++--- 4 files changed, 168 insertions(+), 14 deletions(-) create mode 100644 Server/locales/default.json create mode 100644 Server/nodemon.json diff --git a/Server/index.js b/Server/index.js index 8eefabf30..b5f2561c7 100644 --- a/Server/index.js +++ b/Server/index.js @@ -160,6 +160,7 @@ const startApp = async () => { } } + // Create and Register Primary services const networkService = new NetworkService(axios, ping, logger, http, Docker, net); const translationService = new TranslationService(logger, networkService); const stringService = new StringService(translationService); diff --git a/Server/locales/default.json b/Server/locales/default.json new file mode 100644 index 000000000..a438e5fc8 --- /dev/null +++ b/Server/locales/default.json @@ -0,0 +1,145 @@ +{ + "dontHaveAccount": "Don't have account", + "email": "E-mail", + "forgotPassword": "Forgot Password", + "password": "password", + "signUp": "Sign up", + "submit": "Submit", + "title": "Title", + "continue": "Continue", + "enterEmail": "Enter your email", + "authLoginTitle": "Log In", + "authLoginEnterPassword": "Enter your password", + "commonPassword": "Password", + "commonBack": "Back", + "authForgotPasswordTitle": "Forgot password?", + "authForgotPasswordResetPassword": "Reset password", + "createPassword": "Create your password", + "createAPassword": "Create a password", + "authRegisterAlreadyHaveAccount": "Already have an account?", + "commonAppName": "BlueWave Uptime", + "authLoginEnterEmail": "Enter your email", + "authRegisterTitle": "Create an account", + "authRegisterStepOneTitle": "Create your account", + "authRegisterStepOneDescription": "Enter your details to get started", + "authRegisterStepTwoTitle": "Set up your profile", + "authRegisterStepTwoDescription": "Tell us more about yourself", + "authRegisterStepThreeTitle": "Almost done!", + "authRegisterStepThreeDescription": "Review your information", + "authForgotPasswordDescription": "No worries, we'll send you reset instructions.", + "authForgotPasswordSendInstructions": "Send instructions", + "authForgotPasswordBackTo": "Back to", + "authCheckEmailTitle": "Check your email", + "authCheckEmailDescription": "We sent a password reset link to {{email}}", + "authCheckEmailResendEmail": "Resend email", + "authCheckEmailBackTo": "Back to", + "goBackTo": "Go back to", + "authCheckEmailDidntReceiveEmail": "Didn't receive the email?", + "authCheckEmailClickToResend": "Click to resend", + "authSetNewPasswordTitle": "Set new password", + "authSetNewPasswordDescription": "Your new password must be different from previously used passwords.", + "authSetNewPasswordNewPassword": "New password", + "authSetNewPasswordConfirmPassword": "Confirm password", + "confirmPassword": "Confirm your password", + "authSetNewPasswordResetPassword": "Reset password", + "authSetNewPasswordBackTo": "Back to", + "authPasswordMustBeAtLeast": "Must be at least", + "authPasswordCharactersLong": "8 characters long", + "authPasswordMustContainAtLeast": "Must contain at least", + "authPasswordSpecialCharacter": "one special character", + "authPasswordOneNumber": "one number", + "authPasswordUpperCharacter": "one upper character", + "authPasswordLowerCharacter": "one lower character", + "authPasswordConfirmAndPassword": "Confirm password and password", + "authPasswordMustMatch": "must match", + "friendlyError": "Something went wrong...", + "unknownError": "An unknown error occurred", + "unauthorized": "Unauthorized access", + "authAdminExists": "Admin already exists", + "authInviteNotFound": "Invite not found", + "unknownService": "Unknown service", + "noAuthToken": "No auth token provided", + "invalidAuthToken": "Invalid auth token", + "expiredAuthToken": "Token expired", + "noRefreshToken": "No refresh token provided", + "invalidRefreshToken": "Invalid refresh token", + "expiredRefreshToken": "Refresh token expired", + "requestNewAccessToken": "Request new access token", + "invalidPayload": "Invalid payload", + "verifyOwnerNotFound": "Document not found", + "verifyOwnerUnauthorized": "Unauthorized access", + "insufficientPermissions": "Insufficient permissions", + "dbUserExists": "User already exists", + "dbUserNotFound": "User not found", + "dbTokenNotFound": "Token not found", + "dbResetPasswordBadMatch": "New password must be different from old password", + "dbFindMonitorById": "Monitor with id ${monitorId} not found", + "dbDeleteChecks": "No checks found for monitor with id ${monitorId}", + "authIncorrectPassword": "Incorrect password", + "authUnauthorized": "Unauthorized access", + "monitorGetById": "Monitor not found", + "monitorGetByUserId": "No monitors found for user", + "jobQueueWorkerClose": "Error closing worker", + "jobQueueDeleteJob": "Job not found in queue", + "jobQueueObliterate": "Error obliterating queue", + "pingCannotResolve": "No response", + "statusPageNotFound": "Status page not found", + "statusPageUrlNotUnique": "Status page url must be unique", + "dockerFail": "Failed to fetch Docker container information", + "dockerNotFound": "Docker container not found", + "portFail": "Failed to connect to port", + "alertCreate": "Alert created successfully", + "alertGetByUser": "Got alerts successfully", + "alertGetByMonitor": "Got alerts by Monitor successfully", + "alertGetById": "Got alert by Id successfully", + "alertEdit": "Alert edited successfully", + "alertDelete": "Alert deleted successfully", + "authCreateUser": "User created successfully", + "authLoginUser": "User logged in successfully", + "authLogoutUser": "User logged out successfully", + "authUpdateUser": "User updated successfully", + "authCreateRecoveryToken": "Recovery token created successfully", + "authVerifyRecoveryToken": "Recovery token verified successfully", + "authResetPassword": "Password reset successfully", + "authAdminCheck": "Admin check completed successfully", + "authDeleteUser": "User deleted successfully", + "authTokenRefreshed": "Auth token is refreshed", + "authGetAllUsers": "Got all users successfully", + "inviteIssued": "Invite sent successfully", + "inviteVerified": "Invite verified successfully", + "checkCreate": "Check created successfully", + "checkGet": "Got checks successfully", + "checkDelete": "Checks deleted successfully", + "checkUpdateTtl": "Checks TTL updated successfully", + "monitorGetAll": "Got all monitors successfully", + "monitorStatsById": "Got monitor stats by Id successfully", + "monitorGetByIdSuccess": "Got monitor by Id successfully", + "monitorGetByTeamId": "Got monitors by Team Id successfully", + "monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully", + "monitorCreate": "Monitor created successfully", + "monitorDelete": "Monitor deleted successfully", + "monitorEdit": "Monitor edited successfully", + "monitorCertificate": "Got monitor certificate successfully", + "monitorDemoAdded": "Successfully added demo monitors", + "queueGetMetrics": "Got metrics successfully", + "queueAddJob": "Job added successfully", + "queueObliterate": "Queue obliterated", + "jobQueueDeleteJobSuccess": "Job removed successfully", + "jobQueuePauseJob": "Job paused successfully", + "jobQueueResumeJob": "Job resumed successfully", + "maintenanceWindowGetById": "Got Maintenance Window by Id successfully", + "maintenanceWindowCreate": "Maintenance Window created successfully", + "maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully", + "maintenanceWindowDelete": "Maintenance Window deleted successfully", + "maintenanceWindowEdit": "Maintenance Window edited successfully", + "pingSuccess": "Success", + "getAppSettings": "Got app settings successfully", + "updateAppSettings": "Updated app settings successfully", + "statusPageByUrl": "Got status page by url successfully", + "statusPageCreate": "Status page created successfully", + "newTermsAdded": "New terms added to POEditor", + "dockerSuccess": "Docker container status fetched successfully", + "portSuccess": "Port connected successfully", + "monitorPause": "Monitor paused successfully", + "monitorResume": "Monitor resumed successfully" +} \ No newline at end of file diff --git a/Server/nodemon.json b/Server/nodemon.json new file mode 100644 index 000000000..b43c80fc1 --- /dev/null +++ b/Server/nodemon.json @@ -0,0 +1,12 @@ +{ + "ignore": [ + "locales/*", + "*.log", + "node_modules/*" + ], + "watch": [ + "*.js", + "*.json" + ], + "ext": "js,json" +} \ No newline at end of file diff --git a/Server/service/translationService.js b/Server/service/translationService.js index 652df340c..5a7877d4e 100644 --- a/Server/service/translationService.js +++ b/Server/service/translationService.js @@ -95,20 +95,18 @@ class TranslationService { if (hasError || Object.keys(this.translations[this._language]).length === 0) { this.logger.error({ - message: 'Failed to fetch translations from POEditor, using locales_en.json', + message: 'Failed to fetch translations from POEditor, using default.json', service: 'TranslationService', method: 'loadTranslations' }); - - // Load translations from locales_en.json in utils directory - const utilsPath = path.join(process.cwd(), 'utils'); - const utilsFilePath = path.join(utilsPath, 'locales_en.json'); - if (fs.existsSync(utilsFilePath)) { - const content = fs.readFileSync(utilsFilePath, 'utf8'); + // Load translations from default.json in locales directory + const defaultFilePath = path.join(this.localesDir, 'default.json'); + if (fs.existsSync(defaultFilePath)) { + const content = fs.readFileSync(defaultFilePath, 'utf8'); this.translations['en'] = JSON.parse(content); } else { - throw new Error('locales_en.json file not found'); + throw new Error('default.json file not found in locales directory'); } } @@ -163,10 +161,9 @@ class TranslationService { fs.writeFileSync(filePath, JSON.stringify(translations, null, 2)); } - const utilsPath = path.join(process.cwd(), 'utils'); + const defaultFilePath = path.join(this.localesDir, 'default.json'); const enTranslations = this.translations['en'] || {}; - const utilsFilePath = path.join(utilsPath, 'locales_en.json'); - fs.writeFileSync(utilsFilePath, JSON.stringify(enTranslations, null, 2)); + fs.writeFileSync(defaultFilePath, JSON.stringify(enTranslations, null, 2)); this.logger.info({ message: 'Translations saved to files successfully', @@ -245,9 +242,8 @@ class TranslationService { async syncTermsWithPOEditor() { try { - const utilsPath = path.join(process.cwd(), 'utils'); - const utilsFilePath = path.join(utilsPath, 'locales_en.json'); - const enTranslations = JSON.parse(fs.readFileSync(utilsFilePath, 'utf8')); + const defaultFilePath = path.join(this.localesDir, 'default.json'); + const enTranslations = JSON.parse(fs.readFileSync(defaultFilePath, 'utf8')); const localTerms = Object.keys(enTranslations) .map(term => term); From 7b6eed96f466f3333fb779717d55d434977a82a4 Mon Sep 17 00:00:00 2001 From: cihatata Date: Thu, 6 Feb 2025 23:16:48 +0300 Subject: [PATCH 24/52] fix: remove fetch requests --- Docker/dist/docker-compose.yaml | 2 - Server/index.js | 4 +- Server/locales/{default.json => en.json} | 0 Server/service/networkService.js | 104 +----------- Server/service/translationService.js | 201 +---------------------- Server/utils/locales_en.json | 145 ---------------- 6 files changed, 6 insertions(+), 450 deletions(-) rename Server/locales/{default.json => en.json} (100%) delete mode 100644 Server/utils/locales_en.json diff --git a/Docker/dist/docker-compose.yaml b/Docker/dist/docker-compose.yaml index 1bf579fbb..420941e7e 100644 --- a/Docker/dist/docker-compose.yaml +++ b/Docker/dist/docker-compose.yaml @@ -21,8 +21,6 @@ services: environment: - DB_CONNECTION_STRING=mongodb://mongodb:27017/uptime_db - REDIS_HOST=redis - - POEDITOR_API_TOKEN= - - POEDITOR_PROJECT_ID= # volumes: # - /var/run/docker.sock:/var/run/docker.sock:ro redis: diff --git a/Server/index.js b/Server/index.js index b5f2561c7..af30ae1cf 100644 --- a/Server/index.js +++ b/Server/index.js @@ -161,8 +161,7 @@ const startApp = async () => { } // Create and Register Primary services - const networkService = new NetworkService(axios, ping, logger, http, Docker, net); - const translationService = new TranslationService(logger, networkService); + const translationService = new TranslationService(logger); const stringService = new StringService(translationService); ServiceRegistry.register(StringService.SERVICE_NAME, stringService); @@ -171,6 +170,7 @@ const startApp = async () => { await db.connect(); // Create services + const networkService = new NetworkService(axios, ping, logger, http, Docker, net, stringService); const settingsService = new SettingsService(AppSettings); await settingsService.loadSettings(); const emailService = new EmailService( diff --git a/Server/locales/default.json b/Server/locales/en.json similarity index 100% rename from Server/locales/default.json rename to Server/locales/en.json diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 119f2891b..a1cb64ff2 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -12,9 +12,8 @@ const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push"; */ class NetworkService { static SERVICE_NAME = SERVICE_NAME; - static POEDITOR_BASE_URL = 'https://api.poeditor.com/v2'; - constructor(axios, ping, logger, http, Docker, net) { + constructor(axios, ping, logger, http, Docker, net, stringService) { this.TYPE_PING = "ping"; this.TYPE_HTTP = "http"; this.TYPE_PAGESPEED = "pagespeed"; @@ -31,17 +30,7 @@ class NetworkService { this.http = http; this.Docker = Docker; this.net = net; - - this.apiToken = process.env.POEDITOR_API_TOKEN; - this.projectId = process.env.POEDITOR_PROJECT_ID; - - if (!this.apiToken || !this.projectId) { - this.logger.error({ - message: 'POEditor API token or project ID is missing in environment variables', - service: this.SERVICE_NAME, - method: 'constructor' - }); - } + this.stringService = stringService; } /** @@ -410,95 +399,6 @@ class NetworkService { return this.handleUnsupportedType(type); } } - - async getPoEditorLanguages() { - try { - const params = new URLSearchParams(); - params.append('api_token', this.apiToken); - params.append('id', this.projectId); - - const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/languages/list`, params, { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }); - - return response.data.result.languages.map(lang => lang.code); - } catch (error) { - error.service = this.SERVICE_NAME; - error.method = "getPoEditorLanguages"; - throw error; - } - } - - async exportPoEditorTranslations(language) { - try { - const params = new URLSearchParams(); - params.append('api_token', this.apiToken); - params.append('id', this.projectId); - params.append('language', language); - params.append('type', 'key_value_json'); - - const exportResponse = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/projects/export`, params, { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }); - - const { url } = exportResponse.data.result; - const translationsResponse = await this.axios.get(url); - return translationsResponse.data; - } catch (error) { - error.service = this.SERVICE_NAME; - error.method = "exportPoEditorTranslations"; - throw error; - } - } - - async getPoEditorTerms() { - try { - const params = new URLSearchParams(); - params.append('api_token', this.apiToken); - params.append('id', this.projectId); - - const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/terms/list`, params, { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }); - - return response.data.result?.terms?.map(term => term.term.trim()) || []; - } catch (error) { - error.service = this.SERVICE_NAME; - error.method = "getPoEditorTerms"; - throw error; - } - } - - async addPoEditorTerms(terms) { - try { - const formattedTerms = terms.map(termObj => ({ - term: Object.keys(termObj)[0] - })); - - const params = new URLSearchParams(); - params.append('api_token', this.apiToken); - params.append('id', this.projectId); - params.append('data', JSON.stringify(formattedTerms)); - - const response = await this.axios.post(`${NetworkService.POEDITOR_BASE_URL}/terms/add`, params, { - headers: { - 'Content-Type': 'application/x-www-form-urlencoded' - } - }); - - return response.data; - } catch (error) { - error.service = this.SERVICE_NAME; - error.method = "addPoEditorTerms"; - throw error; - } - } } export default NetworkService; diff --git a/Server/service/translationService.js b/Server/service/translationService.js index 5a7877d4e..64c62bff2 100644 --- a/Server/service/translationService.js +++ b/Server/service/translationService.js @@ -4,9 +4,8 @@ import path from 'path'; class TranslationService { static SERVICE_NAME = 'TranslationService'; - constructor(logger, networkService) { + constructor(logger) { this.logger = logger; - this.networkService = networkService; this.translations = {}; this._language = 'en'; this.localesDir = path.join(process.cwd(), 'locales'); @@ -22,10 +21,8 @@ class TranslationService { async initialize() { try { - await this.loadTranslations(); + await this.loadFromFiles(); - // Yeni eklenen terimleri POEditor'e gönder - await this.syncTermsWithPOEditor(); } catch (error) { this.logger.error({ message: error.message, @@ -73,113 +70,6 @@ class TranslationService { } } - async loadTranslations() { - let hasError = false; - try { - const languages = await this.getLanguages(); - - for (const language of languages) { - try { - const translations = await this.exportTranslations(language); - this.translations[language] = translations; - } catch (error) { - hasError = true; - this.logger.error({ - message: `Failed to fetch translations from POEditor for language ${language}: ${error.message}`, - service: 'TranslationService', - method: 'loadTranslations', - stack: error.stack - }); - } - } - - if (hasError || Object.keys(this.translations[this._language]).length === 0) { - this.logger.error({ - message: 'Failed to fetch translations from POEditor, using default.json', - service: 'TranslationService', - method: 'loadTranslations' - }); - - // Load translations from default.json in locales directory - const defaultFilePath = path.join(this.localesDir, 'default.json'); - if (fs.existsSync(defaultFilePath)) { - const content = fs.readFileSync(defaultFilePath, 'utf8'); - this.translations['en'] = JSON.parse(content); - } else { - throw new Error('default.json file not found in locales directory'); - } - } - - await this.saveTranslations(); - } catch (error) { - hasError = true; - this.logger.error({ - message: error.message, - service: 'TranslationService', - method: 'loadTranslations', - stack: error.stack - }); - } - } - - async getLanguages() { - try { - return await this.networkService.getPoEditorLanguages(); - } catch (error) { - this.logger.error({ - message: error.message, - service: 'TranslationService', - method: 'getLanguages', - stack: error.stack - }); - return ['en']; - } - } - - async exportTranslations(language) { - try { - return await this.networkService.exportPoEditorTranslations(language); - } catch (error) { - this.logger.error({ - message: error.message, - service: 'TranslationService', - method: 'exportTranslations', - stack: error.stack - }); - return {}; - } - } - - async saveTranslations() { - try { - if (!fs.existsSync(this.localesDir)) { - fs.mkdirSync(this.localesDir); - } - - for (const [language, translations] of Object.entries(this.translations)) { - const filePath = path.join(this.localesDir, `${language}.json`); - fs.writeFileSync(filePath, JSON.stringify(translations, null, 2)); - } - - const defaultFilePath = path.join(this.localesDir, 'default.json'); - const enTranslations = this.translations['en'] || {}; - fs.writeFileSync(defaultFilePath, JSON.stringify(enTranslations, null, 2)); - - this.logger.info({ - message: 'Translations saved to files successfully', - service: 'TranslationService', - method: 'saveTranslations' - }); - } catch (error) { - this.logger.error({ - message: error.message, - service: 'TranslationService', - method: 'saveTranslations', - stack: error.stack - }); - } - } - getTranslation(key) { let language = this._language; @@ -195,93 +85,6 @@ class TranslationService { return key; } } - - async getTermsFromPOEditor() { - try { - return await this.networkService.getPoEditorTerms(); - } catch (error) { - this.logger.error({ - message: error.message, - service: 'TranslationService', - method: 'getTermsFromPOEditor', - stack: error.stack - }); - return []; - } - } - - async addTermsToPOEditor(terms) { - try { - if (!terms.length) return; - - const response = await this.networkService.addPoEditorTerms(terms); - - if (response.response?.status === 'fail') { - throw new Error(response.response.message || 'Failed to add terms to POEditor'); - } - - this.logger.info({ - message: `${terms.length} new terms added to POEditor`, - service: 'TranslationService', - method: 'addTermsToPOEditor', - response: response - }); - - return response; - } catch (error) { - this.logger.error({ - message: `Failed to add terms to POEditor: ${error.message}`, - service: 'TranslationService', - method: 'addTermsToPOEditor', - stack: error.stack, - terms: terms - }); - throw error; - } - } - - async syncTermsWithPOEditor() { - try { - const defaultFilePath = path.join(this.localesDir, 'default.json'); - const enTranslations = JSON.parse(fs.readFileSync(defaultFilePath, 'utf8')); - const localTerms = Object.keys(enTranslations) - .map(term => term); - - const poeditorTerms = await this.getTermsFromPOEditor(); - - const newTerms = localTerms?.filter(term => !poeditorTerms?.includes(term)); - - - this.logger.info({ - message: `Comparison results - New terms found: ${newTerms.length}`, - sampleNewTerms: newTerms.slice(0, 5), - service: 'TranslationService', - method: 'syncTermsWithPOEditor' - }); - - if (newTerms.length > 0) { - const formattedTerms = newTerms.map(term => ({ - [term]: enTranslations[term] || '', - })); - - await this.addTermsToPOEditor(formattedTerms); - - } else { - this.logger.info({ - message: 'No new terms found to synchronize', - service: 'TranslationService', - method: 'syncTermsWithPOEditor' - }); - } - } catch (error) { - this.logger.error({ - message: error.message, - service: 'TranslationService', - method: 'syncTermsWithPOEditor', - stack: error.stack - }); - } - } } export default TranslationService; \ No newline at end of file diff --git a/Server/utils/locales_en.json b/Server/utils/locales_en.json deleted file mode 100644 index a438e5fc8..000000000 --- a/Server/utils/locales_en.json +++ /dev/null @@ -1,145 +0,0 @@ -{ - "dontHaveAccount": "Don't have account", - "email": "E-mail", - "forgotPassword": "Forgot Password", - "password": "password", - "signUp": "Sign up", - "submit": "Submit", - "title": "Title", - "continue": "Continue", - "enterEmail": "Enter your email", - "authLoginTitle": "Log In", - "authLoginEnterPassword": "Enter your password", - "commonPassword": "Password", - "commonBack": "Back", - "authForgotPasswordTitle": "Forgot password?", - "authForgotPasswordResetPassword": "Reset password", - "createPassword": "Create your password", - "createAPassword": "Create a password", - "authRegisterAlreadyHaveAccount": "Already have an account?", - "commonAppName": "BlueWave Uptime", - "authLoginEnterEmail": "Enter your email", - "authRegisterTitle": "Create an account", - "authRegisterStepOneTitle": "Create your account", - "authRegisterStepOneDescription": "Enter your details to get started", - "authRegisterStepTwoTitle": "Set up your profile", - "authRegisterStepTwoDescription": "Tell us more about yourself", - "authRegisterStepThreeTitle": "Almost done!", - "authRegisterStepThreeDescription": "Review your information", - "authForgotPasswordDescription": "No worries, we'll send you reset instructions.", - "authForgotPasswordSendInstructions": "Send instructions", - "authForgotPasswordBackTo": "Back to", - "authCheckEmailTitle": "Check your email", - "authCheckEmailDescription": "We sent a password reset link to {{email}}", - "authCheckEmailResendEmail": "Resend email", - "authCheckEmailBackTo": "Back to", - "goBackTo": "Go back to", - "authCheckEmailDidntReceiveEmail": "Didn't receive the email?", - "authCheckEmailClickToResend": "Click to resend", - "authSetNewPasswordTitle": "Set new password", - "authSetNewPasswordDescription": "Your new password must be different from previously used passwords.", - "authSetNewPasswordNewPassword": "New password", - "authSetNewPasswordConfirmPassword": "Confirm password", - "confirmPassword": "Confirm your password", - "authSetNewPasswordResetPassword": "Reset password", - "authSetNewPasswordBackTo": "Back to", - "authPasswordMustBeAtLeast": "Must be at least", - "authPasswordCharactersLong": "8 characters long", - "authPasswordMustContainAtLeast": "Must contain at least", - "authPasswordSpecialCharacter": "one special character", - "authPasswordOneNumber": "one number", - "authPasswordUpperCharacter": "one upper character", - "authPasswordLowerCharacter": "one lower character", - "authPasswordConfirmAndPassword": "Confirm password and password", - "authPasswordMustMatch": "must match", - "friendlyError": "Something went wrong...", - "unknownError": "An unknown error occurred", - "unauthorized": "Unauthorized access", - "authAdminExists": "Admin already exists", - "authInviteNotFound": "Invite not found", - "unknownService": "Unknown service", - "noAuthToken": "No auth token provided", - "invalidAuthToken": "Invalid auth token", - "expiredAuthToken": "Token expired", - "noRefreshToken": "No refresh token provided", - "invalidRefreshToken": "Invalid refresh token", - "expiredRefreshToken": "Refresh token expired", - "requestNewAccessToken": "Request new access token", - "invalidPayload": "Invalid payload", - "verifyOwnerNotFound": "Document not found", - "verifyOwnerUnauthorized": "Unauthorized access", - "insufficientPermissions": "Insufficient permissions", - "dbUserExists": "User already exists", - "dbUserNotFound": "User not found", - "dbTokenNotFound": "Token not found", - "dbResetPasswordBadMatch": "New password must be different from old password", - "dbFindMonitorById": "Monitor with id ${monitorId} not found", - "dbDeleteChecks": "No checks found for monitor with id ${monitorId}", - "authIncorrectPassword": "Incorrect password", - "authUnauthorized": "Unauthorized access", - "monitorGetById": "Monitor not found", - "monitorGetByUserId": "No monitors found for user", - "jobQueueWorkerClose": "Error closing worker", - "jobQueueDeleteJob": "Job not found in queue", - "jobQueueObliterate": "Error obliterating queue", - "pingCannotResolve": "No response", - "statusPageNotFound": "Status page not found", - "statusPageUrlNotUnique": "Status page url must be unique", - "dockerFail": "Failed to fetch Docker container information", - "dockerNotFound": "Docker container not found", - "portFail": "Failed to connect to port", - "alertCreate": "Alert created successfully", - "alertGetByUser": "Got alerts successfully", - "alertGetByMonitor": "Got alerts by Monitor successfully", - "alertGetById": "Got alert by Id successfully", - "alertEdit": "Alert edited successfully", - "alertDelete": "Alert deleted successfully", - "authCreateUser": "User created successfully", - "authLoginUser": "User logged in successfully", - "authLogoutUser": "User logged out successfully", - "authUpdateUser": "User updated successfully", - "authCreateRecoveryToken": "Recovery token created successfully", - "authVerifyRecoveryToken": "Recovery token verified successfully", - "authResetPassword": "Password reset successfully", - "authAdminCheck": "Admin check completed successfully", - "authDeleteUser": "User deleted successfully", - "authTokenRefreshed": "Auth token is refreshed", - "authGetAllUsers": "Got all users successfully", - "inviteIssued": "Invite sent successfully", - "inviteVerified": "Invite verified successfully", - "checkCreate": "Check created successfully", - "checkGet": "Got checks successfully", - "checkDelete": "Checks deleted successfully", - "checkUpdateTtl": "Checks TTL updated successfully", - "monitorGetAll": "Got all monitors successfully", - "monitorStatsById": "Got monitor stats by Id successfully", - "monitorGetByIdSuccess": "Got monitor by Id successfully", - "monitorGetByTeamId": "Got monitors by Team Id successfully", - "monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully", - "monitorCreate": "Monitor created successfully", - "monitorDelete": "Monitor deleted successfully", - "monitorEdit": "Monitor edited successfully", - "monitorCertificate": "Got monitor certificate successfully", - "monitorDemoAdded": "Successfully added demo monitors", - "queueGetMetrics": "Got metrics successfully", - "queueAddJob": "Job added successfully", - "queueObliterate": "Queue obliterated", - "jobQueueDeleteJobSuccess": "Job removed successfully", - "jobQueuePauseJob": "Job paused successfully", - "jobQueueResumeJob": "Job resumed successfully", - "maintenanceWindowGetById": "Got Maintenance Window by Id successfully", - "maintenanceWindowCreate": "Maintenance Window created successfully", - "maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully", - "maintenanceWindowDelete": "Maintenance Window deleted successfully", - "maintenanceWindowEdit": "Maintenance Window edited successfully", - "pingSuccess": "Success", - "getAppSettings": "Got app settings successfully", - "updateAppSettings": "Updated app settings successfully", - "statusPageByUrl": "Got status page by url successfully", - "statusPageCreate": "Status page created successfully", - "newTermsAdded": "New terms added to POEditor", - "dockerSuccess": "Docker container status fetched successfully", - "portSuccess": "Port connected successfully", - "monitorPause": "Monitor paused successfully", - "monitorResume": "Monitor resumed successfully" -} \ No newline at end of file From 4c92922d40172b82ebf70e2d21bea48f3cb50fac Mon Sep 17 00:00:00 2001 From: cihatata Date: Thu, 6 Feb 2025 23:38:13 +0300 Subject: [PATCH 25/52] fix: i18n rebase --- Server/controllers/statusPageController.js | 7 +++---- Server/db/mongo/modules/monitorModule.js | 3 +-- Server/db/mongo/modules/statusPageModule.js | 13 ++++++++++--- Server/locales/en.json | 4 +++- Server/service/stringService.js | 9 +++++++++ 5 files changed, 26 insertions(+), 10 deletions(-) diff --git a/Server/controllers/statusPageController.js b/Server/controllers/statusPageController.js index 6c1192b95..00b8d538d 100644 --- a/Server/controllers/statusPageController.js +++ b/Server/controllers/statusPageController.js @@ -5,7 +5,6 @@ import { getStatusPageQueryValidation, imageValidation, } from "../validation/joi.js"; -import { successMessages, errorMessages } from "../utils/messages.js"; const SERVICE_NAME = "statusPageController"; @@ -47,12 +46,12 @@ class StatusPageController { try { const statusPage = await this.db.updateStatusPage(req.body, req.file); if (statusPage === null) { - const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND); + const error = new Error(this.stringService.statusPageNotFound); error.status = 404; throw error; } return res.success({ - msg: successMessages.STATUS_PAGE_UPDATE, + msg: this.stringService.statusPageUpdate, data: statusPage, }); } catch (error) { @@ -109,7 +108,7 @@ class StatusPageController { try { await this.db.deleteStatusPage(req.params.url); return res.success({ - msg: successMessages.STATUS_PAGE_DELETE, + msg: this.stringService.statusPageDelete, }); } catch (error) { next(handleError(error, SERVICE_NAME, "deleteStatusPage")); diff --git a/Server/db/mongo/modules/monitorModule.js b/Server/db/mongo/modules/monitorModule.js index e217bc9e1..e1db09f0a 100644 --- a/Server/db/mongo/modules/monitorModule.js +++ b/Server/db/mongo/modules/monitorModule.js @@ -3,7 +3,6 @@ import Check from "../../models/Check.js"; import PageSpeedCheck from "../../models/PageSpeedCheck.js"; import HardwareCheck from "../../models/HardwareCheck.js"; import DistributedUptimeCheck from "../../models/DistributedUptimeCheck.js"; -import { errorMessages } from "../../../utils/messages.js"; import Notification from "../../models/Notification.js"; import { NormalizeData, NormalizeDataUptimeDetails } from "../../../utils/dataUtils.js"; import ServiceRegistry from "../../../service/serviceRegistry.js"; @@ -376,7 +375,7 @@ const getDistributedUptimeDetailsById = async (req) => { const { monitorId } = req.params; const monitor = await Monitor.findById(monitorId); if (monitor === null || monitor === undefined) { - throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId)); + throw new Error(this.stringService.dbFindMonitorById(monitorId)); } const { dateRange, normalize } = req.query; diff --git a/Server/db/mongo/modules/statusPageModule.js b/Server/db/mongo/modules/statusPageModule.js index bd2b47375..23cfe3f4d 100644 --- a/Server/db/mongo/modules/statusPageModule.js +++ b/Server/db/mongo/modules/statusPageModule.js @@ -1,10 +1,13 @@ import StatusPage from "../../models/StatusPage.js"; -import { errorMessages } from "../../../utils/messages.js"; import { NormalizeData } from "../../../utils/dataUtils.js"; +import ServiceRegistry from "../../../service/serviceRegistry.js"; +import StringService from "../../../service/stringService.js"; const SERVICE_NAME = "statusPageModule"; const createStatusPage = async (statusPageData, image) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + try { const statusPage = new StatusPage({ ...statusPageData }); if (image) { @@ -67,10 +70,12 @@ const getStatusPageByUrl = async (url, type) => { }; const getStatusPagesByTeamId = async (teamId) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + try { const statusPages = await StatusPage.find({ teamId }); if (statusPages.length === 0) { - const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND); + const error = new Error(stringService.statusPageNotFound); error.status = 404; throw error; } @@ -83,6 +88,8 @@ const getStatusPagesByTeamId = async (teamId) => { }; const getStatusPage = async (url) => { + const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + try { const statusPageQuery = await StatusPage.aggregate([ { $match: { url: url } }, @@ -156,7 +163,7 @@ const getStatusPage = async (url) => { }, ]); if (!statusPageQuery.length) { - const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND); + const error = new Error(stringService.statusPageNotFound); error.status = 404; throw error; } diff --git a/Server/locales/en.json b/Server/locales/en.json index a438e5fc8..c67ca0a7b 100644 --- a/Server/locales/en.json +++ b/Server/locales/en.json @@ -141,5 +141,7 @@ "dockerSuccess": "Docker container status fetched successfully", "portSuccess": "Port connected successfully", "monitorPause": "Monitor paused successfully", - "monitorResume": "Monitor resumed successfully" + "monitorResume": "Monitor resumed successfully", + "statusPageDelete": "Status page deleted successfully", + "statusPageUpdate": "Status page updated successfully" } \ No newline at end of file diff --git a/Server/service/stringService.js b/Server/service/stringService.js index 3c95dacd9..cf679e85a 100644 --- a/Server/service/stringService.js +++ b/Server/service/stringService.js @@ -241,6 +241,15 @@ class StringService { return this.translationService.getTranslation('statusPageCreate'); } + get statusPageDelete() { + return this.translationService.getTranslation('statusPageDelete'); + } + + get statusPageUpdate() { + return this.translationService.getTranslation('statusPageUpdate'); + } + + get statusPageNotFound() { return this.translationService.getTranslation('statusPageNotFound'); } From 93fa4ca13b52feaa7926b3134316320388c26129 Mon Sep 17 00:00:00 2001 From: cihatata Date: Thu, 6 Feb 2025 23:53:37 +0300 Subject: [PATCH 26/52] fix: add injection for jobqueque --- Server/index.js | 1 + Server/service/jobQueue.js | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Server/index.js b/Server/index.js index af30ae1cf..b1f3f914b 100644 --- a/Server/index.js +++ b/Server/index.js @@ -192,6 +192,7 @@ const startApp = async () => { networkService, notificationService, settingsService, + stringService, logger, Queue, Worker diff --git a/Server/service/jobQueue.js b/Server/service/jobQueue.js index b4c91758e..be36383d2 100644 --- a/Server/service/jobQueue.js +++ b/Server/service/jobQueue.js @@ -12,8 +12,6 @@ const QUEUE_LOOKUP = { }; const getSchedulerId = (monitor) => `scheduler:${monitor.type}:${monitor._id}`; -import ServiceRegistry from "../service/serviceRegistry.js"; -import StringService from "../service/stringService.js"; class NewJobQueue { static SERVICE_NAME = SERVICE_NAME; @@ -24,6 +22,7 @@ class NewJobQueue { networkService, notificationService, settingsService, + stringService, logger, Queue, Worker @@ -46,7 +45,7 @@ class NewJobQueue { this.settingsService = settingsService; this.logger = logger; this.Worker = Worker; - this.stringService = ServiceRegistry.get(StringService.SERVICE_NAME); + this.stringService = stringService; QUEUE_NAMES.forEach((name) => { this.queues[name] = new Queue(name, { connection }); From fddaf8f11f301eed229001ee2135378e3dc90268 Mon Sep 17 00:00:00 2001 From: cihat Date: Tue, 11 Feb 2025 08:15:29 +0300 Subject: [PATCH 27/52] fix: resolve conflict --- Server/controllers/statusPageController.js | 4 +- Server/locales/en.json | 3 +- Server/service/stringService.js | 5 +- Server/utils/messages.js | 159 --------------------- 4 files changed, 8 insertions(+), 163 deletions(-) delete mode 100644 Server/utils/messages.js diff --git a/Server/controllers/statusPageController.js b/Server/controllers/statusPageController.js index 00b8d538d..cde6f772b 100644 --- a/Server/controllers/statusPageController.js +++ b/Server/controllers/statusPageController.js @@ -83,7 +83,7 @@ class StatusPageController { try { const statusPage = await this.db.getStatusPageByUrl(req.params.url, req.query.type); return res.success({ - msg: successMessages.STATUS_PAGE_BY_URL, + msg: this.stringService.statusPageByUrl, data: statusPage, }); } catch (error) { @@ -96,7 +96,7 @@ class StatusPageController { const teamId = req.params.teamId; const statusPages = await this.db.getStatusPagesByTeamId(teamId); return res.success({ - msg: successMessages.STATUS_PAGE_BY_TEAM_ID, + msg: this.stringService.statusPageByTeamId, data: statusPages, }); } catch (error) { diff --git a/Server/locales/en.json b/Server/locales/en.json index c67ca0a7b..ba50e1d40 100644 --- a/Server/locales/en.json +++ b/Server/locales/en.json @@ -143,5 +143,6 @@ "monitorPause": "Monitor paused successfully", "monitorResume": "Monitor resumed successfully", "statusPageDelete": "Status page deleted successfully", - "statusPageUpdate": "Status page updated successfully" + "statusPageUpdate": "Status page updated successfully", + "statusPageByTeamId": "Got status pages by team id successfully" } \ No newline at end of file diff --git a/Server/service/stringService.js b/Server/service/stringService.js index cf679e85a..a65e1655b 100644 --- a/Server/service/stringService.js +++ b/Server/service/stringService.js @@ -249,11 +249,14 @@ class StringService { return this.translationService.getTranslation('statusPageUpdate'); } - get statusPageNotFound() { return this.translationService.getTranslation('statusPageNotFound'); } + get statusPageByTeamId() { + return this.translationService.getTranslation('statusPageByTeamId'); + } + get statusPageUrlNotUnique() { return this.translationService.getTranslation('statusPageUrlNotUnique'); } diff --git a/Server/utils/messages.js b/Server/utils/messages.js deleted file mode 100644 index f4568165b..000000000 --- a/Server/utils/messages.js +++ /dev/null @@ -1,159 +0,0 @@ -import ServiceRegistry from '../service/serviceRegistry.js'; -import TranslationService from '../service/translationService.js'; - -const getTranslatedMessage = (key, language = 'en') => { - console.log("getTranslatedMessage", key, language); - const translationService = ServiceRegistry.get(TranslationService.SERVICE_NAME); - return translationService.getTranslation(key, language); -}; - -const errorMessages = { - // General Errors: - FRIENDLY_ERROR: (language) => getTranslatedMessage('FRIENDLY_ERROR', language), - UNKNOWN_ERROR: (language) => getTranslatedMessage('UNKNOWN_ERROR', language), - - // Auth Controller - UNAUTHORIZED: (language) => getTranslatedMessage('UNAUTHORIZED', language), - AUTH_ADMIN_EXISTS: (language) => getTranslatedMessage('AUTH_ADMIN_EXISTS', language), - AUTH_INVITE_NOT_FOUND: (language) => getTranslatedMessage('AUTH_INVITE_NOT_FOUND', language), - - //Error handling middleware - UNKNOWN_SERVICE: (language) => getTranslatedMessage('UNKNOWN_SERVICE', language), - NO_AUTH_TOKEN: (language) => getTranslatedMessage('NO_AUTH_TOKEN', language), - INVALID_AUTH_TOKEN: (language) => getTranslatedMessage('INVALID_AUTH_TOKEN', language), - EXPIRED_AUTH_TOKEN: (language) => getTranslatedMessage('EXPIRED_AUTH_TOKEN', language), - NO_REFRESH_TOKEN: (language) => getTranslatedMessage('NO_REFRESH_TOKEN', language), - INVALID_REFRESH_TOKEN: (language) => getTranslatedMessage('INVALID_REFRESH_TOKEN', language), - EXPIRED_REFRESH_TOKEN: (language) => getTranslatedMessage('EXPIRED_REFRESH_TOKEN', language), - REQUEST_NEW_ACCESS_TOKEN: (language) => getTranslatedMessage('REQUEST_NEW_ACCESS_TOKEN', language), - - //Payload - INVALID_PAYLOAD: (language) => getTranslatedMessage('INVALID_PAYLOAD', language), - - //Ownership Middleware - VERIFY_OWNER_NOT_FOUND: (language) => getTranslatedMessage('VERIFY_OWNER_NOT_FOUND', language), - VERIFY_OWNER_UNAUTHORIZED: (language) => getTranslatedMessage('VERIFY_OWNER_UNAUTHORIZED', language), - - //Permissions Middleware - INSUFFICIENT_PERMISSIONS: (language) => getTranslatedMessage('INSUFFICIENT_PERMISSIONS', language), - - //DB Errors - DB_USER_EXISTS: (language) => getTranslatedMessage('DB_USER_EXISTS', language), - DB_USER_NOT_FOUND: (language) => getTranslatedMessage('DB_USER_NOT_FOUND', language), - DB_TOKEN_NOT_FOUND: (language) => getTranslatedMessage('DB_TOKEN_NOT_FOUND', language), - DB_RESET_PASSWORD_BAD_MATCH: (language) => getTranslatedMessage('DB_RESET_PASSWORD_BAD_MATCH', language), - DB_FIND_MONITOR_BY_ID: (monitorId, language) => getTranslatedMessage('DB_FIND_MONITOR_BY_ID', language).replace('{monitorId}', monitorId), - DB_DELETE_CHECKS: (monitorId, language) => getTranslatedMessage('DB_DELETE_CHECKS', language).replace('{monitorId}', monitorId), - - //Auth errors - AUTH_INCORRECT_PASSWORD: (language) => getTranslatedMessage('AUTH_INCORRECT_PASSWORD', language), - AUTH_UNAUTHORIZED: (language) => getTranslatedMessage('AUTH_UNAUTHORIZED', language), - - // Monitor Errors - MONITOR_GET_BY_ID: (language) => getTranslatedMessage('MONITOR_GET_BY_ID', language), - MONITOR_GET_BY_USER_ID: (language) => getTranslatedMessage('MONITOR_GET_BY_USER_ID', language), - - // Job Queue Errors - JOB_QUEUE_WORKER_CLOSE: (language) => getTranslatedMessage('JOB_QUEUE_WORKER_CLOSE', language), - JOB_QUEUE_DELETE_JOB: (language) => getTranslatedMessage('JOB_QUEUE_DELETE_JOB', language), - JOB_QUEUE_OBLITERATE: (language) => getTranslatedMessage('JOB_QUEUE_OBLITERATE', language), - - // PING Operations - PING_CANNOT_RESOLVE: (language) => getTranslatedMessage('PING_CANNOT_RESOLVE', language), - - // Status Page Errors - STATUS_PAGE_NOT_FOUND: (language) => getTranslatedMessage('STATUS_PAGE_NOT_FOUND', language), - STATUS_PAGE_URL_NOT_UNIQUE: (language) => getTranslatedMessage('STATUS_PAGE_URL_NOT_UNIQUE', language), - - // Docker - DOCKER_FAIL: (language) => getTranslatedMessage('DOCKER_FAIL', language), - DOCKER_NOT_FOUND: (language) => getTranslatedMessage('DOCKER_NOT_FOUND', language), - - // Port - PORT_FAIL: (language) => getTranslatedMessage('PORT_FAIL', language), -}; - -const successMessages = { - //Alert Controller - ALERT_CREATE: (language) => getTranslatedMessage('ALERT_CREATE', language), - ALERT_GET_BY_USER: (language) => getTranslatedMessage('ALERT_GET_BY_USER', language), - ALERT_GET_BY_MONITOR: (language) => getTranslatedMessage('ALERT_GET_BY_MONITOR', language), - ALERT_GET_BY_ID: (language) => getTranslatedMessage('ALERT_GET_BY_ID', language), - ALERT_EDIT: (language) => getTranslatedMessage('ALERT_EDIT', language), - ALERT_DELETE: (language) => getTranslatedMessage('ALERT_DELETE', language), - - // Auth Controller - AUTH_CREATE_USER: (language) => getTranslatedMessage('AUTH_CREATE_USER', language), - AUTH_LOGIN_USER: (language) => getTranslatedMessage('AUTH_LOGIN_USER', language), - AUTH_LOGOUT_USER: (language) => getTranslatedMessage('AUTH_LOGOUT_USER', language), - AUTH_UPDATE_USER: (language) => getTranslatedMessage('AUTH_UPDATE_USER', language), - AUTH_CREATE_RECOVERY_TOKEN: (language) => getTranslatedMessage('AUTH_CREATE_RECOVERY_TOKEN', language), - AUTH_VERIFY_RECOVERY_TOKEN: (language) => getTranslatedMessage('AUTH_VERIFY_RECOVERY_TOKEN', language), - AUTH_RESET_PASSWORD: (language) => getTranslatedMessage('AUTH_RESET_PASSWORD', language), - AUTH_ADMIN_CHECK: (language) => getTranslatedMessage('AUTH_ADMIN_CHECK', language), - AUTH_DELETE_USER: (language) => getTranslatedMessage('AUTH_DELETE_USER', language), - AUTH_TOKEN_REFRESHED: (language) => getTranslatedMessage('AUTH_TOKEN_REFRESHED', language), - AUTH_GET_ALL_USERS: (language) => getTranslatedMessage('AUTH_GET_ALL_USERS', language), - - // Invite Controller - INVITE_ISSUED: (language) => getTranslatedMessage('INVITE_ISSUED', language), - INVITE_VERIFIED: (language) => getTranslatedMessage('INVITE_VERIFIED', language), - - // Check Controller - CHECK_CREATE: (language) => getTranslatedMessage('CHECK_CREATE', language), - CHECK_GET: (language) => getTranslatedMessage('CHECK_GET', language), - CHECK_DELETE: (language) => getTranslatedMessage('CHECK_DELETE', language), - CHECK_UPDATE_TTL: (language) => getTranslatedMessage('CHECK_UPDATE_TTL', language), - - //Monitor Controller - MONITOR_GET_ALL: (language) => getTranslatedMessage('MONITOR_GET_ALL', language), - MONITOR_STATS_BY_ID: (language) => getTranslatedMessage('MONITOR_STATS_BY_ID', language), - MONITOR_GET_BY_ID: (language) => getTranslatedMessage('MONITOR_GET_BY_ID', language), - MONITOR_GET_BY_TEAM_ID: (language) => getTranslatedMessage('MONITOR_GET_BY_TEAM_ID', language), - MONITOR_GET_BY_USER_ID: (userId, language) => getTranslatedMessage('MONITOR_GET_BY_USER_ID', language).replace('{userId}', userId), - MONITOR_CREATE: (language) => getTranslatedMessage('MONITOR_CREATE', language), - MONITOR_DELETE: (language) => getTranslatedMessage('MONITOR_DELETE', language), - MONITOR_EDIT: (language) => getTranslatedMessage('MONITOR_EDIT', language), - MONITOR_CERTIFICATE: (language) => getTranslatedMessage('MONITOR_CERTIFICATE', language), - MONITOR_DEMO_ADDED: (language) => getTranslatedMessage('MONITOR_DEMO_ADDED', language), - - // Queue Controller - QUEUE_GET_METRICS: (language) => getTranslatedMessage('QUEUE_GET_METRICS', language), - QUEUE_ADD_JOB: (language) => getTranslatedMessage('QUEUE_ADD_JOB', language), - QUEUE_OBLITERATE: (language) => getTranslatedMessage('QUEUE_OBLITERATE', language), - - //Job Queue - JOB_QUEUE_DELETE_JOB: (language) => getTranslatedMessage('JOB_QUEUE_DELETE_JOB', language), - JOB_QUEUE_OBLITERATE: (language) => getTranslatedMessage('JOB_QUEUE_OBLITERATE', language), - JOB_QUEUE_PAUSE_JOB: (language) => getTranslatedMessage('JOB_QUEUE_PAUSE_JOB', language), - JOB_QUEUE_RESUME_JOB: (language) => getTranslatedMessage('JOB_QUEUE_RESUME_JOB', language), - - //Maintenance Window Controller - MAINTENANCE_WINDOW_GET_BY_ID: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_GET_BY_ID', language), - MAINTENANCE_WINDOW_CREATE: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_CREATE', language), - MAINTENANCE_WINDOW_GET_BY_TEAM: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_GET_BY_TEAM', language), - MAINTENANCE_WINDOW_DELETE: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_DELETE', language), - MAINTENANCE_WINDOW_EDIT: (language) => getTranslatedMessage('MAINTENANCE_WINDOW_EDIT', language), - - //Ping Operations - PING_SUCCESS: (language) => getTranslatedMessage('PING_SUCCESS', language), - - // App Settings - GET_APP_SETTINGS: (language) => getTranslatedMessage('GET_APP_SETTINGS', language), - UPDATE_APP_SETTINGS: (language) => getTranslatedMessage('UPDATE_APP_SETTINGS', language), - - // Status Page - STATUS_PAGE_BY_URL: "Got status page by url successfully", - STATUS_PAGE: "Got status page successfully", - STATUS_PAGE_CREATE: "Status page created successfully", - STATUS_PAGE_DELETE: "Status page deleted successfully", - STATUS_PAGE_UPDATE: "Status page updated successfully", - STATUS_PAGE_BY_TEAM_ID: "Got status pages by team id successfully", - // Docker - DOCKER_SUCCESS: (language) => getTranslatedMessage('DOCKER_SUCCESS', language), - - // Port - PORT_SUCCESS: (language) => getTranslatedMessage('PORT_SUCCESS', language), -}; - -export { errorMessages, successMessages }; From 1b5c01eeebf322acfd702f611973b0fcb62396da Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Feb 2025 07:16:43 -0800 Subject: [PATCH 28/52] remove error throw --- Server/controllers/statusPageController.js | 1 + 1 file changed, 1 insertion(+) diff --git a/Server/controllers/statusPageController.js b/Server/controllers/statusPageController.js index 1a69c92ec..7d75fc7d3 100644 --- a/Server/controllers/statusPageController.js +++ b/Server/controllers/statusPageController.js @@ -95,6 +95,7 @@ class StatusPageController { try { const teamId = req.params.teamId; const statusPages = await this.db.getStatusPagesByTeamId(teamId); + return res.success({ msg: successMessages.STATUS_PAGE_BY_TEAM_ID, data: statusPages, From 34c8e3ce55a4452589e2ffe1507bdd22d37dc94e Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Feb 2025 07:17:03 -0800 Subject: [PATCH 29/52] add leading / --- .../StatusPages/Components/StatusPagesTable/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx b/Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx index 6e7988153..746d10f42 100644 --- a/Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx +++ b/Client/src/Pages/StatusPage/StatusPages/Components/StatusPagesTable/index.jsx @@ -25,7 +25,7 @@ const StatusPagesTable = ({ data }) => { alignItems="center" gap={theme.spacing(2)} > - {row.url} + {`/${row.url}`} ); From f6f2ae5b3a24d26df3a209debc97c88fe6bc931b Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Feb 2025 07:17:45 -0800 Subject: [PATCH 30/52] remove 404 check --- .../Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx b/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx index 29e7f2b89..26eec8104 100644 --- a/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx +++ b/Client/src/Pages/StatusPage/StatusPages/Hooks/useStatusPagesFetch.jsx @@ -20,9 +20,6 @@ const useStatusPagesFetch = () => { setStatusPages(res?.data?.data); } catch (error) { setNetworkError(true); - if (error.response.status === 404) { - return; - } createToast({ body: error.message, }); From af0f940c22495ab91e5152babff9200a289a5a79 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Feb 2025 07:18:09 -0800 Subject: [PATCH 31/52] Add skeleton --- .../Pages/StatusPage/StatusPages/index.jsx | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/Client/src/Pages/StatusPage/StatusPages/index.jsx b/Client/src/Pages/StatusPage/StatusPages/index.jsx index 8ec741729..9027467a4 100644 --- a/Client/src/Pages/StatusPage/StatusPages/index.jsx +++ b/Client/src/Pages/StatusPage/StatusPages/index.jsx @@ -5,11 +5,11 @@ import Fallback from "../../../Components/Fallback"; import MonitorCreateHeader from "../../../Components/MonitorCreateHeader"; import GenericFallback from "../../../Components/GenericFallback"; import StatusPagesTable from "./Components/StatusPagesTable"; +import SkeletonLayout from "../../../Components/Skeletons/FullPage"; // Utils import { useTheme } from "@emotion/react"; import { useStatusPagesFetch } from "./Hooks/useStatusPagesFetch"; import { useIsAdmin } from "../../../Hooks/useIsAdmin"; -import { useNavigate } from "react-router"; const BREADCRUMBS = [{ name: `Status Pages`, path: "" }]; const StatusPages = () => { @@ -17,30 +17,9 @@ const StatusPages = () => { const theme = useTheme(); const isAdmin = useIsAdmin(); const [isLoading, networkError, statusPages] = useStatusPagesFetch(); - const navigate = useNavigate(); - // Handlers - const handleStatusPageClick = (statusPage) => { - if (statusPage.type === "distributed") { - navigate(`/status/distributed/${statusPage.url}`); - } else if (statusPage.type === "uptime") { - navigate(`/status/uptime/${statusPage.url}`); - } - }; - - if (!isLoading && typeof statusPages === "undefined") { - return ( - - ); + if (isLoading) { + return ; } if (networkError === true) { @@ -57,6 +36,21 @@ const StatusPages = () => { ); } + + if (!isLoading && typeof statusPages !== "undefined" && statusPages.length === 0) { + return ( + + ); + } return ( From c7614ad89eab7e03bfabd32f25028222299e8815 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Feb 2025 07:18:24 -0800 Subject: [PATCH 32/52] add reusable skeleton --- Client/src/Components/Skeletons/FullPage/index.jsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 Client/src/Components/Skeletons/FullPage/index.jsx diff --git a/Client/src/Components/Skeletons/FullPage/index.jsx b/Client/src/Components/Skeletons/FullPage/index.jsx new file mode 100644 index 000000000..233fc3c06 --- /dev/null +++ b/Client/src/Components/Skeletons/FullPage/index.jsx @@ -0,0 +1,14 @@ +import { Stack, Skeleton } from "@mui/material"; + +export const SkeletonLayout = () => { + return ( + + + + ); +}; + +export default SkeletonLayout; From 7846c759d94268d12bbd239008b2f68313c47cde Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Feb 2025 07:18:37 -0800 Subject: [PATCH 33/52] remove 404 --- Server/db/mongo/modules/statusPageModule.js | 5 - Server/locales/en.json.bak | 147 ++++++++++++++++++++ 2 files changed, 147 insertions(+), 5 deletions(-) create mode 100644 Server/locales/en.json.bak diff --git a/Server/db/mongo/modules/statusPageModule.js b/Server/db/mongo/modules/statusPageModule.js index 8687f06ed..a0a1b5af6 100644 --- a/Server/db/mongo/modules/statusPageModule.js +++ b/Server/db/mongo/modules/statusPageModule.js @@ -69,11 +69,6 @@ const getStatusPageByUrl = async (url, type) => { const getStatusPagesByTeamId = async (teamId) => { try { const statusPages = await StatusPage.find({ teamId }); - if (statusPages.length === 0) { - const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND); - error.status = 404; - throw error; - } return statusPages; } catch (error) { error.service = SERVICE_NAME; diff --git a/Server/locales/en.json.bak b/Server/locales/en.json.bak new file mode 100644 index 000000000..eaef58eef --- /dev/null +++ b/Server/locales/en.json.bak @@ -0,0 +1,147 @@ +{ + "dontHaveAccount": "Don't have account", + "email": "E-mail", + "forgotPassword": "Forgot Password", + "password": "password", + "signUp": "Sign up", + "submit": "Submit", + "title": "Title", + "continue": "Continue", + "enterEmail": "Enter your email", + "authLoginTitle": "Log In", + "authLoginEnterPassword": "Enter your password", + "commonPassword": "Password", + "commonBack": "Back", + "authForgotPasswordTitle": "Forgot password?", + "authForgotPasswordResetPassword": "Reset password", + "createPassword": "Create your password", + "createAPassword": "Create a password", + "authRegisterAlreadyHaveAccount": "Already have an account?", + "commonAppName": "BlueWave Uptime", + "authLoginEnterEmail": "Enter your email", + "authRegisterTitle": "Create an account", + "authRegisterStepOneTitle": "Create your account", + "authRegisterStepOneDescription": "Enter your details to get started", + "authRegisterStepTwoTitle": "Set up your profile", + "authRegisterStepTwoDescription": "Tell us more about yourself", + "authRegisterStepThreeTitle": "Almost done!", + "authRegisterStepThreeDescription": "Review your information", + "authForgotPasswordDescription": "No worries, we'll send you reset instructions.", + "authForgotPasswordSendInstructions": "Send instructions", + "authForgotPasswordBackTo": "Back to", + "authCheckEmailTitle": "Check your email", + "authCheckEmailDescription": "We sent a password reset link to {{email}}", + "authCheckEmailResendEmail": "Resend email", + "authCheckEmailBackTo": "Back to", + "goBackTo": "Go back to", + "authCheckEmailDidntReceiveEmail": "Didn't receive the email?", + "authCheckEmailClickToResend": "Click to resend", + "authSetNewPasswordTitle": "Set new password", + "authSetNewPasswordDescription": "Your new password must be different from previously used passwords.", + "authSetNewPasswordNewPassword": "New password", + "authSetNewPasswordConfirmPassword": "Confirm password", + "confirmPassword": "Confirm your password", + "authSetNewPasswordResetPassword": "Reset password", + "authSetNewPasswordBackTo": "Back to", + "authPasswordMustBeAtLeast": "Must be at least", + "authPasswordCharactersLong": "8 characters long", + "authPasswordMustContainAtLeast": "Must contain at least", + "authPasswordSpecialCharacter": "one special character", + "authPasswordOneNumber": "one number", + "authPasswordUpperCharacter": "one upper character", + "authPasswordLowerCharacter": "one lower character", + "authPasswordConfirmAndPassword": "Confirm password and password", + "authPasswordMustMatch": "must match", + "friendlyError": "Something went wrong...", + "unknownError": "An unknown error occurred", + "unauthorized": "Unauthorized access", + "authAdminExists": "Admin already exists", + "authInviteNotFound": "Invite not found", + "unknownService": "Unknown service", + "noAuthToken": "No auth token provided", + "invalidAuthToken": "Invalid auth token", + "expiredAuthToken": "Token expired", + "noRefreshToken": "No refresh token provided", + "invalidRefreshToken": "Invalid refresh token", + "expiredRefreshToken": "Refresh token expired", + "requestNewAccessToken": "Request new access token", + "invalidPayload": "Invalid payload", + "verifyOwnerNotFound": "Document not found", + "verifyOwnerUnauthorized": "Unauthorized access", + "insufficientPermissions": "Insufficient permissions", + "dbUserExists": "User already exists", + "dbUserNotFound": "User not found", + "dbTokenNotFound": "Token not found", + "dbResetPasswordBadMatch": "New password must be different from old password", + "dbFindMonitorById": "Monitor with id ${monitorId} not found", + "dbDeleteChecks": "No checks found for monitor with id ${monitorId}", + "authIncorrectPassword": "Incorrect password", + "authUnauthorized": "Unauthorized access", + "monitorGetById": "Monitor not found", + "monitorGetByUserId": "No monitors found for user", + "jobQueueWorkerClose": "Error closing worker", + "jobQueueDeleteJob": "Job not found in queue", + "jobQueueObliterate": "Error obliterating queue", + "pingCannotResolve": "No response", + "statusPageNotFound": "Status page not found", + "statusPageUrlNotUnique": "Status page url must be unique", + "dockerFail": "Failed to fetch Docker container information", + "dockerNotFound": "Docker container not found", + "portFail": "Failed to connect to port", + "alertCreate": "Alert created successfully", + "alertGetByUser": "Got alerts successfully", + "alertGetByMonitor": "Got alerts by Monitor successfully", + "alertGetById": "Got alert by Id successfully", + "alertEdit": "Alert edited successfully", + "alertDelete": "Alert deleted successfully", + "authCreateUser": "User created successfully", + "authLoginUser": "User logged in successfully", + "authLogoutUser": "User logged out successfully", + "authUpdateUser": "User updated successfully", + "authCreateRecoveryToken": "Recovery token created successfully", + "authVerifyRecoveryToken": "Recovery token verified successfully", + "authResetPassword": "Password reset successfully", + "authAdminCheck": "Admin check completed successfully", + "authDeleteUser": "User deleted successfully", + "authTokenRefreshed": "Auth token is refreshed", + "authGetAllUsers": "Got all users successfully", + "inviteIssued": "Invite sent successfully", + "inviteVerified": "Invite verified successfully", + "checkCreate": "Check created successfully", + "checkGet": "Got checks successfully", + "checkDelete": "Checks deleted successfully", + "checkUpdateTtl": "Checks TTL updated successfully", + "monitorGetAll": "Got all monitors successfully", + "monitorStatsById": "Got monitor stats by Id successfully", + "monitorGetByIdSuccess": "Got monitor by Id successfully", + "monitorGetByTeamId": "Got monitors by Team Id successfully", + "monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully", + "monitorCreate": "Monitor created successfully", + "monitorDelete": "Monitor deleted successfully", + "monitorEdit": "Monitor edited successfully", + "monitorCertificate": "Got monitor certificate successfully", + "monitorDemoAdded": "Successfully added demo monitors", + "queueGetMetrics": "Got metrics successfully", + "queueAddJob": "Job added successfully", + "queueObliterate": "Queue obliterated", + "jobQueueDeleteJobSuccess": "Job removed successfully", + "jobQueuePauseJob": "Job paused successfully", + "jobQueueResumeJob": "Job resumed successfully", + "maintenanceWindowGetById": "Got Maintenance Window by Id successfully", + "maintenanceWindowCreate": "Maintenance Window created successfully", + "maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully", + "maintenanceWindowDelete": "Maintenance Window deleted successfully", + "maintenanceWindowEdit": "Maintenance Window edited successfully", + "pingSuccess": "Success", + "getAppSettings": "Got app settings successfully", + "updateAppSettings": "Updated app settings successfully", + "statusPageByUrl": "Got status page by url successfully", + "statusPageCreate": "Status page created successfully", + "newTermsAdded": "New terms added to POEditor", + "dockerSuccess": "Docker container status fetched successfully", + "portSuccess": "Port connected successfully", + "monitorPause": "Monitor paused successfully", + "monitorResume": "Monitor resumed successfully", + "statusPageDelete": "Status page deleted successfully", + "statusPageUpdate": "Status page updated successfully" +} From 0cc9966e25d70a7bd5854ed0a0881fec3b948708 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 11 Feb 2025 15:21:49 +0000 Subject: [PATCH 34/52] Update material-ui monorepo --- Client/package-lock.json | 36 ++++++++++++++++++------------------ Client/package.json | 6 +++--- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/Client/package-lock.json b/Client/package-lock.json index 556caf1bc..27b3f3502 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -12,9 +12,9 @@ "@emotion/styled": "^11.13.0", "@fontsource/roboto": "^5.0.13", "@hello-pangea/dnd": "^17.0.0", - "@mui/icons-material": "6.4.3", - "@mui/lab": "6.0.0-beta.26", - "@mui/material": "6.4.3", + "@mui/icons-material": "6.4.4", + "@mui/lab": "6.0.0-beta.27", + "@mui/material": "6.4.4", "@mui/x-charts": "^7.5.1", "@mui/x-data-grid": "7.26.0", "@mui/x-date-pickers": "7.26.0", @@ -1215,9 +1215,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.3.tgz", - "integrity": "sha512-hlyOzo2ObarllAOeT1ZSAusADE5NZNencUeIvXrdQ1Na+FL1lcznhbxfV5He1KqGiuR8Az3xtCUcYKwMVGFdzg==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.4.tgz", + "integrity": "sha512-r+J0EditrekkTtO2CnCBCOGpNaDYwJqz8lH4rj6o/anDcskZFJodBlG8aCJkS8DL/CF/9EHS+Gz53EbmYEnQbw==", "license": "MIT", "funding": { "type": "opencollective", @@ -1225,9 +1225,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.3.tgz", - "integrity": "sha512-3IY9LpjkwIJVgL/SkZQKKCUcumdHdQEsJaIavvsQze2QEztBt0HJ17naToN0DBBdhKdtwX5xXrfD6ZFUeWWk8g==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.4.tgz", + "integrity": "sha512-uF1chGaoFmYdRUomK6f8kgJfWosk9A3HXWiVD0vQm+2mE7f25eTQ1E8RRO11LXpnUBqu8Rbv/uGlpnjT/u1Ksg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -1240,7 +1240,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.4.3", + "@mui/material": "^6.4.4", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1251,9 +1251,9 @@ } }, "node_modules/@mui/lab": { - "version": "6.0.0-beta.26", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.26.tgz", - "integrity": "sha512-auu2dXp6jslzW4Cp0tfYWv0xO9FmuwROsjyWcB9wPlAsEoWhh5N1FW8dqESDwaSKqFz5LwV+Y2vsYjYsYX9aOw==", + "version": "6.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.27.tgz", + "integrity": "sha512-weLxPsCs2wJKgWKf46shXHE+x7qlf5VxMK3P+4HsWasMakV/uTmxsoT7PG3QCvakGQ2TdpZtQLE2umJKC0mvKQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -1274,7 +1274,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material": "^6.4.3", + "@mui/material": "^6.4.4", "@mui/material-pigment-css": "^6.4.3", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0", @@ -1296,13 +1296,13 @@ } }, "node_modules/@mui/material": { - "version": "6.4.3", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.3.tgz", - "integrity": "sha512-ubtQjplbWneIEU8Y+4b2VA0CDBlyH5I3AmVFGmsLyDe/bf0ubxav5t11c8Afem6rkSFWPlZA2DilxmGka1xiKQ==", + "version": "6.4.4", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.4.tgz", + "integrity": "sha512-ISVPrIsPQsxnwvS40C4u03AuNSPigFeS2+n1qpuEZ94hDsdMi19dQM2JcC9CHEhXecSIQjP1RTyY0mPiSpSrFQ==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.3", + "@mui/core-downloads-tracker": "^6.4.4", "@mui/system": "^6.4.3", "@mui/types": "^7.2.21", "@mui/utils": "^6.4.3", diff --git a/Client/package.json b/Client/package.json index d4c26e1c5..124adebb9 100644 --- a/Client/package.json +++ b/Client/package.json @@ -15,9 +15,9 @@ "@emotion/styled": "^11.13.0", "@fontsource/roboto": "^5.0.13", "@hello-pangea/dnd": "^17.0.0", - "@mui/icons-material": "6.4.3", - "@mui/lab": "6.0.0-beta.26", - "@mui/material": "6.4.3", + "@mui/icons-material": "6.4.4", + "@mui/lab": "6.0.0-beta.27", + "@mui/material": "6.4.4", "@mui/x-charts": "^7.5.1", "@mui/x-data-grid": "7.26.0", "@mui/x-date-pickers": "7.26.0", From 40ec2c8ceebcc3ff8463074dd26050766ce1bb7d Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 11 Feb 2025 15:35:15 +0000 Subject: [PATCH 35/52] Update dependency @hello-pangea/dnd to v18 --- Client/package-lock.json | 40 +++++++++++++--------------------------- Client/package.json | 2 +- 2 files changed, 14 insertions(+), 28 deletions(-) diff --git a/Client/package-lock.json b/Client/package-lock.json index 27b3f3502..febb13eb1 100644 --- a/Client/package-lock.json +++ b/Client/package-lock.json @@ -11,7 +11,7 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@fontsource/roboto": "^5.0.13", - "@hello-pangea/dnd": "^17.0.0", + "@hello-pangea/dnd": "^18.0.0", "@mui/icons-material": "6.4.4", "@mui/lab": "6.0.0-beta.27", "@mui/material": "6.4.4", @@ -285,9 +285,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -1002,21 +1002,20 @@ } }, "node_modules/@hello-pangea/dnd": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-17.0.0.tgz", - "integrity": "sha512-LDDPOix/5N0j5QZxubiW9T0M0+1PR0rTDWeZF5pu1Tz91UQnuVK4qQ/EjY83Qm2QeX0eM8qDXANfDh3VVqtR4Q==", + "version": "18.0.1", + "resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-18.0.1.tgz", + "integrity": "sha512-xojVWG8s/TGrKT1fC8K2tIWeejJYTAeJuj36zM//yEm/ZrnZUSFGS15BpO+jGZT1ybWvyXmeDJwPYb4dhWlbZQ==", + "license": "Apache-2.0", "dependencies": { - "@babel/runtime": "^7.25.6", + "@babel/runtime": "^7.26.7", "css-box-model": "^1.2.1", - "memoize-one": "^6.0.0", "raf-schd": "^4.0.3", - "react-redux": "^9.1.2", - "redux": "^5.0.1", - "use-memo-one": "^1.1.3" + "react-redux": "^9.2.0", + "redux": "^5.0.1" }, "peerDependencies": { - "react": "^18.0.0", - "react-dom": "^18.0.0" + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" } }, "node_modules/@humanwhocodes/config-array": { @@ -5333,11 +5332,6 @@ "node": ">= 0.4" } }, - "node_modules/memoize-one": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", - "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==" - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -6886,14 +6880,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-memo-one": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/use-memo-one/-/use-memo-one-1.1.3.tgz", - "integrity": "sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==", - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - } - }, "node_modules/use-sync-external-store": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.4.0.tgz", diff --git a/Client/package.json b/Client/package.json index 124adebb9..7ecc398c0 100644 --- a/Client/package.json +++ b/Client/package.json @@ -14,7 +14,7 @@ "@emotion/react": "^11.13.3", "@emotion/styled": "^11.13.0", "@fontsource/roboto": "^5.0.13", - "@hello-pangea/dnd": "^17.0.0", + "@hello-pangea/dnd": "^18.0.0", "@mui/icons-material": "6.4.4", "@mui/lab": "6.0.0-beta.27", "@mui/material": "6.4.4", From 931fd197ac8ec4100e9db3667533bbabb0b05a90 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Feb 2025 09:06:18 -0800 Subject: [PATCH 36/52] fix link --- .../StatusPage/Status/Components/ControlsHeader/index.jsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx b/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx index e8ea104b8..2c96db054 100644 --- a/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx +++ b/Client/src/Pages/StatusPage/Status/Components/ControlsHeader/index.jsx @@ -43,7 +43,11 @@ const Controls = ({ isDeleteOpen, setIsDeleteOpen, isDeleting, url, type }) => { variant="contained" color="secondary" onClick={() => { - navigate(`/status/`); + if (type === "uptime") { + navigate(`/status/uptime/configure/${url}`); + } else { + navigate(`/status/distributed/configure/${url}`); + } }} sx={{ px: theme.spacing(5), From 8bbcb67066e9498f6dcac374863bd4f4bc7331e7 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 11 Feb 2025 17:22:19 +0000 Subject: [PATCH 37/52] chore(deps): update dependency eslint to v9.20.1 --- Server/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Server/package-lock.json b/Server/package-lock.json index d7cb00d6c..4b6ef1a2b 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -3071,9 +3071,9 @@ } }, "node_modules/eslint": { - "version": "9.20.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.0.tgz", - "integrity": "sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA==", + "version": "9.20.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz", + "integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==", "dev": true, "license": "MIT", "dependencies": { From 60000968836526c929d58ba6e45c483e3bb57159 Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Thu, 6 Feb 2025 02:20:40 +0000 Subject: [PATCH 38/52] Add monitor fields --- Server/db/models/Monitor.js | 14 ++++++++++++++ Server/validation/joi.js | 6 ++++++ 2 files changed, 20 insertions(+) diff --git a/Server/db/models/Monitor.js b/Server/db/models/Monitor.js index 90cbb1d62..eb5f67c1b 100644 --- a/Server/db/models/Monitor.js +++ b/Server/db/models/Monitor.js @@ -38,6 +38,20 @@ const MonitorSchema = mongoose.Schema( "distributed_http", ], }, + jsonPath: { + type: String, + }, + expectedValue: { + type: String, + }, + matchMethod: { + type: String, + enum: [ + "equal", + "include", + "regex", + ], + }, url: { type: String, required: true, diff --git a/Server/validation/joi.js b/Server/validation/joi.js index 42c41d8a5..b60022fc5 100644 --- a/Server/validation/joi.js +++ b/Server/validation/joi.js @@ -194,6 +194,9 @@ const createMonitorBodyValidation = joi.object({ }), notifications: joi.array().items(joi.object()), secret: joi.string(), + jsonPath: joi.string(), + expectedValue: joi.string(), + matchMethod: joi.string(), }); const editMonitorBodyValidation = joi.object({ @@ -202,6 +205,9 @@ const editMonitorBodyValidation = joi.object({ interval: joi.number(), notifications: joi.array().items(joi.object()), secret: joi.string(), + jsonPath: joi.string(), + expectedValue: joi.string(), + matchMethod: joi.string(), }); const pauseMonitorParamValidation = joi.object({ From b7a8596b7b435fb45c12632142183108e07067b8 Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Thu, 6 Feb 2025 03:36:52 +0000 Subject: [PATCH 39/52] Add jmespath denpendency --- Server/package-lock.json | 10 ++++++++++ Server/package.json | 1 + 2 files changed, 11 insertions(+) diff --git a/Server/package-lock.json b/Server/package-lock.json index 4b6ef1a2b..cb6cfc11b 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -19,6 +19,7 @@ "handlebars": "^4.7.8", "helmet": "^8.0.0", "ioredis": "^5.4.2", + "jmespath": "^0.16.0", "joi": "^17.13.1", "jsonwebtoken": "9.0.2", "mailersend": "^2.2.0", @@ -4441,6 +4442,15 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jmespath": { + "version": "0.16.0", + "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz", + "integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==", + "license": "Apache-2.0", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/joi": { "version": "17.13.3", "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", diff --git a/Server/package.json b/Server/package.json index 6695a444a..735a12048 100644 --- a/Server/package.json +++ b/Server/package.json @@ -26,6 +26,7 @@ "handlebars": "^4.7.8", "helmet": "^8.0.0", "ioredis": "^5.4.2", + "jmespath": "^0.16.0", "joi": "^17.13.1", "jsonwebtoken": "9.0.2", "mailersend": "^2.2.0", From 81a0ec8b23b8f96fcb2aea7db6625ff4d0cf1829 Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Thu, 6 Feb 2025 03:38:05 +0000 Subject: [PATCH 40/52] Replace variable declarations with destructuring --- Server/service/networkService.js | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index a1cb64ff2..120fa5b2a 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -122,20 +122,19 @@ class NetworkService { */ async requestHttp(job) { try { - const url = job.data.url; + const { url, secret, _id, teamId, type, jsonPath, matchMethod, expectedValue } = job.data; const config = {}; - job.data.secret !== undefined && - (config.headers = { Authorization: `Bearer ${job.data.secret}` }); + secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` }); const { response, responseTime, error } = await this.timeRequest(() => this.axios.get(url, config) ); const httpResponse = { - monitorId: job.data._id, - teamId: job.data.teamId, - type: job.data.type, + monitorId: _id, + teamId, + type, responseTime, payload: response?.data, }; From 8b496033823ca40742fa4845183d4eb8093bed78 Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Thu, 6 Feb 2025 03:38:36 +0000 Subject: [PATCH 41/52] Support jsonPath and expectedValue --- Server/service/networkService.js | 51 +++++++++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 1 deletion(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 120fa5b2a..9a661f104 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -1,3 +1,5 @@ +import jmespath from 'jmespath'; +import { errorMessages, successMessages } from "../utils/messages.js"; const SERVICE_NAME = "NetworkService"; const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push"; @@ -146,8 +148,55 @@ class NetworkService { httpResponse.message = this.http.STATUS_CODES[code] || "Network Error"; return httpResponse; } - httpResponse.status = true; + httpResponse.code = response.status; + + if (expectedValue) { + // validate if response data match expected value + let result = response?.data; + + this.logger.info({ + service: this.SERVICE_NAME, + method: "requestHttp", + message: "match result with expected value", + details: { expectedValue, result, jsonPath, matchMethod } + }); + + if (jsonPath) { + const contentType = response.headers['content-type']; + + if (contentType && contentType.includes('application/json')) { + try { + result = jmespath.search(result, jsonPath).toString(); + } catch (error) { + httpResponse.status = false; + httpResponse.message = "JSONPath Search Error"; + return httpResponse; + } + } else { + httpResponse.status = false; + httpResponse.message = "Response Not JSON"; + return httpResponse; + } + } + + if (!result) { + httpResponse.status = false; + httpResponse.message = "Empty Result"; + return httpResponse; + } + + let match; + if (matchMethod === "include") match = result.includes(expectedValue); + else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result); + else match = result === expectedValue; + + httpResponse.status = match; + httpResponse.message = match ? "Match" : "Not Match"; + return httpResponse; + } + + httpResponse.status = true; httpResponse.message = this.http.STATUS_CODES[response.status]; return httpResponse; } catch (error) { From b263e2746cf506fe8ebfd0121e493e890522f19f Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Thu, 6 Feb 2025 03:46:06 +0000 Subject: [PATCH 42/52] Convert result to string --- Server/service/networkService.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 9a661f104..362e43db3 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -167,7 +167,7 @@ class NetworkService { if (contentType && contentType.includes('application/json')) { try { - result = jmespath.search(result, jsonPath).toString(); + result = jmespath.search(result, jsonPath); } catch (error) { httpResponse.status = false; httpResponse.message = "JSONPath Search Error"; @@ -180,13 +180,14 @@ class NetworkService { } } - if (!result) { + if (result === null || result === undefined) { httpResponse.status = false; httpResponse.message = "Empty Result"; return httpResponse; } let match; + result = result.toString(); if (matchMethod === "include") match = result.includes(expectedValue); else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result); else match = result === expectedValue; From 05e448c4c5b1d3993f6638f3ab7dec9bbd4f9a1a Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Thu, 6 Feb 2025 06:28:29 +0000 Subject: [PATCH 43/52] Add job name and id in log --- Server/service/networkService.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 362e43db3..ef035be31 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -124,7 +124,7 @@ class NetworkService { */ async requestHttp(job) { try { - const { url, secret, _id, teamId, type, jsonPath, matchMethod, expectedValue } = job.data; + const { url, secret, _id, name, teamId, type, jsonPath, matchMethod, expectedValue } = job.data; const config = {}; secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` }); @@ -158,13 +158,13 @@ class NetworkService { this.logger.info({ service: this.SERVICE_NAME, method: "requestHttp", - message: "match result with expected value", + message: `Job: [${name}](${_id}) match result with expected value`, details: { expectedValue, result, jsonPath, matchMethod } }); - + if (jsonPath) { const contentType = response.headers['content-type']; - + if (contentType && contentType.includes('application/json')) { try { result = jmespath.search(result, jsonPath); @@ -191,7 +191,7 @@ class NetworkService { if (matchMethod === "include") match = result.includes(expectedValue); else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result); else match = result === expectedValue; - + httpResponse.status = match; httpResponse.message = match ? "Match" : "Not Match"; return httpResponse; From 9fe2de8969933eb2a01847e9bf749e1357afeaf1 Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Thu, 6 Feb 2025 06:39:32 +0000 Subject: [PATCH 44/52] Use JSON.stringify instead of toString --- Server/service/networkService.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index ef035be31..3ceeef743 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -187,7 +187,7 @@ class NetworkService { } let match; - result = result.toString(); + result = typeof result === "object" ? JSON.stringify(result) : result.toString(); if (matchMethod === "include") match = result.includes(expectedValue); else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result); else match = result === expectedValue; From 4d12608a220ba242f33619045f71f1f297eef9cb Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Mon, 10 Feb 2025 09:08:54 +0000 Subject: [PATCH 45/52] return early instead of nesting if/else blocks --- Server/service/networkService.js | 91 ++++++++++++++++---------------- 1 file changed, 46 insertions(+), 45 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 3ceeef743..3a2bddb8a 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -151,54 +151,55 @@ class NetworkService { httpResponse.code = response.status; - if (expectedValue) { - // validate if response data match expected value - let result = response?.data; - - this.logger.info({ - service: this.SERVICE_NAME, - method: "requestHttp", - message: `Job: [${name}](${_id}) match result with expected value`, - details: { expectedValue, result, jsonPath, matchMethod } - }); - - if (jsonPath) { - const contentType = response.headers['content-type']; - - if (contentType && contentType.includes('application/json')) { - try { - result = jmespath.search(result, jsonPath); - } catch (error) { - httpResponse.status = false; - httpResponse.message = "JSONPath Search Error"; - return httpResponse; - } - } else { - httpResponse.status = false; - httpResponse.message = "Response Not JSON"; - return httpResponse; - } - } - - if (result === null || result === undefined) { - httpResponse.status = false; - httpResponse.message = "Empty Result"; - return httpResponse; - } - - let match; - result = typeof result === "object" ? JSON.stringify(result) : result.toString(); - if (matchMethod === "include") match = result.includes(expectedValue); - else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result); - else match = result === expectedValue; - - httpResponse.status = match; - httpResponse.message = match ? "Match" : "Not Match"; + if (!expectedValue) { + // not configure expected value, return + httpResponse.status = true; + httpResponse.message = this.http.STATUS_CODES[response.status]; return httpResponse; } - httpResponse.status = true; - httpResponse.message = this.http.STATUS_CODES[response.status]; + // validate if response data match expected value + let result = response?.data; + + this.logger.info({ + service: this.SERVICE_NAME, + method: "requestHttp", + message: `Job: [${name}](${_id}) match result with expected value`, + details: { expectedValue, result, jsonPath, matchMethod } + }); + + if (jsonPath) { + const contentType = response.headers['content-type']; + + if (contentType && contentType.includes('application/json')) { + try { + result = jmespath.search(result, jsonPath); + } catch (error) { + httpResponse.status = false; + httpResponse.message = "JSONPath Search Error"; + return httpResponse; + } + } else { + httpResponse.status = false; + httpResponse.message = "Response Not JSON"; + return httpResponse; + } + } + + if (result === null || result === undefined) { + httpResponse.status = false; + httpResponse.message = "Empty Result"; + return httpResponse; + } + + let match; + result = typeof result === "object" ? JSON.stringify(result) : result.toString(); + if (matchMethod === "include") match = result.includes(expectedValue); + else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result); + else match = result === expectedValue; + + httpResponse.status = match; + httpResponse.message = match ? "Match" : "Not Match"; return httpResponse; } catch (error) { error.service = this.SERVICE_NAME; From c3d92ec812a6942bbefc51e0b41dd6ebbdcb5e34 Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Wed, 12 Feb 2025 03:05:58 +0000 Subject: [PATCH 46/52] return early if not json --- Server/service/networkService.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 3a2bddb8a..741b69bef 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -1,5 +1,4 @@ import jmespath from 'jmespath'; -import { errorMessages, successMessages } from "../utils/messages.js"; const SERVICE_NAME = "NetworkService"; const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push"; @@ -171,19 +170,20 @@ class NetworkService { if (jsonPath) { const contentType = response.headers['content-type']; - if (contentType && contentType.includes('application/json')) { - try { - result = jmespath.search(result, jsonPath); - } catch (error) { - httpResponse.status = false; - httpResponse.message = "JSONPath Search Error"; - return httpResponse; - } - } else { + const isJson = contentType?.includes('application/json'); + if (!isJson) { httpResponse.status = false; httpResponse.message = "Response Not JSON"; return httpResponse; } + + try { + result = jmespath.search(result, jsonPath); + } catch (error) { + httpResponse.status = false; + httpResponse.message = "JSONPath Search Error"; + return httpResponse; + } } if (result === null || result === undefined) { From 6837adb5aa3e8fd9dbf84f3b94759caf983a76f1 Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Wed, 12 Feb 2025 03:12:33 +0000 Subject: [PATCH 47/52] add locales for http request --- Server/locales/en.json | 8 +++++++- Server/service/stringService.js | 24 ++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/Server/locales/en.json b/Server/locales/en.json index ba50e1d40..8657deb73 100644 --- a/Server/locales/en.json +++ b/Server/locales/en.json @@ -144,5 +144,11 @@ "monitorResume": "Monitor resumed successfully", "statusPageDelete": "Status page deleted successfully", "statusPageUpdate": "Status page updated successfully", - "statusPageByTeamId": "Got status pages by team id successfully" + "statusPageByTeamId": "Got status pages by team id successfully", + "httpNetworkError": "Network error", + "httpNotJson": "Response data is not json", + "httpJsonPathError": "Failed to parse json data", + "httpEmptyResult": "Result is empty", + "httpMatchSuccess": "Response data match successfully", + "httpMatchFail": "Failed to match response data" } \ No newline at end of file diff --git a/Server/service/stringService.js b/Server/service/stringService.js index a65e1655b..1ef208d6c 100644 --- a/Server/service/stringService.js +++ b/Server/service/stringService.js @@ -321,6 +321,30 @@ class StringService { return this.translationService.getTranslation('getAppSettings'); } + get httpNetworkError() { + return this.translationService.getTranslation('httpNetworkError'); + } + + get httpNotJson() { + return this.translationService.getTranslation('httpNotJson'); + } + + get httpJsonPathError() { + return this.translationService.getTranslation('httpJsonPathError'); + } + + get httpEmptyResult() { + return this.translationService.getTranslation('httpEmptyResult'); + } + + get httpMatchSuccess() { + return this.translationService.getTranslation('httpMatchSuccess'); + } + + get httpMatchFail() { + return this.translationService.getTranslation('httpMatchFail'); + } + get updateAppSettings() { return this.translationService.getTranslation('updateAppSettings'); } From 1f0876dd7198ec59e9f5dadf310e56a1c84395bd Mon Sep 17 00:00:00 2001 From: dongfang <1136005348@qq.com> Date: Wed, 12 Feb 2025 03:16:07 +0000 Subject: [PATCH 48/52] use stringService in requestHttp --- Server/service/networkService.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Server/service/networkService.js b/Server/service/networkService.js index 741b69bef..89dfe85d1 100644 --- a/Server/service/networkService.js +++ b/Server/service/networkService.js @@ -144,7 +144,7 @@ class NetworkService { const code = error.response?.status || this.NETWORK_ERROR; httpResponse.code = code; httpResponse.status = false; - httpResponse.message = this.http.STATUS_CODES[code] || "Network Error"; + httpResponse.message = this.http.STATUS_CODES[code] || this.stringService.httpNetworkError; return httpResponse; } @@ -173,7 +173,7 @@ class NetworkService { const isJson = contentType?.includes('application/json'); if (!isJson) { httpResponse.status = false; - httpResponse.message = "Response Not JSON"; + httpResponse.message = this.stringService.httpNotJson; return httpResponse; } @@ -181,14 +181,14 @@ class NetworkService { result = jmespath.search(result, jsonPath); } catch (error) { httpResponse.status = false; - httpResponse.message = "JSONPath Search Error"; + httpResponse.message = this.stringService.httpJsonPathError; return httpResponse; } } if (result === null || result === undefined) { httpResponse.status = false; - httpResponse.message = "Empty Result"; + httpResponse.message = this.stringService.httpEmptyResult; return httpResponse; } @@ -199,7 +199,7 @@ class NetworkService { else match = result === expectedValue; httpResponse.status = match; - httpResponse.message = match ? "Match" : "Not Match"; + httpResponse.message = match ? this.stringService.httpMatchSuccess : this.stringService.httpMatchFail; return httpResponse; } catch (error) { error.service = this.SERVICE_NAME; From 34e74d2f34cf42514511127817fe55c2acd11bf0 Mon Sep 17 00:00:00 2001 From: Alex Holliday Date: Tue, 11 Feb 2025 22:25:58 -0800 Subject: [PATCH 49/52] fix placeholder --- Client/src/Pages/DistributedUptimeStatus/Status/index.jsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Client/src/Pages/DistributedUptimeStatus/Status/index.jsx b/Client/src/Pages/DistributedUptimeStatus/Status/index.jsx index 89f97a4d0..ef77b3032 100644 --- a/Client/src/Pages/DistributedUptimeStatus/Status/index.jsx +++ b/Client/src/Pages/DistributedUptimeStatus/Status/index.jsx @@ -75,9 +75,9 @@ const DistributedUptimeStatus = () => { marginY={theme.spacing(4)} color={theme.palette.primary.contrastTextTertiary} > - A public status page is not set up. + A status page is not set up. - Please contact to your administrator + Please contact your administrator ); From 70fc3756b5f268d786ff61cb65e5494a226e2132 Mon Sep 17 00:00:00 2001 From: Aryaman Kumar Sharma Date: Wed, 12 Feb 2025 13:15:32 +0530 Subject: [PATCH 50/52] Fix: Average Resposne Time Guage Chart UI --- .DS_Store | Bin 0 -> 6148 bytes .../Components/Charts/ResponseGaugeChart.jsx | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d4b59627dbbc66f603800051340a59ab1897eb93 GIT binary patch literal 6148 zcmeHKNlpVX5G+F!HnE5k7o>Rs5$+d|{X zAaOznbw_r2JY9BoS!2flT;s8`49o!-XA!JTvZ^q7FJ;I^LeC7*=@>mcY;^l)r``VP zkhgW10;a%UQ$W7mI#$ue70wZVzp^Q;uq=J3Y3TFx-9{5F91+)8%X6DO2W(|n!Yw+5 zJwV2ID~&N5I2rb^fd-iwv5oaAwp%Q*>zu2PGu$xV`*Er!RjSs;K3cK2W?1x|#SS&uKlW)Ty=+@n%hh2vNoauhGHsPSDQ4PqKF_sAZa{Si { {responseTime} ms From bcb1f250af0dbc3e8be75a906ff2fab72cbb53f4 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 12 Feb 2025 15:36:48 +0000 Subject: [PATCH 51/52] fix(deps): update dependency bullmq to v5.40.3 --- Server/package-lock.json | 8 ++++---- Server/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Server/package-lock.json b/Server/package-lock.json index cb6cfc11b..cb4ca7886 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -11,7 +11,7 @@ "dependencies": { "axios": "^1.7.2", "bcrypt": "5.1.1", - "bullmq": "5.40.2", + "bullmq": "5.40.3", "cors": "^2.8.5", "dockerode": "4.0.4", "dotenv": "^16.4.5", @@ -1749,9 +1749,9 @@ } }, "node_modules/bullmq": { - "version": "5.40.2", - "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.2.tgz", - "integrity": "sha512-Cn4NUpwGAF4WnuXR2kTZCTAUEUHajSCn/IqiDG9ry1kVvAwwwg1Ati3J5HN2uZjqD5PBfNDXYnsc2+0PzakDwg==", + "version": "5.40.3", + "resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.3.tgz", + "integrity": "sha512-ZHGHilK/A8wo5HHlPbwyOWMCEMRsyEmwFubEu98Xb7IyELyJCDn2hXYQVemG8dqFovkgIxXmerf4y3OHcmwmOQ==", "license": "MIT", "dependencies": { "cron-parser": "^4.9.0", diff --git a/Server/package.json b/Server/package.json index 735a12048..3020ac0e9 100644 --- a/Server/package.json +++ b/Server/package.json @@ -18,7 +18,7 @@ "dependencies": { "axios": "^1.7.2", "bcrypt": "5.1.1", - "bullmq": "5.40.2", + "bullmq": "5.40.3", "cors": "^2.8.5", "dockerode": "4.0.4", "dotenv": "^16.4.5", From 5fb0204b4bb983fe11854132f0f4cdbe9c9c626b Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Wed, 12 Feb 2025 17:49:14 +0000 Subject: [PATCH 52/52] chore(deps): update dependency globals to v15.15.0 --- Server/package-lock.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/Server/package-lock.json b/Server/package-lock.json index cb4ca7886..76cb601a3 100644 --- a/Server/package-lock.json +++ b/Server/package-lock.json @@ -3869,10 +3869,11 @@ } }, "node_modules/globals": { - "version": "15.14.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz", - "integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==", + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" },