diff --git a/README.md b/README.md index 757e70493..4be16fef0 100644 --- a/README.md +++ b/README.md @@ -72,14 +72,20 @@ You can see the memory footprint of MongoDB and Redis on the same server (398Mb ## 💚 Questions & Ideas -If you have any questions, suggestions or comments, please use our [Discord channel](https://discord.gg/NAb6H3UTjK). We've also launched our [Discussions](https://github.com/bluewave-labs/bluewave-uptime/discussions) page! Feel free to ask questions or share your ideas—we'd love to hear from you! +If you have any questions, suggestions or comments, you have several options: + +- [Discord channel](https://discord.gg/NAb6H3UTjK) +- [GitHub Discussions](https://github.com/bluewave-labs/bluewave-uptime/discussions) +- [Reddit group](https://www.reddit.com/r/CheckmateMonitoring/) + +Feel free to ask questions or share your ideas - we'd love to hear from you! ## 🧩 Features - Completely open source, deployable on your servers or home devices (e.g Raspberry Pi 4 or 5) - Website monitoring - Page speed monitoring -- Infrastructure monitoring (memory, disk usage, CPU performance etc) - requires [Capture](https://github.com/bluewave-labs/capture) +- Infrastructure monitoring (memory, disk usage, CPU performance etc) - requires [Capture](https://github.com/bluewave-labs/capture) agent - Docker monitoring - Ping monitoring - SSL monitoring @@ -134,7 +140,7 @@ If you have any questions, suggestions or comments, please use our [Discord chan We are [Alex](http://github.com/ajhollid) (team lead), [Vishnu](http://github.com/vishnusn77), [Mohadeseh](http://github.com/mohicody), [Gorkem](http://github.com/gorkem-bwl/), [Owaise](http://github.com/Owaiseimdad), [Aryaman](https://github.com/Br0wnHammer) and [Mert](https://github.com/mertssmnoglu) helping individuals and businesses monitor their infra and servers. -We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 6000+ stars and attracted 80+ contributors from around the globe. +We pride ourselves on building strong connections with contributors at every level. Despite being a young project, Checkmate has already earned 7000+ stars and attracted 90+ contributors from around the globe. Our repo is starred by employees from **Google, Microsoft, Intel, Cisco, Tencent, Electronic Arts, ByteDance, JP Morgan Chase, Deloitte, Accenture, Foxconn, Broadcom, China Telecom, Barclays, Capgemini, Wipro, Cloudflare, Dassault Systèmes and NEC**, so don’t hold back — jump in, contribute and learn with us! diff --git a/client/src/Components/Toast/body.jsx b/client/src/Components/Toast/body.jsx new file mode 100644 index 000000000..04d5aee99 --- /dev/null +++ b/client/src/Components/Toast/body.jsx @@ -0,0 +1,34 @@ +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; + +import { useTheme } from "@emotion/react"; +import PropTypes from "prop-types"; + +const ToastBody = ({ body }) => { + const theme = useTheme(); + + if (Array.isArray(body)) { + return ( + + {body.map((item, idx) => ( + + {item} + + ))} + + ); + } else if (typeof body === "string") { + return {body}; + } + + return null; +}; + +ToastBody.propTypes = { + body: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), +}; + +export default ToastBody; diff --git a/client/src/Components/Toast/index.jsx b/client/src/Components/Toast/index.jsx new file mode 100644 index 000000000..36df0e631 --- /dev/null +++ b/client/src/Components/Toast/index.jsx @@ -0,0 +1,96 @@ +import Stack from "@mui/material/Stack"; +import IconButton from "@mui/material/IconButton"; +import Typography from "@mui/material/Typography"; +import Button from "@mui/material/Button"; +import ToastBody from "./body"; +import InfoOutlinedIcon from "@mui/icons-material/InfoOutlined"; +import ErrorOutlineOutlinedIcon from "@mui/icons-material/ErrorOutlineOutlined"; +import WarningAmberOutlinedIcon from "@mui/icons-material/WarningAmberOutlined"; +import CloseIcon from "@mui/icons-material/Close"; + +// Utils +import { useTheme } from "@emotion/react"; +import PropTypes from "prop-types"; + +const icons = { + info: , + error: , + warning: , +}; + +const Toast = ({ variant, title, body, onClick, hasDismiss, hasIcon }) => { + const theme = useTheme(); + const icon = icons[variant]; + + return ( + + + {hasIcon && icon} + {title && ( + + {title} + + )} + {title && ( + + + + )} + + + + + {!title && ( + + + + )} + + {hasDismiss && ( + + )} + + ); +}; + +export default Toast; + +Toast.propTypes = { + variant: PropTypes.string.isRequired, + title: PropTypes.string, + body: PropTypes.oneOfType([PropTypes.string, PropTypes.array]), + hasDismiss: PropTypes.bool, + hasIcon: PropTypes.bool, + onClick: PropTypes.func, +}; diff --git a/client/src/Features/Auth/authSlice.js b/client/src/Features/Auth/authSlice.js index 2fde49202..5ee31944a 100644 --- a/client/src/Features/Auth/authSlice.js +++ b/client/src/Features/Auth/authSlice.js @@ -44,7 +44,6 @@ export const login = createAsyncThunk("auth/login", async (form, thunkApi) => { export const update = createAsyncThunk("auth/update", async (data, thunkApi) => { const { localData: form } = data; - const user = thunkApi.getState().auth.user; try { const fd = new FormData(); form.firstName && fd.append("firstName", form.firstName); @@ -60,7 +59,6 @@ export const update = createAsyncThunk("auth/update", async (data, thunkApi) => form.deleteProfileImage && fd.append("deleteProfileImage", form.deleteProfileImage); const res = await networkService.updateUser({ - userId: user._id, form: fd, }); return res.data; @@ -77,10 +75,8 @@ export const update = createAsyncThunk("auth/update", async (data, thunkApi) => }); export const deleteUser = createAsyncThunk("auth/delete", async (_, thunkApi) => { - const user = thunkApi.getState().auth.user; - try { - const res = await networkService.deleteUser({ userId: user._id }); + const res = await networkService.deleteUser(); return res.data; } catch (error) { if (error.response && error.response.data) { diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js index 895d90d30..54bb239a4 100644 --- a/client/src/Utils/NetworkService.js +++ b/client/src/Utils/NetworkService.js @@ -393,7 +393,7 @@ class NetworkService { * */ async updateUser(config) { - return this.axiosInstance.put(`/auth/user/${config.userId}`, config.form); + return this.axiosInstance.put(`/auth/user`, config.form); } /** @@ -406,8 +406,8 @@ class NetworkService { * @param {string} config.userId - The ID of the user to be deleted. * **/ - async deleteUser(config) { - return this.axiosInstance.delete(`/auth/user/${config.userId}`); + async deleteUser() { + return this.axiosInstance.delete(`/auth/user`); } /** diff --git a/client/src/Utils/toastUtils.jsx b/client/src/Utils/toastUtils.jsx index dbec9117e..21cf13e2b 100644 --- a/client/src/Utils/toastUtils.jsx +++ b/client/src/Utils/toastUtils.jsx @@ -1,7 +1,6 @@ import PropTypes from "prop-types"; import { toast, Slide } from "react-toastify"; -import Alert from "../Components/Alert"; - +import Toast from "../Components/Toast"; /** * @param {object} props * @param {'info' | 'error' | 'warning'} - The variant of the alert (e.g., "info", "error"). @@ -15,6 +14,7 @@ export const createToast = ({ variant = "info", title, body, + hasDismiss = false, hasIcon = false, config = {}, }) => { @@ -29,13 +29,14 @@ export const createToast = ({ toast( ({ closeToast }) => ( - ), toastConfig @@ -47,5 +48,6 @@ createToast.propTypes = { title: PropTypes.string, body: PropTypes.string.isRequired, hasIcon: PropTypes.bool, + hasDismiss: PropTypes.bool, config: PropTypes.object, }; diff --git a/server/.prettierrc b/server/.prettierrc index 5c345e37a..681669aa7 100755 --- a/server/.prettierrc +++ b/server/.prettierrc @@ -1,5 +1,5 @@ { - "printWidth": 90, + "printWidth": 150, "useTabs": true, "tabWidth": 2, "singleQuote": false, diff --git a/server/controllers/authController.js b/server/controllers/authController.js index e4a682fec..1cc1a747a 100755 --- a/server/controllers/authController.js +++ b/server/controllers/authController.js @@ -1,7 +1,6 @@ import { registrationBodyValidation, loginValidation, - editUserParamValidation, editUserBodyValidation, recoveryValidation, recoveryTokenValidation, @@ -91,16 +90,14 @@ class AuthController { const html = await this.emailService.buildEmail("welcomeEmailTemplate", { name: newUser.firstName, }); - this.emailService - .sendEmail(newUser.email, "Welcome to Uptime Monitor", html) - .catch((error) => { - this.logger.warn({ - message: error.message, - service: SERVICE_NAME, - method: "registerUser", - stack: error.stack, - }); + this.emailService.sendEmail(newUser.email, "Welcome to Uptime Monitor", html).catch((error) => { + this.logger.warn({ + message: error.message, + service: SERVICE_NAME, + method: "registerUser", + stack: error.stack, }); + }); } catch (error) { this.logger.warn({ message: error.message, @@ -188,14 +185,8 @@ class AuthController { */ editUser = asyncHandler( async (req, res, next) => { - await editUserParamValidation.validateAsync(req.params); await editUserBodyValidation.validateAsync(req.body); - // TODO is this neccessary any longer? Verify ownership middleware should handle this - if (req.params.userId !== req.user._id.toString()) { - throw createAuthError(this.stringService.unauthorized); - } - // Change Password check if (req.body.password && req.body.newPassword) { // Get token from headers @@ -218,7 +209,7 @@ class AuthController { req.body.password = req.body.newPassword; } - const updatedUser = await this.db.updateUser(req, res); + const updatedUser = await this.db.updateUser({ userId: req?.user?._id, user: req.body, file: req.file }); res.success({ msg: this.stringService.authUpdateUser, data: updatedUser, @@ -274,11 +265,7 @@ class AuthController { email, url, }); - const msgId = await this.emailService.sendEmail( - email, - "Checkmate Password Reset", - html - ); + const msgId = await this.emailService.sendEmail(email, "Checkmate Password Reset", html); return res.success({ msg: this.stringService.authCreateRecoveryToken, @@ -349,20 +336,31 @@ class AuthController { */ deleteUser = asyncHandler( async (req, res, next) => { - const token = getTokenFromHeaders(req.headers); - const decodedToken = jwt.decode(token); - const { email } = decodedToken; - - // Check if the user exists - const user = await this.db.getUserByEmail(email); + const email = req?.user?.email; + if (!email) { + throw new Error("No email in request"); + } // 1. Find all the monitors associated with the team ID if superadmin + const teamId = req?.user?.teamId; + const userId = req?.user?._id; + + if (!teamId) { + throw new Error("No team ID in request"); + } + + if (!userId) { + throw new Error("No user ID in request"); + } + + const roles = req.user.role; + const result = await this.db.getMonitorsByTeamId({ - teamId: user.teamId.toString(), + teamId: teamId, }); - if (user.role.includes("superadmin")) { - //2. Remove all jobs, delete checks and alerts + if (roles.includes("superadmin")) { + // 2. Remove all jobs, delete checks and alerts result?.monitors.length > 0 && (await Promise.all( result.monitors.map(async (monitor) => { @@ -371,7 +369,7 @@ class AuthController { )); } // 6. Delete the user by id - await this.db.deleteUser(user._id); + await this.db.deleteUser(userId); return res.success({ msg: this.stringService.authDeleteUser, diff --git a/server/controllers/checkController.js b/server/controllers/checkController.js index 2968614e2..96dabef80 100755 --- a/server/controllers/checkController.js +++ b/server/controllers/checkController.js @@ -1,6 +1,4 @@ import { - createCheckParamValidation, - createCheckBodyValidation, getChecksParamValidation, getChecksQueryValidation, getTeamChecksParamValidation, @@ -23,31 +21,41 @@ class CheckController { this.stringService = stringService; } - createCheck = asyncHandler( - async (req, res, next) => { - await createCheckParamValidation.validateAsync(req.params); - await createCheckBodyValidation.validateAsync(req.body); - - const checkData = { ...req.body }; - const check = await this.db.createCheck(checkData); - - return res.success({ - msg: this.stringService.checkCreate, - data: check, - }); - }, - SERVICE_NAME, - "createCheck" - ); - getChecksByMonitor = asyncHandler( async (req, res, next) => { await getChecksParamValidation.validateAsync(req.params); await getChecksQueryValidation.validateAsync(req.query); - const { monitorId } = req.params; - let { type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status } = - req.query; + const monitorId = req?.params?.monitorId; + + if (!monitorId) { + throw new Error("No monitor ID in request"); + } + + const teamId = req?.user?.teamId; + if (!teamId) { + throw new Error("No team ID in request"); + } + + const monitor = await this.db.getMonitorById(monitorId); + + if (!monitor) { + const err = new Error("Monitor not found"); + err.status = 404; + err.service = SERVICE_NAME; + err.method = "getChecksByMonitor"; + throw err; + } + + if (!monitor.teamId.equals(teamId)) { + const err = new Error("Unauthorized"); + err.status = 403; + err.service = SERVICE_NAME; + err.method = "getChecksByMonitor"; + throw err; + } + + let { type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status } = req.query; const result = await this.db.getChecksByMonitor({ monitorId, type, @@ -75,7 +83,11 @@ class CheckController { await getTeamChecksQueryValidation.validateAsync(req.query); let { sortOrder, dateRange, filter, ack, page, rowsPerPage } = req.query; - const { teamId } = req.user; + const teamId = req?.user?.teamId; + + if (!teamId) { + throw new Error("No team ID in request"); + } const checkData = await this.db.getChecksByTeam({ sortOrder, @@ -97,7 +109,12 @@ class CheckController { getChecksSummaryByTeamId = asyncHandler( async (req, res, next) => { - const { teamId } = req.user; + const teamId = req?.user?.teamId; + + if (!teamId) { + throw new Error("No team ID in request"); + } + const summary = await this.db.getChecksSummaryByTeamId({ teamId }); return res.success({ msg: this.stringService.checkGetSummary, @@ -112,9 +129,17 @@ class CheckController { async (req, res, next) => { await ackCheckBodyValidation.validateAsync(req.body); - const { checkId } = req.params; - const { ack } = req.body; - const { teamId } = req.user; + const checkId = req?.params?.checkId; + const ack = req?.body?.ack; + const teamId = req?.user?.teamId; + + if (!checkId) { + throw new Error("No check ID in request"); + } + + if (!teamId) { + throw new Error("No team ID in request"); + } const updatedCheck = await this.db.ackCheck(checkId, teamId, ack); @@ -132,9 +157,35 @@ class CheckController { await ackAllChecksParamValidation.validateAsync(req.params); await ackAllChecksBodyValidation.validateAsync(req.body); - const { monitorId, path } = req.params; - const { ack } = req.body; - const { teamId } = req.user; + const teamId = req?.user?.teamId; + if (!teamId) { + throw new Error("No team ID in request"); + } + + const monitorId = req?.params?.monitorId; + + const path = req?.params?.path; + + const ack = req?.body?.ack; + + if (path === "monitor") { + if (!monitorId) { + throw new Error("No monitor ID in request"); + } + + const monitor = await this.db.getMonitorById(monitorId); + if (!monitor) { + throw new Error("Monitor not found"); + } + + if (!monitor.teamId.equals(teamId)) { + const err = new Error("Unauthorized"); + err.status = 403; + err.service = SERVICE_NAME; + err.method = "ackAllChecks"; + throw err; + } + } const updatedChecks = await this.db.ackAllChecks(monitorId, teamId, ack, path); @@ -151,6 +202,35 @@ class CheckController { async (req, res, next) => { await deleteChecksParamValidation.validateAsync(req.params); + const monitorId = req?.params?.monitorId; + const teamId = req?.user?.teamId; + + if (!monitorId) { + throw new Error("No monitor ID in request"); + } + + if (!teamId) { + throw new Error("No team ID in request"); + } + + const monitor = await this.db.getMonitorById(monitorId); + + if (!monitor) { + const err = new Error("Monitor not found"); + err.status = 404; + err.service = SERVICE_NAME; + err.method = "deleteChecks"; + throw err; + } + + if (!monitor.teamId.equals(teamId)) { + const err = new Error("Unauthorized"); + err.status = 403; + err.service = SERVICE_NAME; + err.method = "deleteChecks"; + throw err; + } + const deletedCount = await this.db.deleteChecks(req.params.monitorId); return res.success({ @@ -166,7 +246,11 @@ class CheckController { async (req, res, next) => { await deleteChecksByTeamIdParamValidation.validateAsync(req.params); - const { teamId } = req.user; + const teamId = req?.user?.teamId; + if (!teamId) { + throw new Error("No team ID in request"); + } + const deletedCount = await this.db.deleteChecksByTeamId(teamId); return res.success({ diff --git a/server/controllers/diagnosticController.js b/server/controllers/diagnosticController.js index a350a452b..6ba880dcb 100755 --- a/server/controllers/diagnosticController.js +++ b/server/controllers/diagnosticController.js @@ -12,63 +12,20 @@ obs.observe({ entryTypes: ["measure"] }); class DiagnosticController { constructor(db) { this.db = db; - this.getMonitorsByTeamIdExecutionStats = - this.getMonitorsByTeamIdExecutionStats.bind(this); - this.getDbStats = this.getDbStats.bind(this); } - getMonitorsByTeamIdExecutionStats = asyncHandler( - async (req, res, next) => { - const data = await this.db.getMonitorsByTeamIdExecutionStats(req); - return res.success({ - msg: "OK", - data, - }); - }, - SERVICE_NAME, - "getMonitorsByTeamIdExecutionStats" - ); - - getDbStats = asyncHandler( - async (req, res, next) => { - const { methodName, args = [] } = req.body; - if (!methodName || !this.db[methodName]) { - return res.error({ - msg: "Invalid method name or method doesn't exist", - status: 400, - }); - } - const explainMethod = await this.db[methodName].apply(this.db, args); - const stats = { - methodName, - timestamp: new Date(), - explain: explainMethod, - }; - return res.success({ - msg: "OK", - data: stats, - }); - }, - SERVICE_NAME, - "getDbStats" - ); - - getCPUUsage = asyncHandler( - async (req, res, next) => { - const startUsage = process.cpuUsage(); - const timingPeriod = 1000; // measured in ms - await new Promise((resolve) => setTimeout(resolve, timingPeriod)); - const endUsage = process.cpuUsage(startUsage); - const cpuUsage = { - userUsageMs: endUsage.user / 1000, - systemUsageMs: endUsage.system / 1000, - usagePercentage: ((endUsage.user + endUsage.system) / 1000 / timingPeriod) * 100, - }; - return cpuUsage; - }, - SERVICE_NAME, - "getCPUUsage" - ); + getCPUUsage = async () => { + const startUsage = process.cpuUsage(); + const timingPeriod = 1000; // measured in ms + await new Promise((resolve) => setTimeout(resolve, timingPeriod)); + const endUsage = process.cpuUsage(startUsage); + const cpuUsage = { + userUsageMs: endUsage.user / 1000, + systemUsageMs: endUsage.system / 1000, + usagePercentage: ((endUsage.user + endUsage.system) / 1000 / timingPeriod) * 100, + }; + return cpuUsage; + }; getSystemStats = asyncHandler( async (req, res, next) => { diff --git a/server/controllers/inviteController.js b/server/controllers/inviteController.js index abb6b8123..f7f33c5d3 100755 --- a/server/controllers/inviteController.js +++ b/server/controllers/inviteController.js @@ -1,8 +1,4 @@ -import { - inviteRoleValidation, - inviteBodyValidation, - inviteVerificationBodyValidation, -} from "../validation/joi.js"; +import { inviteRoleValidation, inviteBodyValidation, inviteVerificationBodyValidation } from "../validation/joi.js"; import jwt from "jsonwebtoken"; import { getTokenFromHeaders } from "../utils/utils.js"; import { asyncHandler, createServerError } from "../utils/errorUtils.js"; @@ -65,15 +61,9 @@ class InviteController { name: firstname, link: `${clientHost}/register/${inviteToken.token}`, }); - const result = await this.emailService.sendEmail( - req.body.email, - "Welcome to Uptime Monitor", - html - ); + const result = await this.emailService.sendEmail(req.body.email, "Welcome to Uptime Monitor", html); if (!result) { - throw createServerError( - "Failed to send invite e-mail... Please verify your settings." - ); + throw createServerError("Failed to send invite e-mail... Please verify your settings."); } return res.success({ diff --git a/server/controllers/maintenanceWindowController.js b/server/controllers/maintenanceWindowController.js index 8a19fc79d..9807392cc 100755 --- a/server/controllers/maintenanceWindowController.js +++ b/server/controllers/maintenanceWindowController.js @@ -22,8 +22,25 @@ class MaintenanceWindowController { async (req, res, next) => { await createMaintenanceWindowBodyValidation.validateAsync(req.body); - const { teamId } = req.user; + const teamId = req?.user?.teamId; + if (!teamId) { + throw new Error("Team ID is required"); + } + const monitorIds = req.body.monitors; + + const monitors = await this.db.getMonitorsByIds(monitorIds); + + const unauthorizedMonitors = monitors.filter((monitor) => !monitor.teamId.equals(teamId)); + + if (unauthorizedMonitors.length > 0) { + const error = new Error("Unauthorized access to one or more monitors"); + error.status = 403; + error.service = SERVICE_NAME; + error.method = "createMaintenanceWindows"; + throw error; + } + const dbTransactions = monitorIds.map((monitorId) => { return this.db.createMaintenanceWindow({ teamId, @@ -48,7 +65,13 @@ class MaintenanceWindowController { getMaintenanceWindowById = asyncHandler( async (req, res, next) => { await getMaintenanceWindowByIdParamValidation.validateAsync(req.params); - const maintenanceWindow = await this.db.getMaintenanceWindowById(req.params.id); + + const teamId = req.user.teamId; + if (!teamId) { + throw new Error("Team ID is required"); + } + + const maintenanceWindow = await this.db.getMaintenanceWindowById({ id: req.params.id, teamId }); return res.success({ msg: this.stringService.maintenanceWindowGetById, data: maintenanceWindow, @@ -62,11 +85,13 @@ class MaintenanceWindowController { async (req, res, next) => { await getMaintenanceWindowsByTeamIdQueryValidation.validateAsync(req.query); - const { teamId } = req.user; - const maintenanceWindows = await this.db.getMaintenanceWindowsByTeamId( - teamId, - req.query - ); + const teamId = req?.user?.teamId; + + if (!teamId) { + throw new Error("Team ID is required"); + } + + const maintenanceWindows = await this.db.getMaintenanceWindowsByTeamId(teamId, req.query); return res.success({ msg: this.stringService.maintenanceWindowGetByTeam, @@ -81,9 +106,14 @@ class MaintenanceWindowController { async (req, res, next) => { await getMaintenanceWindowsByMonitorIdParamValidation.validateAsync(req.params); - const maintenanceWindows = await this.db.getMaintenanceWindowsByMonitorId( - req.params.monitorId - ); + const monitorId = req.params.monitorId; + + const teamId = req?.user?.teamId; + if (!teamId) { + throw new Error("Team ID is required"); + } + + const maintenanceWindows = await this.db.getMaintenanceWindowsByMonitorId({ monitorId, teamId }); return res.success({ msg: this.stringService.maintenanceWindowGetByUser, @@ -97,7 +127,13 @@ class MaintenanceWindowController { deleteMaintenanceWindow = asyncHandler( async (req, res, next) => { await deleteMaintenanceWindowByIdParamValidation.validateAsync(req.params); - await this.db.deleteMaintenanceWindowById(req.params.id); + + const teamId = req?.user?.teamId; + if (!teamId) { + throw new Error("Team ID is required"); + } + + await this.db.deleteMaintenanceWindowById({ id: req.params.id, teamId }); return res.success({ msg: this.stringService.maintenanceWindowDelete, }); @@ -110,10 +146,14 @@ class MaintenanceWindowController { async (req, res, next) => { await editMaintenanceWindowByIdParamValidation.validateAsync(req.params); await editMaintenanceByIdWindowBodyValidation.validateAsync(req.body); - const editedMaintenanceWindow = await this.db.editMaintenanceWindowById( - req.params.id, - req.body - ); + + const teamId = req.user.teamId; + if (!teamId) { + throw new Error("Team ID is required"); + } + + const editedMaintenanceWindow = await this.db.editMaintenanceWindowById({ id: req.params.id, body: req.body, teamId }); + return res.success({ msg: this.stringService.maintenanceWindowEdit, data: editedMaintenanceWindow, diff --git a/server/controllers/monitorController.js b/server/controllers/monitorController.js index f80fa29e5..79579e521 100755 --- a/server/controllers/monitorController.js +++ b/server/controllers/monitorController.js @@ -463,14 +463,10 @@ class MonitorController { const monitorId = req.params.monitorId; const monitor = await this.db.pauseMonitor({ monitorId }); - monitor.isActive === true - ? await this.jobQueue.resumeJob(monitor._id, monitor) - : await this.jobQueue.pauseJob(monitor); + monitor.isActive === true ? await this.jobQueue.resumeJob(monitor._id, monitor) : await this.jobQueue.pauseJob(monitor); return res.success({ - msg: monitor.isActive - ? this.stringService.monitorResume - : this.stringService.monitorPause, + msg: monitor.isActive ? this.stringService.monitorResume : this.stringService.monitorPause, data: monitor, }); }, @@ -493,9 +489,7 @@ class MonitorController { async (req, res, next) => { const { _id, teamId } = req.user; const demoMonitors = await this.db.addDemoMonitors(_id, teamId); - await Promise.all( - demoMonitors.map((monitor) => this.jobQueue.addJob(monitor._id, monitor)) - ); + await Promise.all(demoMonitors.map((monitor) => this.jobQueue.addJob(monitor._id, monitor))); return res.success({ msg: this.stringService.monitorDemoAdded, diff --git a/server/controllers/statusPageController.js b/server/controllers/statusPageController.js index 1d3dd96fb..8333e8949 100755 --- a/server/controllers/statusPageController.js +++ b/server/controllers/statusPageController.js @@ -1,9 +1,4 @@ -import { - createStatusPageBodyValidation, - getStatusPageParamValidation, - getStatusPageQueryValidation, - imageValidation, -} from "../validation/joi.js"; +import { createStatusPageBodyValidation, getStatusPageParamValidation, getStatusPageQueryValidation, imageValidation } from "../validation/joi.js"; import { asyncHandler } from "../utils/errorUtils.js"; const SERVICE_NAME = "statusPageController"; diff --git a/server/db/models/Monitor.js b/server/db/models/Monitor.js index c71ea5f18..e8e19ac29 100755 --- a/server/db/models/Monitor.js +++ b/server/db/models/Monitor.js @@ -156,10 +156,7 @@ MonitorSchema.pre("deleteMany", async function (next) { } else { await Check.deleteMany({ monitorId: monitor._id }); } - await StatusPage.updateMany( - { monitors: monitor._id }, - { $pull: { monitors: monitor._id } } - ); + await StatusPage.updateMany({ monitors: monitor._id }, { $pull: { monitors: monitor._id } }); await MonitorStats.deleteMany({ monitorId: monitor._id.toString() }); } next(); diff --git a/server/db/mongo/MongoDB.js b/server/db/mongo/MongoDB.js index 5e5e7aec7..6f6a13d64 100755 --- a/server/db/mongo/MongoDB.js +++ b/server/db/mongo/MongoDB.js @@ -89,8 +89,7 @@ class MongoDB { connect = async () => { try { - const connectionString = - this.appSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db"; + const connectionString = this.appSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db"; await mongoose.connect(connectionString); // If there are no AppSettings, create one await AppSettings.findOneAndUpdate( diff --git a/server/db/mongo/modules/checkModule.js b/server/db/mongo/modules/checkModule.js index fb0cc539e..731d168bf 100755 --- a/server/db/mongo/modules/checkModule.js +++ b/server/db/mongo/modules/checkModule.js @@ -58,26 +58,13 @@ const createChecks = async (checks) => { * @returns {Promise>} * @throws {Error} */ -const getChecksByMonitor = async ({ - monitorId, - type, - sortOrder, - dateRange, - filter, - ack, - page, - rowsPerPage, - status, -}) => { +const getChecksByMonitor = async ({ monitorId, type, sortOrder, dateRange, filter, ack, page, rowsPerPage, status }) => { try { status = status === "true" ? true : status === "false" ? false : undefined; page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); - const ackStage = - ack === "true" - ? { ack: true } - : { $or: [{ ack: false }, { ack: { $exists: false } }] }; + const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] }; // Match const matchStage = { @@ -158,23 +145,12 @@ const getChecksByMonitor = async ({ } }; -const getChecksByTeam = async ({ - sortOrder, - dateRange, - filter, - ack, - page, - rowsPerPage, - teamId, -}) => { +const getChecksByTeam = async ({ sortOrder, dateRange, filter, ack, page, rowsPerPage, teamId }) => { try { page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); - const ackStage = - ack === "true" - ? { ack: true } - : { $or: [{ ack: false }, { ack: { $exists: false } }] }; + const ackStage = ack === "true" ? { ack: true } : { $or: [{ ack: false }, { ack: { $exists: false } }] }; const matchStage = { teamId: new ObjectId(teamId), @@ -264,11 +240,7 @@ const getChecksByTeam = async ({ */ const ackCheck = async (checkId, teamId, ack) => { try { - const updatedCheck = await Check.findOneAndUpdate( - { _id: checkId, teamId: teamId }, - { $set: { ack, ackAt: new Date() } }, - { new: true } - ); + const updatedCheck = await Check.findOneAndUpdate({ _id: checkId, teamId: teamId }, { $set: { ack, ackAt: new Date() } }, { new: true }); if (!updatedCheck) { throw new Error("Check not found"); @@ -293,10 +265,7 @@ const ackCheck = async (checkId, teamId, ack) => { */ const ackAllChecks = async (monitorId, teamId, ack, path) => { try { - const updatedChecks = await Check.updateMany( - path === "monitor" ? { monitorId } : { teamId }, - { $set: { ack, ackAt: new Date() } } - ); + const updatedChecks = await Check.updateMany(path === "monitor" ? { monitorId } : { teamId }, { $set: { ack, ackAt: new Date() } }); return updatedChecks.modifiedCount; } catch (error) { error.service = SERVICE_NAME; @@ -317,9 +286,7 @@ const getChecksSummaryByTeamId = async ({ teamId }) => { const matchStage = { teamId: new ObjectId(teamId), }; - const checks = await Check.aggregate( - buildChecksSummaryByTeamIdPipeline({ matchStage }) - ); + const checks = await Check.aggregate(buildChecksSummaryByTeamIdPipeline({ matchStage })); return checks[0].summary; } catch (error) { error.service = SERVICE_NAME; diff --git a/server/db/mongo/modules/checkModuleQueries.js b/server/db/mongo/modules/checkModuleQueries.js index ffea43ba5..f2ad2a922 100644 --- a/server/db/mongo/modules/checkModuleQueries.js +++ b/server/db/mongo/modules/checkModuleQueries.js @@ -10,20 +10,12 @@ const buildChecksSummaryByTeamIdPipeline = ({ matchStage }) => { totalChecks: { $sum: { $cond: [{ $eq: ["$status", false] }, 1, 0] } }, resolvedChecks: { $sum: { - $cond: [ - { $and: [{ $eq: ["$ack", true] }, { $eq: ["$status", false] }] }, - 1, - 0, - ], + $cond: [{ $and: [{ $eq: ["$ack", true] }, { $eq: ["$status", false] }] }, 1, 0], }, }, downChecks: { $sum: { - $cond: [ - { $and: [{ $eq: ["$ack", false] }, { $eq: ["$status", false] }] }, - 1, - 0, - ], + $cond: [{ $and: [{ $eq: ["$ack", false] }, { $eq: ["$status", false] }] }, 1, 0], }, }, cannotResolveChecks: { diff --git a/server/db/mongo/modules/diagnosticModule.js b/server/db/mongo/modules/diagnosticModule.js index e59830518..8717720a3 100755 --- a/server/db/mongo/modules/diagnosticModule.js +++ b/server/db/mongo/modules/diagnosticModule.js @@ -2,11 +2,7 @@ import Monitor from "../../models/Monitor.js"; import { ObjectId } from "mongodb"; const SERVICE_NAME = "diagnosticModule"; -import { - buildMonitorSummaryByTeamIdPipeline, - buildMonitorsByTeamIdPipeline, - buildFilteredMonitorsByTeamIdPipeline, -} from "./monitorModuleQueries.js"; +import { buildMonitorSummaryByTeamIdPipeline, buildMonitorsByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline } from "./monitorModuleQueries.js"; const getMonitorsByTeamIdExecutionStats = async (req) => { try { @@ -24,13 +20,9 @@ const getMonitorsByTeamIdExecutionStats = async (req) => { matchStage.type = Array.isArray(type) ? { $in: type } : type; } - const summary = await Monitor.aggregate( - buildMonitorSummaryByTeamIdPipeline({ matchStage }) - ).explain("executionStats"); + const summary = await Monitor.aggregate(buildMonitorSummaryByTeamIdPipeline({ matchStage })).explain("executionStats"); - const monitors = await Monitor.aggregate( - buildMonitorsByTeamIdPipeline({ matchStage, field, order }) - ).explain("executionStats"); + const monitors = await Monitor.aggregate(buildMonitorsByTeamIdPipeline({ matchStage, field, order })).explain("executionStats"); const filteredMonitors = await Monitor.aggregate( buildFilteredMonitorsByTeamIdPipeline({ diff --git a/server/db/mongo/modules/hardwareCheckModule.js b/server/db/mongo/modules/hardwareCheckModule.js index 868c83958..bfb9b0730 100755 --- a/server/db/mongo/modules/hardwareCheckModule.js +++ b/server/db/mongo/modules/hardwareCheckModule.js @@ -23,14 +23,10 @@ const createHardwareCheck = async (hardwareCheckData) => { if (monitor.uptimePercentage === undefined) { newUptimePercentage = status === true ? 1 : 0; } else { - newUptimePercentage = - (monitor.uptimePercentage * (n - 1) + (status === true ? 1 : 0)) / n; + newUptimePercentage = (monitor.uptimePercentage * (n - 1) + (status === true ? 1 : 0)) / n; } - await Monitor.findOneAndUpdate( - { _id: monitorId }, - { uptimePercentage: newUptimePercentage } - ); + await Monitor.findOneAndUpdate({ _id: monitorId }, { uptimePercentage: newUptimePercentage }); const hardwareCheck = await new HardwareCheck({ ...hardwareCheckData, diff --git a/server/db/mongo/modules/maintenanceWindowModule.js b/server/db/mongo/modules/maintenanceWindowModule.js index 959f3f961..f7fea4636 100755 --- a/server/db/mongo/modules/maintenanceWindowModule.js +++ b/server/db/mongo/modules/maintenanceWindowModule.js @@ -34,10 +34,11 @@ const createMaintenanceWindow = async (maintenanceWindowData) => { } }; -const getMaintenanceWindowById = async (maintenanceWindowId) => { +const getMaintenanceWindowById = async ({ id, teamId }) => { try { - const maintenanceWindow = await MaintenanceWindow.findById({ - _id: maintenanceWindowId, + const maintenanceWindow = await MaintenanceWindow.findOne({ + _id: id, + teamId: teamId, }); return maintenanceWindow; } catch (error) { @@ -63,8 +64,7 @@ const getMaintenanceWindowsByTeamId = async (teamId, query) => { if (active !== undefined) maintenanceQuery.active = active; - const maintenanceWindowCount = - await MaintenanceWindow.countDocuments(maintenanceQuery); + const maintenanceWindowCount = await MaintenanceWindow.countDocuments(maintenanceQuery); // Pagination let skip = 0; @@ -78,10 +78,7 @@ const getMaintenanceWindowsByTeamId = async (teamId, query) => { sort[field] = order === "asc" ? 1 : -1; } - const maintenanceWindows = await MaintenanceWindow.find(maintenanceQuery) - .skip(skip) - .limit(rowsPerPage) - .sort(sort); + const maintenanceWindows = await MaintenanceWindow.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort); return { maintenanceWindows, maintenanceWindowCount }; } catch (error) { @@ -99,10 +96,11 @@ const getMaintenanceWindowsByTeamId = async (teamId, query) => { * @returns {Promise>} An array of MaintenanceWindow documents. * @throws {Error} If there is an error retrieving the documents. */ -const getMaintenanceWindowsByMonitorId = async (monitorId) => { +const getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => { try { const maintenanceWindows = await MaintenanceWindow.find({ monitorId: monitorId, + teamId: teamId, }); return maintenanceWindows; } catch (error) { @@ -120,10 +118,9 @@ const getMaintenanceWindowsByMonitorId = async (monitorId) => { * @returns {Promise} The deleted MaintenanceWindow document. * @throws {Error} If there is an error deleting the document. */ -const deleteMaintenanceWindowById = async (maintenanceWindowId) => { +const deleteMaintenanceWindowById = async ({ id, teamId }) => { try { - const maintenanceWindow = - await MaintenanceWindow.findByIdAndDelete(maintenanceWindowId); + const maintenanceWindow = await MaintenanceWindow.findOneAndDelete({ _id: id, teamId }); return maintenanceWindow; } catch (error) { error.service = SERVICE_NAME; @@ -172,13 +169,9 @@ const deleteMaintenanceWindowByUserId = async (userId) => { } }; -const editMaintenanceWindowById = async (maintenanceWindowId, maintenanceWindowData) => { +const editMaintenanceWindowById = async ({ id, body, teamId }) => { try { - const editedMaintenanceWindow = await MaintenanceWindow.findByIdAndUpdate( - maintenanceWindowId, - maintenanceWindowData, - { new: true } - ); + const editedMaintenanceWindow = await MaintenanceWindow.findByIdAndUpdate(id, body, { new: true }); return editedMaintenanceWindow; } catch (error) { error.service = SERVICE_NAME; diff --git a/server/db/mongo/modules/monitorModule.js b/server/db/mongo/modules/monitorModule.js index bc31915c2..b331bc337 100755 --- a/server/db/mongo/modules/monitorModule.js +++ b/server/db/mongo/modules/monitorModule.js @@ -285,10 +285,7 @@ const groupChecksByTime = (checks, dateRange) => { return acc; } - const time = - dateRange === "day" - ? checkDate.setMinutes(0, 0, 0) - : checkDate.toISOString().split("T")[0]; + const time = dateRange === "day" ? checkDate.setMinutes(0, 0, 0) : checkDate.toISOString().split("T")[0]; if (!acc[time]) { acc[time] = { time, checks: [] }; @@ -306,9 +303,7 @@ const groupChecksByTime = (checks, dateRange) => { const calculateGroupStats = (group) => { const totalChecks = group.checks.length; - const checksWithResponseTime = group.checks.filter( - (check) => typeof check.responseTime === "number" && !Number.isNaN(check.responseTime) - ); + const checksWithResponseTime = group.checks.filter((check) => typeof check.responseTime === "number" && !Number.isNaN(check.responseTime)); return { time: group.time, @@ -317,8 +312,7 @@ const calculateGroupStats = (group) => { totalIncidents: group.checks.filter((check) => !check.status).length, avgResponseTime: checksWithResponseTime.length > 0 - ? checksWithResponseTime.reduce((sum, check) => sum + check.responseTime, 0) / - checksWithResponseTime.length + ? checksWithResponseTime.reduce((sum, check) => sum + check.responseTime, 0) / checksWithResponseTime.length : 0, }; }; @@ -344,29 +338,15 @@ const getUptimeDetailsById = async ({ monitorId, dateRange, normalize }) => { const dateString = formatLookup[dateRange]; - const results = await Check.aggregate( - buildUptimeDetailsPipeline(monitorId, dates, dateString) - ); + const results = await Check.aggregate(buildUptimeDetailsPipeline(monitorId, dates, dateString)); const monitorData = results[0]; - monitorData.groupedUpChecks = NormalizeDataUptimeDetails( - monitorData.groupedUpChecks, - 10, - 100 - ); + monitorData.groupedUpChecks = NormalizeDataUptimeDetails(monitorData.groupedUpChecks, 10, 100); - monitorData.groupedDownChecks = NormalizeDataUptimeDetails( - monitorData.groupedDownChecks, - 10, - 100 - ); + monitorData.groupedDownChecks = NormalizeDataUptimeDetails(monitorData.groupedDownChecks, 10, 100); - const normalizedGroupChecks = NormalizeDataUptimeDetails( - monitorData.groupedChecks, - 10, - 100 - ); + const normalizedGroupChecks = NormalizeDataUptimeDetails(monitorData.groupedChecks, 10, 100); monitorData.groupedChecks = normalizedGroupChecks; const monitorStats = await MonitorStats.findOne({ monitorId }); @@ -386,14 +366,7 @@ const getUptimeDetailsById = async ({ monitorId, dateRange, normalize }) => { * @returns {Promise} * @throws {Error} */ -const getMonitorStatsById = async ({ - monitorId, - limit, - sortOrder, - dateRange, - numToDisplay, - normalize, -}) => { +const getMonitorStatsById = async ({ monitorId, limit, sortOrder, dateRange, numToDisplay, normalize }) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { // Get monitor, if we can't find it, abort with error @@ -408,12 +381,7 @@ const getMonitorStatsById = async ({ // Get Checks for monitor in date range requested const model = CHECK_MODEL_LOOKUP[monitor.type]; const dates = getDateRange(dateRange); - const { checksAll, checksForDateRange } = await getMonitorChecks( - monitorId, - model, - dates, - sort - ); + const { checksAll, checksForDateRange } = await getMonitorChecks(monitorId, model, dates, sort); // Build monitor stats const monitorStats = { @@ -423,20 +391,10 @@ const getMonitorStatsById = async ({ latestResponseTime: getLatestResponseTime(checksAll), periodIncidents: getIncidents(checksForDateRange), periodTotalChecks: checksForDateRange.length, - checks: processChecksForDisplay( - NormalizeData, - checksForDateRange, - numToDisplay, - normalize - ), + checks: processChecksForDisplay(NormalizeData, checksForDateRange, numToDisplay, normalize), }; - if ( - monitor.type === "http" || - monitor.type === "ping" || - monitor.type === "docker" || - monitor.type === "port" - ) { + if (monitor.type === "http" || monitor.type === "ping" || monitor.type === "docker" || monitor.type === "port") { // HTTP/PING Specific stats monitorStats.periodAvgResponseTime = getAverageResponseTime(checksForDateRange); monitorStats.periodUptime = getUptimePercentage(checksForDateRange); @@ -463,9 +421,7 @@ const getHardwareDetailsById = async ({ monitorId, dateRange }) => { month: "%Y-%m-%dT00:00:00Z", }; const dateString = formatLookup[dateRange]; - const hardwareStats = await HardwareCheck.aggregate( - buildHardwareDetailsPipeline(monitor, dates, dateString) - ); + const hardwareStats = await HardwareCheck.aggregate(buildHardwareDetailsPipeline(monitor, dates, dateString)); const monitorStats = { ...monitor.toObject(), @@ -505,16 +461,18 @@ const getMonitorById = async (monitorId) => { } }; -const getMonitorsByTeamId = async ({ - limit, - type, - page, - rowsPerPage, - filter, - field, - order, - teamId, -}) => { +const getMonitorsByIds = async (monitorIds) => { + try { + const objectIds = monitorIds.map((id) => new ObjectId(id)); + return await Monitor.find({ _id: { $in: objectIds } }, { _id: 1, teamId: 1 }).lean(); + } catch (error) { + error.service = SERVICE_NAME; + error.method = "getMonitorsByIds"; + throw error; + } +}; + +const getMonitorsByTeamId = async ({ limit, type, page, rowsPerPage, filter, field, order, teamId }) => { limit = parseInt(limit); page = parseInt(page); rowsPerPage = parseInt(rowsPerPage); @@ -528,14 +486,10 @@ const getMonitorsByTeamId = async ({ matchStage.type = Array.isArray(type) ? { $in: type } : type; } - const summaryResult = await Monitor.aggregate( - buildMonitorSummaryByTeamIdPipeline({ matchStage }) - ); + const summaryResult = await Monitor.aggregate(buildMonitorSummaryByTeamIdPipeline({ matchStage })); const summary = summaryResult[0]; - const monitors = await Monitor.aggregate( - buildMonitorsByTeamIdPipeline({ matchStage, field, order }) - ); + const monitors = await Monitor.aggregate(buildMonitorsByTeamIdPipeline({ matchStage, field, order })); const filteredMonitors = await Monitor.aggregate( buildFilteredMonitorsByTeamIdPipeline({ @@ -569,14 +523,10 @@ const getMonitorsAndSummaryByTeamId = async ({ type, explain, teamId }) => { } if (explain === true) { - return Monitor.aggregate( - buildMonitorsAndSummaryByTeamIdPipeline({ matchStage }) - ).explain("executionStats"); + return Monitor.aggregate(buildMonitorsAndSummaryByTeamIdPipeline({ matchStage })).explain("executionStats"); } - const queryResult = await Monitor.aggregate( - buildMonitorsAndSummaryByTeamIdPipeline({ matchStage }) - ); + const queryResult = await Monitor.aggregate(buildMonitorsAndSummaryByTeamIdPipeline({ matchStage })); const { monitors, summary } = queryResult?.[0] ?? {}; return { monitors, summary }; } catch (error) { @@ -586,17 +536,7 @@ const getMonitorsAndSummaryByTeamId = async ({ type, explain, teamId }) => { } }; -const getMonitorsWithChecksByTeamId = async ({ - limit, - type, - page, - rowsPerPage, - filter, - field, - order, - teamId, - explain, -}) => { +const getMonitorsWithChecksByTeamId = async ({ limit, type, page, rowsPerPage, filter, field, order, teamId, explain }) => { try { limit = parseInt(limit); page = parseInt(page); @@ -684,9 +624,7 @@ const createMonitor = async ({ body, teamId, userId }) => { */ const createBulkMonitors = async (req) => { try { - const monitors = req.map( - (item) => new Monitor({ ...item, notifications: undefined }) - ); + const monitors = req.map((item) => new Monitor({ ...item, notifications: undefined })); await Monitor.bulkSave(monitors); return monitors; } catch (error) { @@ -826,6 +764,7 @@ export { getAllMonitorsWithUptimeStats, getMonitorStatsById, getMonitorById, + getMonitorsByIds, getMonitorsByTeamId, getMonitorsAndSummaryByTeamId, getMonitorsWithChecksByTeamId, diff --git a/server/db/mongo/modules/monitorModuleQueries.js b/server/db/mongo/modules/monitorModuleQueries.js index ab45eebb6..422362f30 100755 --- a/server/db/mongo/modules/monitorModuleQueries.js +++ b/server/db/mongo/modules/monitorModuleQueries.js @@ -31,11 +31,7 @@ const buildUptimeDetailsPipeline = (monitorId, dates, dateString) => { $project: { _id: 0, percentage: { - $cond: [ - { $eq: ["$totalChecks", 0] }, - 0, - { $divide: ["$upChecks", "$totalChecks"] }, - ], + $cond: [{ $eq: ["$totalChecks", 0] }, 0, { $divide: ["$upChecks", "$totalChecks"] }], }, }, }, @@ -235,11 +231,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => { { $match: { $expr: { - $and: [ - { $eq: ["$monitorId", monitor._id] }, - { $gte: ["$createdAt", dates.start] }, - { $lte: ["$createdAt", dates.end] }, - ], + $and: [{ $eq: ["$monitorId", monitor._id] }, { $gte: ["$createdAt", dates.start] }, { $lte: ["$createdAt", dates.end] }], }, }, }, @@ -326,10 +318,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => { input: "$disks", as: "diskArray", in: { - $arrayElemAt: [ - "$$diskArray.read_speed_bytes", - "$$diskIndex", - ], + $arrayElemAt: ["$$diskArray.read_speed_bytes", "$$diskIndex"], }, }, }, @@ -340,10 +329,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => { input: "$disks", as: "diskArray", in: { - $arrayElemAt: [ - "$$diskArray.write_speed_bytes", - "$$diskIndex", - ], + $arrayElemAt: ["$$diskArray.write_speed_bytes", "$$diskIndex"], }, }, }, @@ -354,10 +340,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => { input: "$disks", as: "diskArray", in: { - $arrayElemAt: [ - "$$diskArray.total_bytes", - "$$diskIndex", - ], + $arrayElemAt: ["$$diskArray.total_bytes", "$$diskIndex"], }, }, }, @@ -379,10 +362,7 @@ const buildHardwareDetailsPipeline = (monitor, dates, dateString) => { input: "$disks", as: "diskArray", in: { - $arrayElemAt: [ - "$$diskArray.usage_percent", - "$$diskIndex", - ], + $arrayElemAt: ["$$diskArray.usage_percent", "$$diskIndex"], }, }, }, @@ -556,26 +536,14 @@ const buildMonitorsAndSummaryByTeamIdPipeline = ({ matchStage }) => { ]; }; -const buildMonitorsWithChecksByTeamIdPipeline = ({ - matchStage, - filter, - page, - rowsPerPage, - field, - order, - limit, - type, -}) => { +const buildMonitorsWithChecksByTeamIdPipeline = ({ matchStage, filter, page, rowsPerPage, field, order, limit, type }) => { const skip = page && rowsPerPage ? page * rowsPerPage : 0; const sort = { [field]: order === "asc" ? 1 : -1 }; const limitStage = rowsPerPage ? [{ $limit: rowsPerPage }] : []; // Match name if (typeof filter !== "undefined" && field === "name") { - matchStage.$or = [ - { name: { $regex: filter, $options: "i" } }, - { url: { $regex: filter, $options: "i" } }, - ]; + matchStage.$or = [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }]; } // Match isActive @@ -667,37 +635,20 @@ const buildMonitorsWithChecksByTeamIdPipeline = ({ return pipeline; }; -const buildFilteredMonitorsByTeamIdPipeline = ({ - matchStage, - filter, - page, - rowsPerPage, - field, - order, - limit, - type, -}) => { +const buildFilteredMonitorsByTeamIdPipeline = ({ matchStage, filter, page, rowsPerPage, field, order, limit, type }) => { const skip = page && rowsPerPage ? page * rowsPerPage : 0; const sort = { [field]: order === "asc" ? 1 : -1 }; const limitStage = rowsPerPage ? [{ $limit: rowsPerPage }] : []; if (typeof filter !== "undefined" && field === "name") { - matchStage.$or = [ - { name: { $regex: filter, $options: "i" } }, - { url: { $regex: filter, $options: "i" } }, - ]; + matchStage.$or = [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }]; } if (typeof filter !== "undefined" && field === "status") { matchStage.status = filter === "true"; } - const pipeline = [ - { $match: matchStage }, - { $sort: sort }, - { $skip: skip }, - ...limitStage, - ]; + const pipeline = [{ $match: matchStage }, { $sort: sort }, { $skip: skip }, ...limitStage]; // Add checks if (limit) { @@ -792,10 +743,7 @@ const buildGetMonitorsByTeamIdPipeline = (req) => { ? [ { $match: { - $or: [ - { name: { $regex: filter, $options: "i" } }, - { url: { $regex: filter, $options: "i" } }, - ], + $or: [{ name: { $regex: filter, $options: "i" } }, { url: { $regex: filter, $options: "i" } }], }, }, ] diff --git a/server/db/mongo/modules/networkCheckModule.js b/server/db/mongo/modules/networkCheckModule.js index 53c55c7e3..12a86e73b 100644 --- a/server/db/mongo/modules/networkCheckModule.js +++ b/server/db/mongo/modules/networkCheckModule.js @@ -32,9 +32,7 @@ const createNetworkCheck = async (networkCheckData) => { */ const getNetworkChecksByMonitorId = async (monitorId, limit = 100) => { try { - const networkChecks = await NetworkCheck.find({ monitorId }) - .sort({ createdAt: -1 }) - .limit(limit); + const networkChecks = await NetworkCheck.find({ monitorId }).sort({ createdAt: -1 }).limit(limit); return networkChecks; } catch (error) { error.service = SERVICE_NAME; diff --git a/server/db/mongo/modules/settingsModule.js b/server/db/mongo/modules/settingsModule.js index 4fd0d1634..df325d2fa 100755 --- a/server/db/mongo/modules/settingsModule.js +++ b/server/db/mongo/modules/settingsModule.js @@ -29,9 +29,7 @@ const updateAppSettings = async (newSettings) => { await AppSettings.findOneAndUpdate({}, update, { upsert: true, }); - const settings = await AppSettings.findOne() - .select("-__v -_id -createdAt -updatedAt -singleton") - .lean(); + const settings = await AppSettings.findOne().select("-__v -_id -createdAt -updatedAt -singleton").lean(); return settings; } catch (error) { error.service = SERVICE_NAME; diff --git a/server/db/mongo/modules/statusPageModule.js b/server/db/mongo/modules/statusPageModule.js index 8a682d567..c126d913f 100755 --- a/server/db/mongo/modules/statusPageModule.js +++ b/server/db/mongo/modules/statusPageModule.js @@ -48,13 +48,9 @@ const updateStatusPage = async (statusPageData, image) => { if (statusPageData.deleteSubmonitors === "true") { statusPageData.subMonitors = []; } - const statusPage = await StatusPage.findOneAndUpdate( - { url: statusPageData.url }, - statusPageData, - { - new: true, - } - ); + const statusPage = await StatusPage.findOneAndUpdate({ url: statusPageData.url }, statusPageData, { + new: true, + }); return statusPage; } catch (error) { @@ -98,19 +94,8 @@ const getStatusPage = async (url) => { } if (!preliminaryStatusPage.monitors || preliminaryStatusPage.monitors.length === 0) { - const { - _id, - color, - companyName, - isPublished, - logo, - originalMonitors, - showCharts, - showUptimePercentage, - timezone, - showAdminLoginLink, - url, - } = preliminaryStatusPage; + const { _id, color, companyName, isPublished, logo, originalMonitors, showCharts, showUptimePercentage, timezone, showAdminLoginLink, url } = + preliminaryStatusPage; return { statusPage: { _id, diff --git a/server/db/mongo/modules/userModule.js b/server/db/mongo/modules/userModule.js index 4069be8c6..cd609bb5d 100755 --- a/server/db/mongo/modules/userModule.js +++ b/server/db/mongo/modules/userModule.js @@ -16,11 +16,7 @@ const SERVICE_NAME = "userModule"; * @returns {Promise} * @throws {Error} */ -const insertUser = async ( - userData, - imageFile, - generateAvatarImage = GenerateAvatarImage -) => { +const insertUser = async (userData, imageFile, generateAvatarImage = GenerateAvatarImage) => { const stringService = ServiceRegistry.get(StringService.SERVICE_NAME); try { if (imageFile) { @@ -47,9 +43,7 @@ const insertUser = async ( const newUser = new UserModel(userData); await newUser.save(); - return await UserModel.findOne({ _id: newUser._id }) - .select("-password") - .select("-profileImage"); // .select() doesn't work with create, need to save then find + return await UserModel.findOne({ _id: newUser._id }).select("-password").select("-profileImage"); // .select() doesn't work with create, need to save then find } catch (error) { if (error.code === DUPLICATE_KEY_CODE) { error.message = stringService.dbUserExists; @@ -98,31 +92,26 @@ const getUserByEmail = async (email) => { * @throws {Error} */ -const updateUser = async ( - req, - res, - parseBoolean = ParseBoolean, - generateAvatarImage = GenerateAvatarImage -) => { - const candidateUserId = req.params.userId; - try { - const candidateUser = { ...req.body }; - // ****************************************** - // Handle profile image - // ****************************************** +const updateUser = async ({ userId, user, file }) => { + if (!userId) { + throw new Error("No user in request"); + } - if (parseBoolean(candidateUser.deleteProfileImage) === true) { + try { + const candidateUser = { ...user }; + + if (ParseBoolean(candidateUser.deleteProfileImage) === true) { candidateUser.profileImage = null; candidateUser.avatarImage = null; - } else if (req.file) { + } else if (file) { // 1. Save the full size image candidateUser.profileImage = { - data: req.file.buffer, - contentType: req.file.mimetype, + data: file.buffer, + contentType: file.mimetype, }; // 2. Get the avatar sized image - const avatar = await generateAvatarImage(req.file); + const avatar = await GenerateAvatarImage(file); candidateUser.avatarImage = avatar; } @@ -131,7 +120,7 @@ const updateUser = async ( // ****************************************** const updatedUser = await UserModel.findByIdAndUpdate( - candidateUserId, + userId, candidateUser, { new: true } // Returns updated user instead of pre-update user ) @@ -220,13 +209,4 @@ const logoutUser = async (userId) => { } }; -export { - insertUser, - getUserByEmail, - updateUser, - deleteUser, - deleteTeam, - deleteAllOtherUsers, - getAllUsers, - logoutUser, -}; +export { insertUser, getUserByEmail, updateUser, deleteUser, deleteTeam, deleteAllOtherUsers, getAllUsers, logoutUser }; diff --git a/server/index.js b/server/index.js index 4b74ef46c..5f391c754 100755 --- a/server/index.js +++ b/server/index.js @@ -99,9 +99,7 @@ const SHUTDOWN_TIMEOUT = 1000; let isShuttingDown = false; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const openApiSpec = JSON.parse( - fs.readFileSync(path.join(__dirname, "openapi.json"), "utf8") -); +const openApiSpec = JSON.parse(fs.readFileSync(path.join(__dirname, "openapi.json"), "utf8")); const frontendPath = path.join(__dirname, "public"); @@ -156,25 +154,8 @@ const startApp = async () => { // Set allowed origin const allowedOrigin = appSettings.clientHost; - const networkService = new NetworkService( - axios, - ping, - logger, - http, - Docker, - net, - stringService, - settingsService - ); - const emailService = new EmailService( - settingsService, - fs, - path, - compile, - mjml2html, - nodemailer, - logger - ); + const networkService = new NetworkService(axios, ping, logger, http, Docker, net, stringService, settingsService); + const emailService = new EmailService(settingsService, fs, path, compile, mjml2html, nodemailer, logger); const bufferService = new BufferService({ db, logger }); const statusService = new StatusService({ db, logger, buffer: bufferService }); const notificationUtils = new NotificationUtils({ @@ -292,17 +273,11 @@ const startApp = async () => { ServiceRegistry.get(StringService.SERVICE_NAME) ); - const queueController = new QueueController( - ServiceRegistry.get(JobQueue.SERVICE_NAME), - ServiceRegistry.get(StringService.SERVICE_NAME) - ); + const queueController = new QueueController(ServiceRegistry.get(JobQueue.SERVICE_NAME), ServiceRegistry.get(StringService.SERVICE_NAME)); const logController = new LogController(logger); - const statusPageController = new StatusPageController( - ServiceRegistry.get(MongoDB.SERVICE_NAME), - ServiceRegistry.get(StringService.SERVICE_NAME) - ); + const statusPageController = new StatusPageController(ServiceRegistry.get(MongoDB.SERVICE_NAME), ServiceRegistry.get(StringService.SERVICE_NAME)); const notificationController = new NotificationController({ notificationService: ServiceRegistry.get(NotificationService.SERVICE_NAME), @@ -311,9 +286,7 @@ const startApp = async () => { db: ServiceRegistry.get(MongoDB.SERVICE_NAME), }); - const diagnosticController = new DiagnosticController( - ServiceRegistry.get(MongoDB.SERVICE_NAME) - ); + const diagnosticController = new DiagnosticController(ServiceRegistry.get(MongoDB.SERVICE_NAME)); //Create routes const authRoutes = new AuthRoutes(authController); @@ -321,9 +294,7 @@ const startApp = async () => { const settingsRoutes = new SettingsRoutes(settingsController); const checkRoutes = new CheckRoutes(checkController); const inviteRoutes = new InviteRoutes(inviteController); - const maintenanceWindowRoutes = new MaintenanceWindowRoutes( - maintenanceWindowController - ); + const maintenanceWindowRoutes = new MaintenanceWindowRoutes(maintenanceWindowController); const queueRoutes = new QueueRoutes(queueController); const logRoutes = new LogRoutes(logController); const statusPageRoutes = new StatusPageRoutes(statusPageController); @@ -372,16 +343,17 @@ const startApp = async () => { //routes app.use("/api/v1/auth", authRoutes.getRouter()); - app.use("/api/v1/settings", verifyJWT, settingsRoutes.getRouter()); - app.use("/api/v1/invite", inviteRoutes.getRouter()); - app.use("/api/v1/monitors", verifyJWT, monitorRoutes.getRouter()); app.use("/api/v1/checks", verifyJWT, checkRoutes.getRouter()); - app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRoutes.getRouter()); - app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter()); - app.use("/api/v1/logs", verifyJWT, logRoutes.getRouter()); - app.use("/api/v1/status-page", statusPageRoutes.getRouter()); - app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter()); app.use("/api/v1/diagnostic", verifyJWT, diagnosticRoutes.getRouter()); + app.use("/api/v1/invite", inviteRoutes.getRouter()); + app.use("/api/v1/logs", verifyJWT, logRoutes.getRouter()); + app.use("/api/v1/maintenance-window", verifyJWT, maintenanceWindowRoutes.getRouter()); + app.use("/api/v1/monitors", verifyJWT, monitorRoutes.getRouter()); + app.use("/api/v1/notifications", verifyJWT, notificationRoutes.getRouter()); + app.use("/api/v1/queue", verifyJWT, queueRoutes.getRouter()); + app.use("/api/v1/settings", verifyJWT, settingsRoutes.getRouter()); + app.use("/api/v1/status-page", statusPageRoutes.getRouter()); + app.use("/api/v1/health", (req, res) => { res.json({ status: "OK", diff --git a/server/middleware/isAllowed.js b/server/middleware/isAllowed.js index 4c6f89aa3..27fb875ed 100755 --- a/server/middleware/isAllowed.js +++ b/server/middleware/isAllowed.js @@ -29,9 +29,7 @@ const isAllowed = (allowedRoles) => { // Parse the token try { const parsedToken = token.slice(TOKEN_PREFIX.length, token.length); - const { jwtSecret } = ServiceRegistry.get( - SettingsService.SERVICE_NAME - ).getSettings(); + const { jwtSecret } = ServiceRegistry.get(SettingsService.SERVICE_NAME).getSettings(); var decoded = jwt.verify(parsedToken, jwtSecret); const userRoles = decoded.role; diff --git a/server/middleware/languageMiddleware.js b/server/middleware/languageMiddleware.js index 1449e203f..effd49cc7 100755 --- a/server/middleware/languageMiddleware.js +++ b/server/middleware/languageMiddleware.js @@ -1,28 +1,27 @@ import logger from "../utils/logger.js"; -const languageMiddleware = - (stringService, translationService) => async (req, res, next) => { - try { - const acceptLanguage = req.headers["accept-language"] || "en"; - const language = acceptLanguage.split(",")[0].slice(0, 2).toLowerCase(); +const languageMiddleware = (stringService, translationService) => async (req, res, next) => { + try { + const acceptLanguage = req.headers["accept-language"] || "en"; + const language = acceptLanguage.split(",")[0].slice(0, 2).toLowerCase(); - translationService.setLanguage(language); - stringService.setLanguage(language); + translationService.setLanguage(language); + stringService.setLanguage(language); - next(); - } catch (error) { - logger.error({ - message: error.message, - service: "languageMiddleware", - }); - const acceptLanguage = req.headers["accept-language"] || "en"; - const language = acceptLanguage.split(",")[0].slice(0, 2).toLowerCase(); + next(); + } catch (error) { + logger.error({ + message: error.message, + service: "languageMiddleware", + }); + const acceptLanguage = req.headers["accept-language"] || "en"; + const language = acceptLanguage.split(",")[0].slice(0, 2).toLowerCase(); - translationService.setLanguage(language); - stringService.setLanguage(language); + translationService.setLanguage(language); + stringService.setLanguage(language); - next(); - } - }; + next(); + } +}; export default languageMiddleware; diff --git a/server/middleware/verifyJWT.js b/server/middleware/verifyJWT.js index bd17edcfd..46611c0dd 100755 --- a/server/middleware/verifyJWT.js +++ b/server/middleware/verifyJWT.js @@ -39,13 +39,13 @@ const verifyJWT = (req, res, next) => { const { jwtSecret } = ServiceRegistry.get(SettingsService.SERVICE_NAME).getSettings(); jwt.verify(parsedToken, jwtSecret, (err, decoded) => { if (err) { - if (err) { - const errorMessage = - err.name === "TokenExpiredError" - ? stringService.expiredAuthToken - : stringService.invalidAuthToken; - return res.status(401).json({ success: false, msg: errorMessage }); - } + const errorMessage = err.name === "TokenExpiredError" ? stringService.expiredAuthToken : stringService.invalidAuthToken; + err.details = { msg: errorMessage }; + err.status = 401; + err.service = SERVICE_NAME; + err.method = "verifyJWT"; + next(err); + return; } else { // Token is valid, carry on req.user = decoded; diff --git a/server/middleware/verifySuperAdmin.js b/server/middleware/verifySuperAdmin.js index 98c336fad..a5a93eede 100755 --- a/server/middleware/verifySuperAdmin.js +++ b/server/middleware/verifySuperAdmin.js @@ -47,9 +47,7 @@ const verifySuperAdmin = (req, res, next) => { stack: err.stack, details: stringService.invalidAuthToken, }); - return res - .status(401) - .json({ success: false, msg: stringService.invalidAuthToken }); + return res.status(401).json({ success: false, msg: stringService.invalidAuthToken }); } if (decoded.role.includes("superadmin") === false) { diff --git a/server/openapi.json b/server/openapi.json index 7b7bb3cb3..465bf948a 100755 --- a/server/openapi.json +++ b/server/openapi.json @@ -100,14 +100,7 @@ "application/json": { "schema": { "type": "object", - "required": [ - "firstName", - "lastName", - "email", - "password", - "role", - "teamId" - ], + "required": ["firstName", "lastName", "email", "password", "role", "teamId"], "properties": { "firstName": { "type": "string" diff --git a/server/routes/announcementsRoute.js b/server/routes/announcementsRoute.js index 0b234cb96..011dfac4a 100755 --- a/server/routes/announcementsRoute.js +++ b/server/routes/announcementsRoute.js @@ -15,12 +15,7 @@ class AnnouncementRoutes { * @desc Create a new announcement * @access Private (Requires JWT verification) */ - this.router.post( - "/", - verifyJWT, - isAllowed(["admin", "superadmin"]), - this.announcementController.createAnnouncement - ); + this.router.post("/", verifyJWT, isAllowed(["admin", "superadmin"]), this.announcementController.createAnnouncement); /** * @route GET / diff --git a/server/routes/authRoute.js b/server/routes/authRoute.js index aabd8de89..6d8af52f8 100755 --- a/server/routes/authRoute.js +++ b/server/routes/authRoute.js @@ -15,40 +15,19 @@ class AuthRoutes { } initRoutes() { - this.router.post( - "/register", - upload.single("profileImage"), - this.authController.registerUser - ); + this.router.post("/register", upload.single("profileImage"), this.authController.registerUser); this.router.post("/login", this.authController.loginUser); - this.router.put( - "/user/:userId", - upload.single("profileImage"), - verifyJWT, - this.authController.editUser - ); - - this.router.get("/users/superadmin", this.authController.checkSuperadminExists); - - this.router.get( - "/users", - verifyJWT, - isAllowed(["admin", "superadmin"]), - this.authController.getAllUsers - ); - - this.router.delete( - "/user/:userId", - verifyJWT, - verifyOwnership(User, "userId"), - this.authController.deleteUser - ); - - // Recovery routes this.router.post("/recovery/request", this.authController.requestRecovery); this.router.post("/recovery/validate", this.authController.validateRecovery); this.router.post("/recovery/reset/", this.authController.resetPassword); + + this.router.get("/users/superadmin", this.authController.checkSuperadminExists); + + this.router.get("/users", verifyJWT, isAllowed(["admin", "superadmin"]), this.authController.getAllUsers); + + this.router.put("/user", verifyJWT, upload.single("profileImage"), this.authController.editUser); + this.router.delete("/user", verifyJWT, this.authController.deleteUser); } getRouter() { diff --git a/server/routes/checkRoute.js b/server/routes/checkRoute.js index f215cbe37..8bdb2d4fb 100755 --- a/server/routes/checkRoute.js +++ b/server/routes/checkRoute.js @@ -13,35 +13,16 @@ class CheckRoutes { } initRoutes() { - this.router.get("/team", this.checkController.getChecksByTeam); this.router.get("/team/summary", this.checkController.getChecksSummaryByTeamId); - this.router.delete( - "/team", - isAllowed(["admin", "superadmin"]), - this.checkController.deleteChecksByTeamId - ); - this.router.put( - "/check/:checkId", - verifyTeamAccess(Check, "checkId"), - this.checkController.ackCheck - ); - this.router.put( - "/team/ttl", - isAllowed(["admin", "superadmin"]), - this.checkController.updateChecksTTL - ); + this.router.get("/team", this.checkController.getChecksByTeam); + this.router.put("/team/ttl", isAllowed(["admin", "superadmin"]), this.checkController.updateChecksTTL); + this.router.delete("/team", isAllowed(["admin", "superadmin"]), this.checkController.deleteChecksByTeamId); + + this.router.put("/check/:checkId", this.checkController.ackCheck); this.router.get("/:monitorId", this.checkController.getChecksByMonitor); - this.router.post( - "/:monitorId", - verifyOwnership(Monitor, "monitorId"), - this.checkController.createCheck - ); - this.router.delete( - "/:monitorId", - verifyOwnership(Monitor, "monitorId"), - this.checkController.deleteChecks - ); + this.router.delete("/:monitorId", this.checkController.deleteChecks); + this.router.put("/:path/:monitorId?", this.checkController.ackAllChecks); } diff --git a/server/routes/diagnosticRoute.js b/server/routes/diagnosticRoute.js index 0d8f4df84..a15f5a038 100755 --- a/server/routes/diagnosticRoute.js +++ b/server/routes/diagnosticRoute.js @@ -1,4 +1,6 @@ import { Router } from "express"; +import { verifyJWT } from "../middleware/verifyJWT.js"; +import { isAllowed } from "../middleware/isAllowed.js"; class DiagnosticRoutes { constructor(diagnosticController) { @@ -7,13 +9,7 @@ class DiagnosticRoutes { this.initRoutes(); } initRoutes() { - this.router.get( - "/db/get-monitors-by-team-id/:teamId", - this.diagnosticController.getMonitorsByTeamIdExecutionStats - ); - - this.router.post("/db/stats", this.diagnosticController.getDbStats); - this.router.get("/system", this.diagnosticController.getSystemStats); + this.router.get("/system", verifyJWT, isAllowed(["admin", "superadmin"]), this.diagnosticController.getSystemStats); } getRouter() { diff --git a/server/routes/inviteRoute.js b/server/routes/inviteRoute.js index 301959389..c50eae9e3 100755 --- a/server/routes/inviteRoute.js +++ b/server/routes/inviteRoute.js @@ -10,15 +10,9 @@ class InviteRoutes { } initRoutes() { - this.router.post( - "/", - isAllowed(["admin", "superadmin"]), - verifyJWT, - this.inviteController.getInviteToken - ); - this.router.post("/send", this.inviteController.sendInviteEmail); this.router.post("/verify", this.inviteController.inviteVerifyController); + this.router.post("/", verifyJWT, isAllowed(["admin", "superadmin"]), this.inviteController.getInviteToken); } getRouter() { diff --git a/server/routes/maintenanceWindowRoute.js b/server/routes/maintenanceWindowRoute.js index 36f982e25..774434ed6 100755 --- a/server/routes/maintenanceWindowRoute.js +++ b/server/routes/maintenanceWindowRoute.js @@ -1,41 +1,20 @@ import { Router } from "express"; -import { verifyOwnership } from "../middleware/verifyOwnership.js"; -import { verifyTeamAccess } from "../middleware/verifyTeamAccess.js"; -import Monitor from "../db/models/Monitor.js"; import MaintenanceWindow from "../db/models/MaintenanceWindow.js"; class MaintenanceWindowRoutes { constructor(maintenanceWindowController) { this.router = Router(); - this.maintenanceWindowController = maintenanceWindowController; + this.mwController = maintenanceWindowController; this.initRoutes(); } initRoutes() { - this.router.post("/", this.maintenanceWindowController.createMaintenanceWindows); + this.router.post("/", this.mwController.createMaintenanceWindows); + this.router.get("/team/", this.mwController.getMaintenanceWindowsByTeamId); - this.router.get( - "/monitor/:monitorId", - verifyOwnership(Monitor, "monitorId"), - this.maintenanceWindowController.getMaintenanceWindowsByMonitorId - ); + this.router.get("/monitor/:monitorId", this.mwController.getMaintenanceWindowsByMonitorId); - this.router.get( - "/team/", - this.maintenanceWindowController.getMaintenanceWindowsByTeamId - ); - - this.router.get("/:id", this.maintenanceWindowController.getMaintenanceWindowById); - - this.router.put( - "/:id", - verifyTeamAccess(MaintenanceWindow, "id"), - this.maintenanceWindowController.editMaintenanceWindow - ); - - this.router.delete( - "/:id", - verifyTeamAccess(MaintenanceWindow, "id"), - this.maintenanceWindowController.deleteMaintenanceWindow - ); + this.router.get("/:id", this.mwController.getMaintenanceWindowById); + this.router.put("/:id", this.mwController.editMaintenanceWindow); + this.router.delete("/:id", this.mwController.deleteMaintenanceWindow); } getRouter() { diff --git a/server/routes/monitorRoute.js b/server/routes/monitorRoute.js index 168f8b88d..2911bf9d7 100755 --- a/server/routes/monitorRoute.js +++ b/server/routes/monitorRoute.js @@ -17,103 +17,49 @@ class MonitorRoutes { } initRoutes() { - this.router.get("/", this.monitorController.getAllMonitors); - this.router.get("/uptime", this.monitorController.getAllMonitorsWithUptimeStats); - this.router.get( - "/export", - isAllowed(["admin", "superadmin"]), - this.monitorController.exportMonitorsToCSV - ); - this.router.get("/stats/:monitorId", this.monitorController.getMonitorStatsById); - this.router.get( - "/hardware/details/:monitorId", - this.monitorController.getHardwareDetailsById - ); - - this.router.get( - "/uptime/details/:monitorId", - this.monitorController.getUptimeDetailsById - ); - this.router.get("/certificate/:monitorId", (req, res, next) => { - this.monitorController.getMonitorCertificate( - req, - res, - next, - fetchMonitorCertificate - ); - }); + // Team routes this.router.get("/team", this.monitorController.getMonitorsByTeamId); + this.router.get("/team/with-checks", this.monitorController.getMonitorsWithChecksByTeamId); + this.router.get("/summary/team", this.monitorController.getMonitorsAndSummaryByTeamId); // TODO should be /team/summary + // Uptime routes + this.router.get("/uptime", this.monitorController.getAllMonitorsWithUptimeStats); + this.router.get("/uptime/details/:monitorId", this.monitorController.getUptimeDetailsById); + + // Hardware routes + this.router.get("/hardware/details/:monitorId", this.monitorController.getHardwareDetailsById); + + // General monitor routes + this.router.post("/pause/:monitorId", isAllowed(["admin", "superadmin"]), this.monitorController.pauseMonitor); + this.router.get("/stats/:monitorId", this.monitorController.getMonitorStatsById); + + // Util routes + this.router.get("/resolution/url", isAllowed(["admin", "superadmin"]), this.monitorController.checkEndpointResolution); + this.router.get("/certificate/:monitorId", (req, res, next) => { + this.monitorController.getMonitorCertificate(req, res, next, fetchMonitorCertificate); + }); + + // General monitor CRUD routes + this.router.get("/", this.monitorController.getAllMonitors); + this.router.post("/", isAllowed(["admin", "superadmin"]), this.monitorController.createMonitor); + this.router.delete("/", isAllowed(["superadmin"]), this.monitorController.deleteAllMonitors); + + // Other static routes + this.router.post("/demo", isAllowed(["admin", "superadmin"]), this.monitorController.addDemoMonitors); + this.router.get("/export", isAllowed(["admin", "superadmin"]), this.monitorController.exportMonitorsToCSV); + this.router.post("/seed", isAllowed(["superadmin"]), this.monitorController.seedDb); + this.router.post("/bulk", isAllowed(["admin", "superadmin"]), upload.single("csvFile"), this.monitorController.createBulkMonitors); + this.router.post("/test-email", isAllowed(["admin", "superadmin"]), this.monitorController.sendTestEmail); + + // Individual monitor CRUD routes this.router.get("/:monitorId", this.monitorController.getMonitorById); - - this.router.get( - "/summary/team", - this.monitorController.getMonitorsAndSummaryByTeamId - ); - - this.router.get( - "/team/with-checks", - this.monitorController.getMonitorsWithChecksByTeamId - ); - - this.router.get( - "/resolution/url", - isAllowed(["admin", "superadmin"]), - this.monitorController.checkEndpointResolution - ); - + this.router.put("/:monitorId", verifyTeamAccess(Monitor, "monitorId"), isAllowed(["admin", "superadmin"]), this.monitorController.editMonitor); this.router.delete( "/:monitorId", verifyOwnership(Monitor, "monitorId"), isAllowed(["admin", "superadmin"]), this.monitorController.deleteMonitor ); - - this.router.post( - "/", - isAllowed(["admin", "superadmin"]), - this.monitorController.createMonitor - ); - - this.router.put( - "/:monitorId", - verifyTeamAccess(Monitor, "monitorId"), - isAllowed(["admin", "superadmin"]), - this.monitorController.editMonitor - ); - - this.router.delete( - "/", - isAllowed(["superadmin"]), - this.monitorController.deleteAllMonitors - ); - - this.router.post( - "/pause/:monitorId", - isAllowed(["admin", "superadmin"]), - this.monitorController.pauseMonitor - ); - - this.router.post( - "/demo", - isAllowed(["admin", "superadmin"]), - this.monitorController.addDemoMonitors - ); - - this.router.post( - "/bulk", - isAllowed(["admin", "superadmin"]), - upload.single("csvFile"), - this.monitorController.createBulkMonitors - ); - - this.router.post("/seed", isAllowed(["superadmin"]), this.monitorController.seedDb); - - this.router.post( - "/test-email", - isAllowed(["admin", "superadmin"]), - this.monitorController.sendTestEmail - ); } getRouter() { diff --git a/server/routes/notificationRoute.js b/server/routes/notificationRoute.js index 09becd16c..6c77d8519 100755 --- a/server/routes/notificationRoute.js +++ b/server/routes/notificationRoute.js @@ -13,25 +13,16 @@ class NotificationRoutes { initializeRoutes() { this.router.use(verifyJWT); - this.router.post("/test", this.notificationController.testNotification); - this.router.post("/test/all", this.notificationController.testAllNotifications); - this.router.post("/", this.notificationController.createNotification); + this.router.post("/test/all", this.notificationController.testAllNotifications); + this.router.post("/test", this.notificationController.testNotification); + this.router.get("/team", this.notificationController.getNotificationsByTeamId); - this.router.delete( - "/:id", - verifyOwnership(Notification, "id"), - this.notificationController.deleteNotification - ); - + this.router.delete("/:id", verifyOwnership(Notification, "id"), this.notificationController.deleteNotification); this.router.get("/:id", this.notificationController.getNotificationById); - this.router.put( - "/:id", - verifyTeamAccess(Notification, "id"), - this.notificationController.editNotification - ); + this.router.put("/:id", verifyTeamAccess(Notification, "id"), this.notificationController.editNotification); } getRouter() { diff --git a/server/routes/queueRoute.js b/server/routes/queueRoute.js index c40d21528..55e829b91 100755 --- a/server/routes/queueRoute.js +++ b/server/routes/queueRoute.js @@ -7,41 +7,13 @@ class QueueRoutes { this.initRoutes(); } initRoutes() { - this.router.get( - "/metrics", - isAllowed(["admin", "superadmin"]), - this.queueController.getMetrics - ); + this.router.get("/jobs", isAllowed(["admin", "superadmin"]), this.queueController.getJobs); + this.router.post("/jobs", isAllowed(["admin", "superadmin"]), this.queueController.addJob); - this.router.get( - "/jobs", - isAllowed(["admin", "superadmin"]), - this.queueController.getJobs - ); - - this.router.get( - "/all-metrics", - isAllowed(["admin", "superadmin"]), - this.queueController.getAllMetrics - ); - - this.router.post( - "/jobs", - isAllowed(["admin", "superadmin"]), - this.queueController.addJob - ); - - this.router.post( - "/flush", - isAllowed(["admin", "superadmin"]), - this.queueController.flushQueue - ); - - this.router.get( - "/health", - isAllowed(["admin", "superadmin"]), - this.queueController.checkQueueHealth - ); + this.router.get("/metrics", isAllowed(["admin", "superadmin"]), this.queueController.getMetrics); + this.router.get("/health", isAllowed(["admin", "superadmin"]), this.queueController.checkQueueHealth); + this.router.get("/all-metrics", isAllowed(["admin", "superadmin"]), this.queueController.getAllMetrics); + this.router.post("/flush", isAllowed(["admin", "superadmin"]), this.queueController.flushQueue); } getRouter() { diff --git a/server/routes/settingsRoute.js b/server/routes/settingsRoute.js index f293fd660..88eb67e65 100755 --- a/server/routes/settingsRoute.js +++ b/server/routes/settingsRoute.js @@ -10,16 +10,8 @@ class SettingsRoutes { initRoutes() { this.router.get("/", this.settingsController.getAppSettings); - this.router.put( - "/", - isAllowed(["admin", "superadmin"]), - this.settingsController.updateAppSettings - ); - this.router.post( - "/test-email", - isAllowed(["admin", "superadmin"]), - this.settingsController.sendTestEmail - ); + this.router.put("/", isAllowed(["admin", "superadmin"]), this.settingsController.updateAppSettings); + this.router.post("/test-email", isAllowed(["admin", "superadmin"]), this.settingsController.sendTestEmail); } getRouter() { diff --git a/server/routes/statusPageRoute.js b/server/routes/statusPageRoute.js index 98b3a3433..53eb21fba 100755 --- a/server/routes/statusPageRoute.js +++ b/server/routes/statusPageRoute.js @@ -13,20 +13,11 @@ class StatusPageRoutes { initRoutes() { this.router.get("/", this.statusPageController.getStatusPage); this.router.get("/team", verifyJWT, this.statusPageController.getStatusPagesByTeamId); - this.router.get("/:url", this.statusPageController.getStatusPageByUrl); - this.router.post( - "/", - upload.single("logo"), - verifyJWT, - this.statusPageController.createStatusPage - ); - this.router.put( - "/", - upload.single("logo"), - verifyJWT, - this.statusPageController.updateStatusPage - ); + this.router.post("/", upload.single("logo"), verifyJWT, this.statusPageController.createStatusPage); + this.router.put("/", upload.single("logo"), verifyJWT, this.statusPageController.updateStatusPage); + + this.router.get("/:url", this.statusPageController.getStatusPageByUrl); this.router.delete("/:url(*)", verifyJWT, this.statusPageController.deleteStatusPage); } diff --git a/server/service/JobQueue/JobQueue.js b/server/service/JobQueue/JobQueue.js index 38cca7d4d..6e316c616 100644 --- a/server/service/JobQueue/JobQueue.js +++ b/server/service/JobQueue/JobQueue.js @@ -150,11 +150,7 @@ class JobQueue { }; const schedulerId = getSchedulerId(monitor); - await queue.upsertJobScheduler( - schedulerId, - { every: monitor?.interval ?? 60000 }, - jobTemplate - ); + await queue.upsertJobScheduler(schedulerId, { every: monitor?.interval ?? 60000 }, jobTemplate); } async deleteJob(monitor) { diff --git a/server/service/JobQueue/JobQueueHelper.js b/server/service/JobQueue/JobQueueHelper.js index d286b7b14..4107966e8 100644 --- a/server/service/JobQueue/JobQueueHelper.js +++ b/server/service/JobQueue/JobQueueHelper.js @@ -1,16 +1,7 @@ const SERVICE_NAME = "JobQueueHelper"; class JobQueueHelper { - constructor({ - redisService, - Queue, - Worker, - logger, - db, - networkService, - statusService, - notificationService, - }) { + constructor({ redisService, Queue, Worker, logger, db, networkService, statusService, notificationService }) { this.db = db; this.redisService = redisService; this.Queue = Queue; @@ -265,11 +256,7 @@ class JobQueueHelper { // Handle status change await job.updateProgress(60); - const { - monitor: updatedMonitor, - statusChanged, - prevStatus, - } = await this.statusService.updateStatus(networkResponse); + const { monitor: updatedMonitor, statusChanged, prevStatus } = await this.statusService.updateStatus(networkResponse); // Handle notifications await job.updateProgress(80); this.notificationService diff --git a/server/service/PulseQueue/PulseQueue.js b/server/service/PulseQueue/PulseQueue.js index 50a959559..85d14ab07 100644 --- a/server/service/PulseQueue/PulseQueue.js +++ b/server/service/PulseQueue/PulseQueue.js @@ -22,8 +22,7 @@ class PulseQueue { // **************************************** init = async () => { try { - const mongoConnectionString = - this.appSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db"; + const mongoConnectionString = this.appSettings.dbConnectionString || "mongodb://localhost:27017/uptime_db"; this.pulse = new Pulse({ db: { address: mongoConnectionString } }); await this.pulse.start(); this.pulse.define("monitor-job", this.pulseQueueHelper.getMonitorJob(), {}); @@ -186,9 +185,7 @@ class PulseQueue { failReason: job.attrs.failReason, lastRunAt: job.attrs.lastRunAt, lastFinishedAt: job.attrs.lastFinishedAt, - lastRunTook: job.attrs.lockedAt - ? null - : job.attrs.lastFinishedAt - job.attrs.lastRunAt, + lastRunTook: job.attrs.lockedAt ? null : job.attrs.lastFinishedAt - job.attrs.lastRunAt, lastFailedAt: job.attrs.failedAt, }; }); diff --git a/server/service/PulseQueue/PulseQueueHelper.js b/server/service/PulseQueue/PulseQueueHelper.js index 959c12e3b..c3b8992d8 100644 --- a/server/service/PulseQueue/PulseQueueHelper.js +++ b/server/service/PulseQueue/PulseQueueHelper.js @@ -34,11 +34,7 @@ class PulseQueueHelper { throw new Error("No network response"); } - const { - monitor: updatedMonitor, - statusChanged, - prevStatus, - } = await this.statusService.updateStatus(networkResponse); + const { monitor: updatedMonitor, statusChanged, prevStatus } = await this.statusService.updateStatus(networkResponse); this.notificationService .handleNotifications({ diff --git a/server/service/emailService.js b/server/service/emailService.js index 433fd0871..b6b0eb4af 100755 --- a/server/service/emailService.js +++ b/server/service/emailService.js @@ -42,10 +42,7 @@ class EmailService { */ this.loadTemplate = (templateName) => { try { - const templatePath = this.path.join( - __dirname, - `../templates/${templateName}.mjml` - ); + const templatePath = this.path.join(__dirname, `../templates/${templateName}.mjml`); const templateContent = this.fs.readFileSync(templatePath, "utf8"); return this.compile(templateContent); } catch (error) { diff --git a/server/service/networkService.js b/server/service/networkService.js index 08b2f065e..70d7cc932 100755 --- a/server/service/networkService.js +++ b/server/service/networkService.js @@ -78,9 +78,7 @@ class NetworkService { async requestPing(monitor) { try { const url = monitor.url; - const { response, responseTime, error } = await this.timeRequest(() => - this.ping.promise.probe(url) - ); + const { response, responseTime, error } = await this.timeRequest(() => this.ping.promise.probe(url)); const pingResponse = { monitorId: monitor._id, @@ -133,18 +131,7 @@ class NetworkService { */ async requestHttp(monitor) { try { - const { - url, - secret, - _id, - name, - teamId, - type, - ignoreTlsErrors, - jsonPath, - matchMethod, - expectedValue, - } = monitor; + const { url, secret, _id, name, teamId, type, ignoreTlsErrors, jsonPath, matchMethod, expectedValue } = monitor; const config = {}; secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` }); @@ -155,9 +142,7 @@ class NetworkService { }); } - const { response, responseTime, error } = await this.timeRequest(() => - this.axios.get(url, config) - ); + const { response, responseTime, error } = await this.timeRequest(() => this.axios.get(url, config)); const httpResponse = { monitorId: _id, @@ -171,8 +156,7 @@ class NetworkService { const code = error.response?.status || this.NETWORK_ERROR; httpResponse.code = code; httpResponse.status = false; - httpResponse.message = - this.http.STATUS_CODES[code] || this.stringService.httpNetworkError; + httpResponse.message = this.http.STATUS_CODES[code] || this.stringService.httpNetworkError; return httpResponse; } @@ -227,9 +211,7 @@ class NetworkService { else match = result === expectedValue; httpResponse.status = match; - httpResponse.message = match - ? this.stringService.httpMatchSuccess - : this.stringService.httpMatchFail; + httpResponse.message = match ? this.stringService.httpMatchSuccess : this.stringService.httpMatchFail; return httpResponse; } catch (error) { error.service = this.SERVICE_NAME; @@ -320,9 +302,7 @@ class NetworkService { } const container = docker.getContainer(monitor.url); - const { response, responseTime, error } = await this.timeRequest(() => - container.inspect() - ); + const { response, responseTime, error } = await this.timeRequest(() => container.inspect()); const dockerResponse = { monitorId: monitor._id, @@ -333,8 +313,7 @@ class NetworkService { if (error) { dockerResponse.status = false; dockerResponse.code = error.statusCode || this.NETWORK_ERROR; - dockerResponse.message = - error.reason || "Failed to fetch Docker container information"; + dockerResponse.message = error.reason || "Failed to fetch Docker container information"; return dockerResponse; } dockerResponse.status = response?.State?.Status === "running" ? true : false; diff --git a/server/service/notificationService.js b/server/service/notificationService.js index a6048b8cd..f253cf760 100644 --- a/server/service/notificationService.js +++ b/server/service/notificationService.js @@ -3,14 +3,7 @@ const SERVICE_NAME = "NotificationService"; class NotificationService { static SERVICE_NAME = SERVICE_NAME; - constructor({ - emailService, - db, - logger, - networkService, - stringService, - notificationUtils, - }) { + constructor({ emailService, db, logger, networkService, stringService, notificationUtils }) { this.emailService = emailService; this.db = db; this.logger = logger; @@ -69,20 +62,15 @@ class NotificationService { const alerts = await this.notificationUtils.buildHardwareAlerts(networkResponse); if (alerts.length === 0) return false; - const { subject, html } = await this.notificationUtils.buildHardwareEmail( - networkResponse, - alerts - ); - const content = - await this.notificationUtils.buildHardwareNotificationMessage(alerts); + const { subject, html } = await this.notificationUtils.buildHardwareEmail(networkResponse, alerts); + const content = await this.notificationUtils.buildHardwareNotificationMessage(alerts); const success = await this.notifyAll({ notificationIDs, subject, html, content }); return success; } // Status monitors - const { subject, html } = - await this.notificationUtils.buildStatusEmail(networkResponse); + const { subject, html } = await this.notificationUtils.buildStatusEmail(networkResponse); const content = await this.notificationUtils.buildWebhookMessage(networkResponse); const success = this.notifyAll({ notificationIDs, subject, html, content }); return success; diff --git a/server/service/notificationUtils.js b/server/service/notificationUtils.js index 61c3ec36a..d070a9995 100644 --- a/server/service/notificationUtils.js +++ b/server/service/notificationUtils.js @@ -26,10 +26,7 @@ class NotificationUtils { const date = new Date(timestamp); // Get timezone abbreviation and format the date - const timeZoneAbbr = date - .toLocaleTimeString("en-US", { timeZoneName: "short" }) - .split(" ") - .pop(); + const timeZoneAbbr = date.toLocaleTimeString("en-US", { timeZoneName: "short" }).split(" ").pop(); // Format the date with readable format return ( @@ -50,9 +47,7 @@ class NotificationUtils { }; // Get formatted time - const formattedTime = timestamp - ? formatTime(timestamp) - : formatTime(new Date().getTime()); + const formattedTime = timestamp ? formatTime(timestamp) : formatTime(new Date().getTime()); // Create different messages based on status with extra spacing let messageText; @@ -73,29 +68,15 @@ class NotificationUtils { buildHardwareAlerts = async (networkResponse) => { const monitor = networkResponse?.monitor; const thresholds = networkResponse?.monitor?.thresholds; - const { - usage_cpu: cpuThreshold = -1, - usage_memory: memoryThreshold = -1, - usage_disk: diskThreshold = -1, - } = thresholds; + const { usage_cpu: cpuThreshold = -1, usage_memory: memoryThreshold = -1, usage_disk: diskThreshold = -1 } = thresholds; const metrics = networkResponse?.payload?.data; - const { - cpu: { usage_percent: cpuUsage = -1 } = {}, - memory: { usage_percent: memoryUsage = -1 } = {}, - disk = [], - } = metrics; + const { cpu: { usage_percent: cpuUsage = -1 } = {}, memory: { usage_percent: memoryUsage = -1 } = {}, disk = [] } = metrics; const alerts = { cpu: cpuThreshold !== -1 && cpuUsage > cpuThreshold ? true : false, memory: memoryThreshold !== -1 && memoryUsage > memoryThreshold ? true : false, - disk: - disk?.some( - (d) => - diskThreshold !== -1 && - typeof d?.usage_percent === "number" && - d?.usage_percent > diskThreshold - ) ?? false, + disk: disk?.some((d) => diskThreshold !== -1 && typeof d?.usage_percent === "number" && d?.usage_percent > diskThreshold) ?? false, }; const alertsToSend = []; @@ -110,16 +91,13 @@ class NotificationUtils { monitor[`${type}AlertThreshold`] = monitor.alertThreshold; const formatAlert = { - cpu: () => - `Your current CPU usage (${(cpuUsage * 100).toFixed(0)}%) is above your threshold (${(cpuThreshold * 100).toFixed(0)}%)`, + cpu: () => `Your current CPU usage (${(cpuUsage * 100).toFixed(0)}%) is above your threshold (${(cpuThreshold * 100).toFixed(0)}%)`, memory: () => `Your current memory usage (${(memoryUsage * 100).toFixed(0)}%) is above your threshold (${(memoryThreshold * 100).toFixed(0)}%)`, disk: () => `Your current disk usage: ${disk .map((d, idx) => `(Disk${idx}: ${(d.usage_percent * 100).toFixed(0)}%)`) - .join( - ", " - )} is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`, + .join(", ")} is above your threshold (${(diskThreshold * 100).toFixed(0)}%)`, }; alertsToSend.push(formatAlert[type]()); } diff --git a/server/service/settingsService.js b/server/service/settingsService.js index de035922c..d2c1a17ae 100755 --- a/server/service/settingsService.js +++ b/server/service/settingsService.js @@ -62,14 +62,10 @@ class SettingsService { // Remove any old settings await this.AppSettings.deleteMany({ version: { $exists: false } }); - let settings = await this.AppSettings.findOne({ singleton: true }) - .select("-__v -_id -createdAt -updatedAt -singleton") - .lean(); + let settings = await this.AppSettings.findOne({ singleton: true }).select("-__v -_id -createdAt -updatedAt -singleton").lean(); if (settings === null) { await this.AppSettings.create({}); - settings = await this.AppSettings.findOne({ singleton: true }) - .select("-__v -_id -createdAt -updatedAt -singleton") - .lean(); + settings = await this.AppSettings.findOne({ singleton: true }).select("-__v -_id -createdAt -updatedAt -singleton").lean(); } return settings; } diff --git a/server/service/statusService.js b/server/service/statusService.js index 3d7bb4543..1982bbb30 100755 --- a/server/service/statusService.js +++ b/server/service/statusService.js @@ -48,9 +48,7 @@ class StatusService { if (avgResponseTime === 0) { avgResponseTime = responseTime; } else { - avgResponseTime = - (avgResponseTime * (stats.totalChecks - 1) + responseTime) / - stats.totalChecks; + avgResponseTime = (avgResponseTime * (stats.totalChecks - 1) + responseTime) / stats.totalChecks; } } stats.avgResponseTime = avgResponseTime; @@ -138,9 +136,7 @@ class StatusService { // Monitor status changed, save prev status and update monitor this.logger.info({ service: this.SERVICE_NAME, - message: `${monitor.name} went from ${this.getStatusString( - monitor.status - )} to ${this.getStatusString(status)}`, + message: `${monitor.name} went from ${this.getStatusString(monitor.status)} to ${this.getStatusString(status)}`, prevStatus: monitor.status, newStatus: status, }); @@ -279,9 +275,7 @@ class StatusService { message: error.message, service: error.service || this.SERVICE_NAME, method: error.method || "insertCheck", - details: - error.details || - `Error inserting check for monitor: ${networkResponse?.monitorId}`, + details: error.details || `Error inserting check for monitor: ${networkResponse?.monitorId}`, stack: error.stack, }); } diff --git a/server/service/stringService.js b/server/service/stringService.js index 6f9ad8cd8..b88e73917 100755 --- a/server/service/stringService.js +++ b/server/service/stringService.js @@ -211,23 +211,16 @@ class StringService { } getWebhookUnsupportedPlatform(platform) { - return this.translationService - .getTranslation("webhookUnsupportedPlatform") - .replace("{platform}", platform); + return this.translationService.getTranslation("webhookUnsupportedPlatform").replace("{platform}", platform); } getWebhookSendError(platform) { - return this.translationService - .getTranslation("webhookSendError") - .replace("{platform}", platform); + return this.translationService.getTranslation("webhookSendError").replace("{platform}", platform); } getMonitorStatus(name, status, url) { const translationKey = status === true ? "monitorStatusUp" : "monitorStatusDown"; - return this.translationService - .getTranslation(translationKey) - .replace("{name}", name) - .replace("{url}", url); + return this.translationService.getTranslation(translationKey).replace("{name}", name).replace("{url}", url); } // Error Messages @@ -378,9 +371,7 @@ class StringService { } getDeletedCount(count) { - return this.translationService - .getTranslation("deletedCount") - .replace("{count}", count); + return this.translationService.getTranslation("deletedCount").replace("{count}", count); } get pingSuccess() { @@ -424,9 +415,7 @@ class StringService { } getDbFindMonitorById(monitorId) { - return this.translationService - .getTranslation("dbFindMonitorById") - .replace("${monitorId}", monitorId); + return this.translationService.getTranslation("dbFindMonitorById").replace("${monitorId}", monitorId); } get dbUserExists() { diff --git a/server/service/translationService.js b/server/service/translationService.js index de29b5eb1..38346f35d 100755 --- a/server/service/translationService.js +++ b/server/service/translationService.js @@ -38,9 +38,7 @@ class TranslationService { return false; } - const files = fs - .readdirSync(this.localesDir) - .filter((file) => file.endsWith(".json")); + const files = fs.readdirSync(this.localesDir).filter((file) => file.endsWith(".json")); if (files.length === 0) { return false; diff --git a/server/templates/addReview.mjml b/server/templates/addReview.mjml index 9d00be7d0..e193ac1ba 100755 --- a/server/templates/addReview.mjml +++ b/server/templates/addReview.mjml @@ -30,22 +30,17 @@ -

Hello {{name}}!

+

Hello {{ name }}!

- We hope you’re finding Checkmate helpful in monitoring your infrastructure. - Your support means a lot to us, and we truly appreciate having you as - part of our community. + We hope you’re finding Checkmate helpful in monitoring your infrastructure. Your support means a lot to us, and we + truly appreciate having you as part of our community.

- If you’re happy with Checkmate, we’d love to hear about your experience! - Leaving a review on G2 helps others discover Checkmate and supports our - ongoing improvements. + If you’re happy with Checkmate, we’d love to hear about your experience! Leaving a review on G2 helps others discover Checkmate and + supports our ongoing improvements.

G2 Link: TBD -

- Thank you for taking the time to share your thoughts - we greatly appreciate - it! -

+

Thank you for taking the time to share your thoughts - we greatly appreciate it!

Checkmate Team
diff --git a/server/templates/employeeActivation.mjml b/server/templates/employeeActivation.mjml index 3e35db6e8..317832c1f 100755 --- a/server/templates/employeeActivation.mjml +++ b/server/templates/employeeActivation.mjml @@ -29,10 +29,12 @@ -

Hello {{name}}!

+

Hello {{ name }}!

One of the admins created an account for you on the Checkmate server.

You can go ahead and create your account using this link.

-

{{link}}

+

+ {{ link }} +

Thank you.

diff --git a/server/templates/hardwareIncident.mjml b/server/templates/hardwareIncident.mjml index 19fb589fc..3f50a3d58 100755 --- a/server/templates/hardwareIncident.mjml +++ b/server/templates/hardwareIncident.mjml @@ -47,10 +47,10 @@ -

Hello {{name}}!

-

{{monitor}} at {{url}} has the following infrastructure alerts:

+

Hello {{ name }}!

+

{{ monitor }} at {{ url }} has the following infrastructure alerts:

{{#each alerts}} -

• {{this}}

+

• {{ this }}

{{/each}}
diff --git a/server/templates/noIncidentsThisWeek.mjml b/server/templates/noIncidentsThisWeek.mjml index d31d63d10..d160e44de 100755 --- a/server/templates/noIncidentsThisWeek.mjml +++ b/server/templates/noIncidentsThisWeek.mjml @@ -45,7 +45,7 @@ -

Hello {{name}}!

+

Hello {{ name }}!

There were no incidents this week. Good job!

Current monitors:

Google: 100% availability

diff --git a/server/templates/passwordReset.mjml b/server/templates/passwordReset.mjml index e0b85a4f4..568b363ee 100755 --- a/server/templates/passwordReset.mjml +++ b/server/templates/passwordReset.mjml @@ -31,10 +31,10 @@ -

Hello {{name}}!

+

Hello {{ name }}!

- You are receiving this email because a password reset request has been made - for {{email}}. Please use the link below on the site to reset your password. + You are receiving this email because a password reset request has been made for {{ email }}. Please use the link below on the site to + reset your password.

Reset Password

If you didn't request this, please ignore this email.

diff --git a/server/templates/serverIsDown.mjml b/server/templates/serverIsDown.mjml index b75d970e6..bf6424a6b 100755 --- a/server/templates/serverIsDown.mjml +++ b/server/templates/serverIsDown.mjml @@ -36,7 +36,7 @@ font-size="18px" color="red" > - {{monitor}} is down + {{ monitor }} is down
-

Hello {{name}}!

-

- We detected an incident on one of your monitors. Your service is currently - down. We'll send a message to you once it is up again. -

-

Monitor name: {{monitor}}

-

URL: {{url}}

+

Hello {{ name }}!

+

We detected an incident on one of your monitors. Your service is currently down. We'll send a message to you once it is up again.

+

Monitor name: {{ monitor }}

+

URL: {{ url }}

diff --git a/server/templates/serverIsUp.mjml b/server/templates/serverIsUp.mjml index beb34481d..074c5e3dd 100755 --- a/server/templates/serverIsUp.mjml +++ b/server/templates/serverIsUp.mjml @@ -36,7 +36,7 @@ font-size="18px" color="green" > - {{monitor}} is up + {{ monitor }} is up
-

Hello {{name}}!

+

Hello {{ name }}!

Your latest incident is resolved and your monitored service is up again.

-

Monitor name: {{monitor}}

-

URL: {{url}}

+

Monitor name: {{ monitor }}

+

URL: {{ url }}

diff --git a/server/templates/welcomeEmail.mjml b/server/templates/welcomeEmail.mjml index 663518ec2..e9f569e3f 100755 --- a/server/templates/welcomeEmail.mjml +++ b/server/templates/welcomeEmail.mjml @@ -30,15 +30,9 @@ -

Hello {{name}}!

-

- Thank you for trying out Checkmate! We developed it with great care to meet - our own needs, and we're excited to share it with you. -

-

- Checkmate is an automated way of checking whether a service such as a website - or an application is available or not. -

+

Hello {{ name }}!

+

Thank you for trying out Checkmate! We developed it with great care to meet our own needs, and we're excited to share it with you.

+

Checkmate is an automated way of checking whether a service such as a website or an application is available or not.

We hope you find our service as valuable as we do.

Thank you.

diff --git a/server/tests/controllers/authController.test.js b/server/tests/controllers/authController.test.js index 2990afe22..341c8eda6 100755 --- a/server/tests/controllers/authController.test.js +++ b/server/tests/controllers/authController.test.js @@ -28,9 +28,7 @@ describe("Auth Controller - issueToken", function () { stub = sinon.stub(jwt, "sign").throws(error); const payload = { id: "123" }; const appSettings = { jwtSecret: "my_secret" }; - expect(() => issueToken(payload, tokenType.ACCESS_TOKEN, appSettings)).to.throw( - error - ); + expect(() => issueToken(payload, tokenType.ACCESS_TOKEN, appSettings)).to.throw(error); }); it("should return a token if jwt.sign is successful and appSettings.jwtTTL is not defined", function () { @@ -163,9 +161,7 @@ describe("Auth Controller - registerUser", function () { req.db.checkSuperadmin.resolves(false); req.db.updateAppSettings.resolves(); req.db.insertUser.resolves({ _id: "123" }); - req.settingsService.getSettings.rejects( - new Error("settingsService.getSettings error") - ); + req.settingsService.getSettings.rejects(new Error("settingsService.getSettings error")); await registerUser(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); expect(next.firstCall.args[0].message).to.equal("settingsService.getSettings error"); @@ -304,9 +300,7 @@ describe("Auth Controller - loginUser", function () { user.comparePassword.resolves(false); 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 - ); + expect(next.firstCall.args[0].message).to.equal(errorMessages.AUTH_INCORRECT_PASSWORD); }); }); @@ -370,9 +364,7 @@ describe("Auth Controller - refreshAuthToken", function () { }); it("should reject if settingsService.getSettings fails", async function () { - req.settingsService.getSettings.rejects( - new Error("settingsService.getSettings error") - ); + req.settingsService.getSettings.rejects(new Error("settingsService.getSettings error")); await refreshAuthToken(req, res, next); expect(next.firstCall.args[0]).to.be.an("error"); @@ -475,9 +467,7 @@ describe("Auth Controller - editUser", function () { await editUser(req, res, next); 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 - ); + expect(next.firstCall.args[0].message).to.equal(errorMessages.AUTH_INCORRECT_PASSWORD); }); it("should edit a user if it receives a proper request", async function () { @@ -863,8 +853,7 @@ describe("Auth Controller - deleteUser", function () { ).to.be.true; expect(req.jobQueue.deleteJob.calledOnceWith(monitors[0])).to.be.true; expect(req.db.deleteChecks.calledOnceWith("monitor_id")).to.be.true; - expect(req.db.deletePageSpeedChecksByMonitorId.calledOnceWith("monitor_id")).to.be - .true; + expect(req.db.deletePageSpeedChecksByMonitorId.calledOnceWith("monitor_id")).to.be.true; expect(req.db.deleteNotificationsByMonitorId.calledOnceWith("monitor_id")).to.be.true; expect(req.db.deleteTeam.calledOnceWith("team_id")).to.be.true; expect(req.db.deleteAllOtherUsers.calledOnce).to.be.true; diff --git a/server/tests/controllers/checkController.test.js b/server/tests/controllers/checkController.test.js index 322e6c8ca..c5fb57d50 100755 --- a/server/tests/controllers/checkController.test.js +++ b/server/tests/controllers/checkController.test.js @@ -1,11 +1,4 @@ -import { - createCheck, - getChecks, - getTeamChecks, - deleteChecks, - deleteChecksByTeamId, - updateChecksTTL, -} from "../../controllers/checkController.js"; +import { createCheck, getChecks, getTeamChecks, deleteChecks, deleteChecksByTeamId, updateChecksTTL } from "../../controllers/checkController.js"; import jwt from "jsonwebtoken"; import { errorMessages, successMessages } from "../../utils/messages.js"; import sinon from "sinon"; diff --git a/server/tests/controllers/controllerUtils.test.js b/server/tests/controllers/controllerUtils.test.js index d594e932d..a55318857 100755 --- a/server/tests/controllers/controllerUtils.test.js +++ b/server/tests/controllers/controllerUtils.test.js @@ -1,10 +1,6 @@ import sinon from "sinon"; -import { - handleValidationError, - handleError, - fetchMonitorCertificate, -} from "../../controllers/controllerUtils.js"; +import { handleValidationError, handleError, fetchMonitorCertificate } from "../../controllers/controllerUtils.js"; import { expect } from "chai"; import sslChecker from "ssl-checker"; import { afterEach } from "node:test"; diff --git a/server/tests/controllers/inviteController.test.js b/server/tests/controllers/inviteController.test.js index 3311f715e..635bdf653 100755 --- a/server/tests/controllers/inviteController.test.js +++ b/server/tests/controllers/inviteController.test.js @@ -1,7 +1,4 @@ -import { - issueInvitation, - inviteVerifyController, -} from "../../controllers/inviteController.js"; +import { issueInvitation, inviteVerifyController } from "../../controllers/inviteController.js"; import jwt from "jsonwebtoken"; import sinon from "sinon"; import joi from "joi"; diff --git a/server/tests/controllers/monitorController.test.js b/server/tests/controllers/monitorController.test.js index 5aa68b514..963862579 100755 --- a/server/tests/controllers/monitorController.test.js +++ b/server/tests/controllers/monitorController.test.js @@ -623,9 +623,7 @@ describe("Monitor Controller - deleteMonitor", function () { req.jobQueue.deleteJob.rejects(error); await deleteMonitor(req, res, next); expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` - ); + expect(logger.error.firstCall.args[0].message).to.equal(`Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`); }); it("should log an error if deleteChecks throws an error", async function () { @@ -635,9 +633,7 @@ describe("Monitor Controller - deleteMonitor", function () { req.db.deleteChecks.rejects(error); await deleteMonitor(req, res, next); expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` - ); + expect(logger.error.firstCall.args[0].message).to.equal(`Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`); }); it("should log an error if deletePageSpeedChecksByMonitorId throws an error", async function () { @@ -647,9 +643,7 @@ describe("Monitor Controller - deleteMonitor", function () { req.db.deletePageSpeedChecksByMonitorId.rejects(error); await deleteMonitor(req, res, next); expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` - ); + expect(logger.error.firstCall.args[0].message).to.equal(`Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`); }); it("should log an error if deleteNotificationsByMonitorId throws an error", async function () { @@ -659,9 +653,7 @@ describe("Monitor Controller - deleteMonitor", function () { req.db.deleteNotificationsByMonitorId.rejects(error); await deleteMonitor(req, res, next); expect(logger.error.calledOnce).to.be.true; - expect(logger.error.firstCall.args[0].message).to.equal( - `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}` - ); + expect(logger.error.firstCall.args[0].message).to.equal(`Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`); }); it("should return success message if all operations succeed", async function () { diff --git a/server/tests/controllers/queueController.test.js b/server/tests/controllers/queueController.test.js index 72450a0bd..6201bdb5a 100755 --- a/server/tests/controllers/queueController.test.js +++ b/server/tests/controllers/queueController.test.js @@ -1,10 +1,5 @@ import { afterEach } from "node:test"; -import { - getMetrics, - getJobs, - addJob, - obliterateQueue, -} from "../../controllers/queueController.js"; +import { getMetrics, getJobs, addJob, obliterateQueue } from "../../controllers/queueController.js"; import { successMessages } from "../../utils/messages.js"; import sinon from "sinon"; diff --git a/server/tests/controllers/settingsController.test.js b/server/tests/controllers/settingsController.test.js index 0f8e628af..721c41c02 100755 --- a/server/tests/controllers/settingsController.test.js +++ b/server/tests/controllers/settingsController.test.js @@ -1,8 +1,5 @@ import { afterEach } from "node:test"; -import { - getAppSettings, - updateAppSettings, -} from "../../controllers/settingsController.js"; +import { getAppSettings, updateAppSettings } from "../../controllers/settingsController.js"; import { successMessages } from "../../utils/messages.js"; import sinon from "sinon"; diff --git a/server/tests/controllers/statusPageController.test.js b/server/tests/controllers/statusPageController.test.js index 3247ca975..cada662e0 100755 --- a/server/tests/controllers/statusPageController.test.js +++ b/server/tests/controllers/statusPageController.test.js @@ -1,8 +1,5 @@ import sinon from "sinon"; -import { - createStatusPage, - getStatusPageByUrl, -} from "../../controllers/statusPageController.js"; +import { createStatusPage, getStatusPageByUrl } from "../../controllers/statusPageController.js"; describe("statusPageController", function () { let req, res, next; diff --git a/server/tests/db/checkModule.test.js b/server/tests/db/checkModule.test.js index 7ef2f5b77..6eef12445 100755 --- a/server/tests/db/checkModule.test.js +++ b/server/tests/db/checkModule.test.js @@ -216,11 +216,7 @@ describe("checkModule", function () { const result = await getChecksCount(req); expect(result).to.equal(4); - expect(checkCountDocumentStub.firstCall.args[0]).to.have.all.keys( - "monitorId", - "createdAt", - "status" - ); + expect(checkCountDocumentStub.firstCall.args[0]).to.have.all.keys("monitorId", "createdAt", "status"); }); }); diff --git a/server/tests/db/hardwareCheckModule.test.js b/server/tests/db/hardwareCheckModule.test.js index 566c45a8e..20ce197fb 100755 --- a/server/tests/db/hardwareCheckModule.test.js +++ b/server/tests/db/hardwareCheckModule.test.js @@ -52,10 +52,7 @@ const mockMonitor = { }; describe("HardwareCheckModule", function () { - let hardwareCheckSaveStub, - hardwareCheckCountDocumentsStub, - monitorFindByIdStub, - loggerStub; + let hardwareCheckSaveStub, hardwareCheckCountDocumentsStub, monitorFindByIdStub, loggerStub; beforeEach(function () { loggerStub = sinon.stub(logger, "error"); diff --git a/server/tests/db/inviteModule.test.js b/server/tests/db/inviteModule.test.js index 9b3aab9cc..e8913c983 100755 --- a/server/tests/db/inviteModule.test.js +++ b/server/tests/db/inviteModule.test.js @@ -1,10 +1,6 @@ import sinon from "sinon"; import InviteToken from "../../db/models/InviteToken.js"; -import { - requestInviteToken, - getInviteToken, - getInviteTokenAndDelete, -} from "../../db/mongo/modules/inviteModule.js"; +import { requestInviteToken, getInviteToken, getInviteTokenAndDelete } from "../../db/mongo/modules/inviteModule.js"; import { errorMessages } from "../../utils/messages.js"; describe("Invite Module", function () { @@ -15,10 +11,7 @@ describe("Invite Module", function () { token: "123", }; const mockInviteToken = { _id: 123, time: 123 }; - let inviteTokenDeleteManyStub, - inviteTokenSaveStub, - inviteTokenFindOneStub, - inviteTokenFindOneAndDeleteStub; + let inviteTokenDeleteManyStub, inviteTokenSaveStub, inviteTokenFindOneStub, inviteTokenFindOneAndDeleteStub; beforeEach(function () { inviteTokenDeleteManyStub = sinon.stub(InviteToken, "deleteMany"); diff --git a/server/tests/db/maintenanceWindowModule.test.js b/server/tests/db/maintenanceWindowModule.test.js index 842c283c8..576c97f84 100755 --- a/server/tests/db/maintenanceWindowModule.test.js +++ b/server/tests/db/maintenanceWindowModule.test.js @@ -40,15 +40,9 @@ describe("MaintenanceWindow Module", function () { }), }), }); - maintenanceWindowFindByIdAndDeleteStub = sinon.stub( - MaintenanceWindow, - "findByIdAndDelete" - ); + maintenanceWindowFindByIdAndDeleteStub = sinon.stub(MaintenanceWindow, "findByIdAndDelete"); maintenanceWindowDeleteManyStub = sinon.stub(MaintenanceWindow, "deleteMany"); - maintenanceWindowFindByIdAndUpdateStub = sinon.stub( - MaintenanceWindow, - "findByIdAndUpdate" - ); + maintenanceWindowFindByIdAndUpdateStub = sinon.stub(MaintenanceWindow, "findByIdAndUpdate"); }); afterEach(function () { @@ -110,10 +104,7 @@ describe("MaintenanceWindow Module", function () { it("should return a list of maintenance windows and count", async function () { maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId( - mockMaintenanceWindow.teamId, - query - ); + const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); expect(result).to.deep.equal({ maintenanceWindows: mockMaintenanceWindows, maintenanceWindowCount: 1, @@ -123,10 +114,7 @@ describe("MaintenanceWindow Module", function () { it("should return a list of maintenance windows and count with empty query", async function () { query = undefined; maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId( - mockMaintenanceWindow.teamId, - query - ); + const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); expect(result).to.deep.equal({ maintenanceWindows: mockMaintenanceWindows, maintenanceWindowCount: 1, @@ -137,10 +125,7 @@ describe("MaintenanceWindow Module", function () { query.page = undefined; query.rowsPerPage = undefined; maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId( - mockMaintenanceWindow.teamId, - query - ); + const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); expect(result).to.deep.equal({ maintenanceWindows: mockMaintenanceWindows, maintenanceWindowCount: 1, @@ -150,10 +135,7 @@ describe("MaintenanceWindow Module", function () { it("should return a list of maintenance windows and count with field and desc order", async function () { query.order = "desc"; maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId( - mockMaintenanceWindow.teamId, - query - ); + const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); expect(result).to.deep.equal({ maintenanceWindows: mockMaintenanceWindows, maintenanceWindowCount: 1, @@ -163,10 +145,7 @@ describe("MaintenanceWindow Module", function () { it("should return a list of maintenance windows and count no field", async function () { query.field = undefined; maintenanceWindowCountDocumentsStub.resolves(1); - const result = await getMaintenanceWindowsByTeamId( - mockMaintenanceWindow.teamId, - query - ); + const result = await getMaintenanceWindowsByTeamId(mockMaintenanceWindow.teamId, query); expect(result).to.deep.equal({ maintenanceWindows: mockMaintenanceWindows, maintenanceWindowCount: 1, @@ -187,9 +166,7 @@ describe("MaintenanceWindow Module", function () { describe("getMaintenanceWindowsByMonitorId", function () { it("should return a list of maintenance windows", async function () { maintenanceWindowFindStub.resolves(mockMaintenanceWindows); - const result = await getMaintenanceWindowsByMonitorId( - mockMaintenanceWindow.monitorId - ); + const result = await getMaintenanceWindowsByMonitorId(mockMaintenanceWindow.monitorId); expect(result).to.deep.equal(mockMaintenanceWindows); }); @@ -225,9 +202,7 @@ describe("MaintenanceWindow Module", function () { describe("deleteMaintenanceWindowByMonitorId", function () { it("should return the number of documents deleted", async function () { maintenanceWindowDeleteManyStub.resolves({ deletedCount: 1 }); - const result = await deleteMaintenanceWindowByMonitorId( - mockMaintenanceWindow.monitorId - ); + const result = await deleteMaintenanceWindowByMonitorId(mockMaintenanceWindow.monitorId); expect(result).to.deep.equal({ deletedCount: 1 }); }); @@ -263,10 +238,7 @@ describe("MaintenanceWindow Module", function () { describe("editMaintenanceWindowById", function () { it("should return the updated maintenance window", async function () { maintenanceWindowFindByIdAndUpdateStub.resolves(mockMaintenanceWindow); - const result = await editMaintenanceWindowById( - mockMaintenanceWindow.id, - mockMaintenanceWindow - ); + const result = await editMaintenanceWindowById(mockMaintenanceWindow.id, mockMaintenanceWindow); expect(result).to.deep.equal(mockMaintenanceWindow); }); diff --git a/server/tests/db/monitorModule.test.js b/server/tests/db/monitorModule.test.js index 3d9bb5324..e59c352cf 100755 --- a/server/tests/db/monitorModule.test.js +++ b/server/tests/db/monitorModule.test.js @@ -120,12 +120,7 @@ describe("monitorModule", function () { }, ]; - const mockChecks = [ - { status: true }, - { status: true }, - { status: false }, - { status: true }, - ]; + const mockChecks = [{ status: true }, { status: true }, { status: false }, { status: true }]; monitorFindStub.resolves(mockMonitors); checkFindStub.resolves(mockChecks); @@ -166,12 +161,7 @@ describe("monitorModule", function () { }, ]; - const mockChecks = [ - { status: true }, - { status: true }, - { status: false }, - { status: true }, - ]; + const mockChecks = [{ status: true }, { status: true }, { status: false }, { status: true }]; monitorFindStub.resolves(mockMonitors); pageSpeedCheckFindStub.resolves(mockChecks); @@ -212,12 +202,7 @@ describe("monitorModule", function () { }, ]; - const mockChecks = [ - { status: true }, - { status: true }, - { status: false }, - { status: true }, - ]; + const mockChecks = [{ status: true }, { status: true }, { status: false }, { status: true }]; monitorFindStub.resolves(mockMonitors); hardwareCheckFindStub.resolves(mockChecks); @@ -400,10 +385,7 @@ describe("monitorModule", 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" }, - ]; + const checks = [{ createdAt: "2024-01-01T11:30:00Z" }, { responseTime: 200, createdAt: "2024-01-01T11:00:00Z" }]; expect(getLatestResponseTime(checks)).to.equal(0); }); @@ -441,10 +423,7 @@ describe("monitorModule", 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" }, - ]; + const checks = [{ createdAt: "2024-01-01T11:30:00Z" }, { createdAt: "2024-01-01T11:00:00Z" }]; expect(getAverageResponseTime(checks)).to.equal(0); }); @@ -470,12 +449,7 @@ describe("monitorModule", function () { }); it("should calculate correct percentage for mixed status checks", function () { - const checks = [ - { status: true }, - { status: false }, - { status: true }, - { status: true }, - ]; + const checks = [{ status: true }, { status: false }, { status: true }, { status: true }]; // 3 up out of 4 total = 75% expect(getUptimePercentage(checks)).to.equal(75); }); @@ -507,23 +481,12 @@ describe("monitorModule", function () { }); it("should count correct number of incidents for mixed status checks", function () { - const checks = [ - { status: true }, - { status: false }, - { status: true }, - { status: false }, - { status: true }, - ]; + const checks = [{ status: true }, { status: false }, { status: true }, { status: false }, { status: true }]; expect(getIncidents(checks)).to.equal(2); }); it("should handle undefined status values", function () { - const checks = [ - { status: true }, - { status: undefined }, - { status: false }, - { status: false }, - ]; + const checks = [{ status: true }, { status: undefined }, { status: false }, { status: false }]; // Only counts explicit false values expect(getIncidents(checks)).to.equal(2); }); @@ -628,12 +591,8 @@ describe("monitorModule", function () { ); // Assert - expect(result.checksAll[0].createdAt).to.be.greaterThan( - result.checksAll[1].createdAt - ); - expect(result.checksForDateRange[0].createdAt).to.be.greaterThan( - result.checksForDateRange[1].createdAt - ); + expect(result.checksAll[0].createdAt).to.be.greaterThan(result.checksAll[1].createdAt); + expect(result.checksForDateRange[0].createdAt).to.be.greaterThan(result.checksForDateRange[1].createdAt); }); }); @@ -753,10 +712,7 @@ describe("monitorModule", function () { }); it("should handle checks in same time group", function () { - const checksInSameHour = [ - { createdAt: "2024-01-15T10:15:00Z" }, - { createdAt: "2024-01-15T10:45:00Z" }, - ]; + const checksInSameHour = [{ createdAt: "2024-01-15T10:15:00Z" }, { createdAt: "2024-01-15T10:45:00Z" }]; const result = groupChecksByTime(checksInSameHour, "day"); @@ -816,11 +772,7 @@ describe("monitorModule", function () { it("should handle missing responseTime values", function () { const mockGroup = { time: "2024-01-15", - checks: [ - { status: true }, - { status: false, responseTime: 200 }, - { status: true, responseTime: undefined }, - ], + checks: [{ status: true }, { status: false, responseTime: 200 }, { status: true, responseTime: undefined }], }; const result = calculateGroupStats(mockGroup, uptimePercentageStub); @@ -1413,10 +1365,7 @@ describe("monitorModule", function () { expect(Monitor.find.firstCall.args[0]).to.deep.equal({ teamId: "team123", - $or: [ - { name: { $regex: "search", $options: "i" } }, - { url: { $regex: "search", $options: "i" } }, - ], + $or: [{ name: { $regex: "search", $options: "i" } }, { url: { $regex: "search", $options: "i" } }], }); }); @@ -1816,14 +1765,9 @@ describe("monitorModule", function () { // Assert expect(result).to.deep.equal(mockUpdatedMonitor); - sinon.assert.calledWith( - monitorFindByIdAndUpdateStub, - candidateId, - expectedUpdateData, - { - new: true, - } - ); + sinon.assert.calledWith(monitorFindByIdAndUpdateStub, candidateId, expectedUpdateData, { + new: true, + }); }); it("should return null when monitor not found", async function () { @@ -1840,12 +1784,7 @@ describe("monitorModule", function () { // Assert expect(result).to.be.null; - sinon.assert.calledWith( - monitorFindByIdAndUpdateStub, - candidateId, - { name: "Updated Monitor", notifications: undefined }, - { new: true } - ); + sinon.assert.calledWith(monitorFindByIdAndUpdateStub, candidateId, { name: "Updated Monitor", notifications: undefined }, { new: true }); }); it("should remove notifications from update data", async function () { @@ -1870,14 +1809,9 @@ describe("monitorModule", function () { await editMonitor(candidateId, candidateMonitor); // Assert - sinon.assert.calledWith( - monitorFindByIdAndUpdateStub, - candidateId, - expectedUpdateData, - { - new: true, - } - ); + sinon.assert.calledWith(monitorFindByIdAndUpdateStub, candidateId, expectedUpdateData, { + new: true, + }); }); it("should handle database errors", async function () { diff --git a/server/tests/db/notificationModule.test.js b/server/tests/db/notificationModule.test.js index 3128130f8..fbb97f33a 100755 --- a/server/tests/db/notificationModule.test.js +++ b/server/tests/db/notificationModule.test.js @@ -1,10 +1,6 @@ import sinon from "sinon"; import Notification from "../../db/models/Notification.js"; -import { - createNotification, - getNotificationsByMonitorId, - deleteNotificationsByMonitorId, -} from "../../db/mongo/modules/notificationModule.js"; +import { createNotification, getNotificationsByMonitorId, deleteNotificationsByMonitorId } from "../../db/mongo/modules/notificationModule.js"; describe("notificationModule", function () { const mockNotification = { diff --git a/server/tests/db/pageSpeedCheckModule.test.js b/server/tests/db/pageSpeedCheckModule.test.js index 5ef0e1845..45135135d 100755 --- a/server/tests/db/pageSpeedCheckModule.test.js +++ b/server/tests/db/pageSpeedCheckModule.test.js @@ -1,9 +1,6 @@ import sinon from "sinon"; import PageSpeedCheck from "../../db/models/PageSpeedCheck.js"; -import { - createPageSpeedCheck, - deletePageSpeedChecksByMonitorId, -} from "../../db/mongo/modules/pageSpeedCheckModule.js"; +import { createPageSpeedCheck, deletePageSpeedChecksByMonitorId } from "../../db/mongo/modules/pageSpeedCheckModule.js"; const mockPageSpeedCheck = { monitorId: "monitorId", diff --git a/server/tests/db/recoveryModule.test.js b/server/tests/db/recoveryModule.test.js index 3f4e11c5c..88afa257a 100755 --- a/server/tests/db/recoveryModule.test.js +++ b/server/tests/db/recoveryModule.test.js @@ -1,11 +1,7 @@ import sinon from "sinon"; import RecoveryToken from "../../db/models/RecoveryToken.js"; import User from "../../db/models/User.js"; -import { - requestRecoveryToken, - validateRecoveryToken, - resetPassword, -} from "../../db/mongo/modules/recoveryModule.js"; +import { requestRecoveryToken, validateRecoveryToken, resetPassword } from "../../db/mongo/modules/recoveryModule.js"; import { errorMessages } from "../../utils/messages.js"; const mockRecoveryToken = { @@ -44,12 +40,7 @@ const createQueryChain = (finalResult, comparePasswordResult = false) => ({ }); describe("recoveryModule", function () { - let deleteManyStub, - saveStub, - findOneStub, - userCompareStub, - userSaveStub, - userFindOneStub; + let deleteManyStub, saveStub, findOneStub, userCompareStub, userSaveStub, userFindOneStub; let req, res; beforeEach(function () { @@ -153,9 +144,7 @@ describe("recoveryModule", function () { it("should throw an error if the passwords match", async function () { findOneStub.resolves(mockRecoveryToken); saveStub.resolves(); - userFindOneStub = sinon - .stub(User, "findOne") - .returns(createQueryChain(mockUser, true)); + userFindOneStub = sinon.stub(User, "findOne").returns(createQueryChain(mockUser, true)); try { await resetPassword(req, res); } catch (error) { diff --git a/server/tests/db/settingsModule.test.js b/server/tests/db/settingsModule.test.js index 7651adb4b..9d1a80c3e 100755 --- a/server/tests/db/settingsModule.test.js +++ b/server/tests/db/settingsModule.test.js @@ -1,8 +1,5 @@ import sinon from "sinon"; -import { - getAppSettings, - updateAppSettings, -} from "../../db/mongo/modules/settingsModule.js"; +import { getAppSettings, updateAppSettings } from "../../db/mongo/modules/settingsModule.js"; import AppSettings from "../../db/models/AppSettings.js"; const mockAppSettings = { diff --git a/server/tests/db/statusPageModule.test.js b/server/tests/db/statusPageModule.test.js index 60c356aa5..54e1a287f 100755 --- a/server/tests/db/statusPageModule.test.js +++ b/server/tests/db/statusPageModule.test.js @@ -1,8 +1,5 @@ import sinon from "sinon"; -import { - createStatusPage, - getStatusPageByUrl, -} from "../../db/mongo/modules/statusPageModule.js"; +import { createStatusPage, getStatusPageByUrl } from "../../db/mongo/modules/statusPageModule.js"; import StatusPage from "../../db/models/StatusPage.js"; import { errorMessages } from "../../utils/messages.js"; diff --git a/server/tests/db/userModule.test.js b/server/tests/db/userModule.test.js index 884956995..d0f5ecb3e 100755 --- a/server/tests/db/userModule.test.js +++ b/server/tests/db/userModule.test.js @@ -157,12 +157,7 @@ describe("userModule", function () { select: sinon.stub().resolves(mockUser), }), }); - const result = await updateUser( - req, - res, - parseBooleanStub, - generateAvatarImageStub - ); + const result = await updateUser(req, res, parseBooleanStub, generateAvatarImageStub); expect(result).to.deep.equal(mockUser); }); @@ -173,12 +168,7 @@ describe("userModule", function () { select: sinon.stub().resolves(mockUser), }), }); - const result = await updateUser( - req, - res, - parseBooleanStub, - generateAvatarImageStub - ); + const result = await updateUser(req, res, parseBooleanStub, generateAvatarImageStub); expect(result).to.deep.equal(mockUser); }); diff --git a/server/tests/services/emailService.test.js b/server/tests/services/emailService.test.js index 541789d99..cca86896a 100755 --- a/server/tests/services/emailService.test.js +++ b/server/tests/services/emailService.test.js @@ -48,15 +48,7 @@ describe("EmailService - Constructor", function () { }); it("should initialize template loaders and email transporter", function () { - const emailService = new EmailService( - settingsServiceMock, - fsMock, - pathMock, - compileMock, - mjml2htmlMock, - nodemailerMock, - loggerMock - ); + const emailService = new EmailService(settingsServiceMock, fsMock, pathMock, compileMock, mjml2htmlMock, nodemailerMock, loggerMock); // Verify that the settingsService is assigned correctly expect(emailService.settingsService).to.equal(settingsServiceMock); @@ -83,15 +75,7 @@ describe("EmailService - Constructor", function () { fsMock = { readFileSync: sinon.stub().throws(new Error("File read error")), }; - const emailService = new EmailService( - settingsServiceMock, - fsMock, - pathMock, - compileMock, - mjml2htmlMock, - nodemailerMock, - loggerMock - ); + const emailService = new EmailService(settingsServiceMock, fsMock, pathMock, compileMock, mjml2htmlMock, nodemailerMock, loggerMock); expect(loggerMock.error.called).to.be.true; expect(loggerMock.error.firstCall.args[0].message).to.equal("File read error"); }); @@ -139,15 +123,7 @@ describe("EmailService - buildAndSendEmail", function () { error: sinon.stub(), }; - emailService = new EmailService( - settingsServiceMock, - fsMock, - pathMock, - compileMock, - mjml2htmlMock, - nodemailerMock, - loggerMock - ); + emailService = new EmailService(settingsServiceMock, fsMock, pathMock, compileMock, mjml2htmlMock, nodemailerMock, loggerMock); }); afterEach(function () { @@ -155,12 +131,7 @@ describe("EmailService - buildAndSendEmail", function () { }); it("should build and send email successfully", async function () { - const messageId = await emailService.buildAndSendEmail( - "welcomeEmailTemplate", - {}, - "recipient@example.com", - "Welcome" - ); + const messageId = await emailService.buildAndSendEmail("welcomeEmailTemplate", {}, "recipient@example.com", "Welcome"); expect(messageId).to.equal("12345"); expect(nodemailerMock.createTransport().sendMail.calledOnce).to.be.true; @@ -169,24 +140,14 @@ describe("EmailService - buildAndSendEmail", function () { it("should log error if building HTML fails", async function () { mjml2htmlMock.throws(new Error("MJML error")); - const messageId = await emailService.buildAndSendEmail( - "welcomeEmailTemplate", - {}, - "recipient@example.com", - "Welcome" - ); + const messageId = await emailService.buildAndSendEmail("welcomeEmailTemplate", {}, "recipient@example.com", "Welcome"); expect(loggerMock.error.calledOnce).to.be.true; expect(loggerMock.error.getCall(0).args[0].message).to.equal("MJML error"); }); it("should log error if sending email fails", async function () { nodemailerMock.createTransport().sendMail.rejects(new Error("SMTP error")); - await emailService.buildAndSendEmail( - "welcomeEmailTemplate", - {}, - "recipient@example.com", - "Welcome" - ); + await emailService.buildAndSendEmail("welcomeEmailTemplate", {}, "recipient@example.com", "Welcome"); expect(loggerMock.error.calledOnce).to.be.true; expect(loggerMock.error.getCall(0).args[0].message).to.equal("SMTP error"); }); @@ -195,12 +156,7 @@ describe("EmailService - buildAndSendEmail", function () { mjml2htmlMock.throws(new Error("MJML error")); nodemailerMock.createTransport().sendMail.rejects(new Error("SMTP error")); - const messageId = await emailService.buildAndSendEmail( - "welcomeEmailTemplate", - {}, - "recipient@example.com", - "Welcome" - ); + const messageId = await emailService.buildAndSendEmail("welcomeEmailTemplate", {}, "recipient@example.com", "Welcome"); expect(messageId).to.be.undefined; expect(loggerMock.error.calledTwice).to.be.true; diff --git a/server/tests/services/jobQueue.test.js b/server/tests/services/jobQueue.test.js index 88d57f339..155f9119e 100755 --- a/server/tests/services/jobQueue.test.js +++ b/server/tests/services/jobQueue.test.js @@ -52,13 +52,7 @@ class WorkerStub { } describe("JobQueue", function () { - let settingsService, - logger, - db, - networkService, - statusService, - notificationService, - jobQueue; + let settingsService, logger, db, networkService, statusService, notificationService, jobQueue; beforeEach(async function () { settingsService = { getSettings: sinon.stub() }; @@ -71,16 +65,7 @@ describe("JobQueue", function () { getMaintenanceWindowsByMonitorId: sinon.stub().returns([]), }; networkService = { getStatus: sinon.stub() }; - jobQueue = await JobQueue.createJobQueue( - db, - networkService, - statusService, - notificationService, - settingsService, - logger, - QueueStub, - WorkerStub - ); + jobQueue = await JobQueue.createJobQueue(db, networkService, statusService, notificationService, settingsService, logger, QueueStub, WorkerStub); }); afterEach(function () { @@ -327,9 +312,7 @@ describe("JobQueue", function () { const handler = jobQueue.createJobHandler(); await handler({ data: { _id: 1 } }); expect(logger.info.calledOnce).to.be.true; - expect(logger.info.firstCall.args[0].message).to.equal( - "Monitor 1 is in maintenance window" - ); + expect(logger.info.firstCall.args[0].message).to.equal("Monitor 1 is in maintenance window"); }); it("should return if status has not changed", async function () { @@ -362,9 +345,7 @@ describe("JobQueue", function () { WorkerStub ); jobQueue.isInMaintenanceWindow = sinon.stub().returns(false); - statusService.updateStatus = sinon - .stub() - .returns({ statusChanged: true, prevStatus: undefined }); + statusService.updateStatus = sinon.stub().returns({ statusChanged: true, prevStatus: undefined }); const handler = jobQueue.createJobHandler(); await handler({ data: { _id: 1 } }); expect(jobQueue.notificationService.handleNotifications.notCalled).to.be.true; @@ -382,9 +363,7 @@ describe("JobQueue", function () { WorkerStub ); jobQueue.isInMaintenanceWindow = sinon.stub().returns(false); - statusService.updateStatus = sinon - .stub() - .returns({ statusChanged: true, prevStatus: false }); + statusService.updateStatus = sinon.stub().returns({ statusChanged: true, prevStatus: false }); const handler = jobQueue.createJobHandler(); await handler({ data: { _id: 1 } }); expect(jobQueue.notificationService.handleNotifications.calledOnce).to.be.true; diff --git a/server/tests/services/networkService.test.js b/server/tests/services/networkService.test.js index 44ddf8576..cb48ed9b2 100755 --- a/server/tests/services/networkService.test.js +++ b/server/tests/services/networkService.test.js @@ -26,9 +26,7 @@ describe("Network Service", function () { }; ping = { promise: { - probe: sinon - .stub() - .resolves({ response: { alive: true }, responseTime: 100, alive: true }), + probe: sinon.stub().resolves({ response: { alive: true }, responseTime: 100, alive: true }), }, }; logger = { error: sinon.stub() }; @@ -73,9 +71,7 @@ describe("Network Service", function () { it("should return a response object if ping unsuccessful", async function () { const error = new Error("Test error"); - networkService.timeRequest = sinon - .stub() - .resolves({ response: null, responseTime: 1, error }); + networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); const pingResult = await networkService.requestPing({ data: { url: "http://test.com", _id: "123" }, }); @@ -113,9 +109,7 @@ describe("Network Service", 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 - .stub() - .resolves({ response: null, responseTime: 1, error }); + networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; const httpResult = await networkService.requestHttp(job); expect(httpResult.monitorId).to.equal("123"); @@ -128,9 +122,7 @@ describe("Network Service", 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 - .stub() - .resolves({ response: null, responseTime: 1, error }); + networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); const job = { data: { url: "http://test.com", _id: "123", type: "http" } }; const httpResult = await networkService.requestHttp(job); expect(httpResult.monitorId).to.equal("123"); @@ -167,9 +159,7 @@ describe("Network Service", 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 - .stub() - .resolves({ response: null, responseTime: 1, error }); + networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; const pagespeedResult = await networkService.requestPagespeed(job); expect(pagespeedResult.monitorId).to.equal("123"); @@ -182,9 +172,7 @@ describe("Network Service", 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 - .stub() - .resolves({ response: null, responseTime: 1, error }); + networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); const job = { data: { url: "http://test.com", _id: "123", type: "pagespeed" } }; const pagespeedResult = await networkService.requestPagespeed(job); expect(pagespeedResult.monitorId).to.equal("123"); @@ -237,9 +225,7 @@ describe("Network Service", 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 - .stub() - .resolves({ response: null, responseTime: 1, error }); + networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; const httpResult = await networkService.requestHardware(job); expect(httpResult.monitorId).to.equal("123"); @@ -252,9 +238,7 @@ describe("Network Service", 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 - .stub() - .resolves({ response: null, responseTime: 1, error }); + networkService.timeRequest = sinon.stub().resolves({ response: null, responseTime: 1, error }); const job = { data: { url: "http://test.com", _id: "123", type: "hardware" } }; const httpResult = await networkService.requestHardware(job); expect(httpResult.monitorId).to.equal("123"); diff --git a/server/tests/services/notificationService.test.js b/server/tests/services/notificationService.test.js index 207b40db0..211cc8b46 100755 --- a/server/tests/services/notificationService.test.js +++ b/server/tests/services/notificationService.test.js @@ -43,10 +43,7 @@ describe("NotificationService", function () { await notificationService.sendEmail(networkResponse, address); expect(notificationService.emailService.buildAndSendEmail.calledOnce).to.be.true; expect( - notificationService.emailService.buildAndSendEmail.calledWith( - "serverIsUpTemplate", - { monitor: "Test Monitor", url: "http://test.com" } - ) + notificationService.emailService.buildAndSendEmail.calledWith("serverIsUpTemplate", { monitor: "Test Monitor", url: "http://test.com" }) ); }); @@ -132,22 +129,14 @@ describe("NotificationService", function () { it("should send an email notification with Hardware Template", async function () { emailService.buildAndSendEmail.resolves(true); - const res = await notificationService.sendHardwareEmail( - networkResponse, - address, - alerts - ); + const res = await notificationService.sendHardwareEmail(networkResponse, address, alerts); expect(res).to.be.true; }); it("should return false if no alerts are provided", async function () { alerts = []; emailService.buildAndSendEmail.resolves(true); - const res = await notificationService.sendHardwareEmail( - networkResponse, - address, - alerts - ); + const res = await notificationService.sendHardwareEmail(networkResponse, address, alerts); expect(res).to.be.false; }); }); @@ -172,9 +161,7 @@ describe("NotificationService", function () { }); it("should handle status notifications", async function () { - db.getNotificationsByMonitorId.resolves([ - { type: "email", address: "test@test.com" }, - ]); + db.getNotificationsByMonitorId.resolves([{ type: "email", address: "test@test.com" }]); const res = await notificationService.handleStatusNotifications(networkResponse); expect(res).to.be.true; }); @@ -247,15 +234,13 @@ describe("NotificationService", function () { describe("it should return false if no thresholds are set", function () { it("should return false if no thresholds are set", async function () { networkResponse.monitor.thresholds = undefined; - const res = - await notificationService.handleHardwareNotifications(networkResponse); + const res = await notificationService.handleHardwareNotifications(networkResponse); expect(res).to.be.false; }); it("should return false if metrics are null", async function () { networkResponse.payload.data = null; - const res = - await notificationService.handleHardwareNotifications(networkResponse); + const res = await notificationService.handleHardwareNotifications(networkResponse); expect(res).to.be.false; }); @@ -271,8 +256,7 @@ describe("NotificationService", function () { save: sinon.stub().resolves(), }, ]); - const res = - await notificationService.handleHardwareNotifications(networkResponse); + const res = await notificationService.handleHardwareNotifications(networkResponse); expect(res).to.be.true; }); @@ -293,8 +277,7 @@ describe("NotificationService", function () { usage_memory: 0.01, usage_disk: 0.01, }; - const res = - await notificationService.handleHardwareNotifications(networkResponse); + const res = await notificationService.handleHardwareNotifications(networkResponse); expect(res).to.be.true; }); }); diff --git a/server/tests/services/settingsService.test.js b/server/tests/services/settingsService.test.js index 3e04ced38..6c9960e5e 100755 --- a/server/tests/services/settingsService.test.js +++ b/server/tests/services/settingsService.test.js @@ -13,9 +13,7 @@ describe("SettingsService", function () { sandbox.stub(process.env, "JWT_SECRET").value("secret"); sandbox.stub(process.env, "REFRESH_TOKEN_SECRET").value("refreshSecret"); sandbox.stub(process.env, "DB_TYPE").value("postgres"); - sandbox - .stub(process.env, "DB_CONNECTION_STRING") - .value("postgres://user:pass@localhost/db"); + sandbox.stub(process.env, "DB_CONNECTION_STRING").value("postgres://user:pass@localhost/db"); sandbox.stub(process.env, "REDIS_HOST").value("localhost"); sandbox.stub(process.env, "REDIS_PORT").value("6379"); sandbox.stub(process.env, "TOKEN_TTL").value("3600"); diff --git a/server/tests/services/statusService.test.js b/server/tests/services/statusService.test.js index aa8865024..289a36884 100755 --- a/server/tests/services/statusService.test.js +++ b/server/tests/services/statusService.test.js @@ -73,9 +73,7 @@ describe("StatusService", () => { }); it("should return {statusChanged: true} if status has changed from down to up", async function () { - statusService.db.getMonitorById = sinon - .stub() - .returns({ status: false, save: sinon.stub() }); + statusService.db.getMonitorById = sinon.stub().returns({ status: false, save: sinon.stub() }); const result = await statusService.updateStatus({ monitorId: "test", status: true, @@ -87,9 +85,7 @@ describe("StatusService", () => { }); it("should return {statusChanged: true} if status has changed from up to down", async function () { - statusService.db.getMonitorById = sinon - .stub() - .returns({ status: true, save: sinon.stub() }); + statusService.db.getMonitorById = sinon.stub().returns({ status: true, save: sinon.stub() }); const result = await statusService.updateStatus({ monitorId: "test", status: false, diff --git a/server/tests/utils/dataUtils.test.js b/server/tests/utils/dataUtils.test.js index e39af5a75..ec9eabb40 100755 --- a/server/tests/utils/dataUtils.test.js +++ b/server/tests/utils/dataUtils.test.js @@ -51,13 +51,7 @@ describe("NormalizeData", function () { describe("calculatePercentile", function () { it("should return the lower value when upper is greater than or equal to the length of the sorted array", function () { - const checks = [ - { responseTime: 10 }, - { responseTime: 20 }, - { responseTime: 30 }, - { responseTime: 40 }, - { responseTime: 50 }, - ]; + const checks = [{ responseTime: 10 }, { responseTime: 20 }, { responseTime: 30 }, { responseTime: 40 }, { responseTime: 50 }]; const percentile = 100; const result = calculatePercentile(checks, percentile); diff --git a/server/tests/utils/imageProcessing.test.js b/server/tests/utils/imageProcessing.test.js index 29e47af6a..09330c317 100755 --- a/server/tests/utils/imageProcessing.test.js +++ b/server/tests/utils/imageProcessing.test.js @@ -12,9 +12,7 @@ describe("imageProcessing - GenerateAvatarImage", function () { // Stub the sharp function const toBufferStub = sinon.stub().resolves(Buffer.from("resized image buffer")); const resizeStub = sinon.stub().returns({ toBuffer: toBufferStub }); - const sharpStub = sinon - .stub(sharp.prototype, "resize") - .returns({ toBuffer: toBufferStub }); + const sharpStub = sinon.stub(sharp.prototype, "resize").returns({ toBuffer: toBufferStub }); const result = await GenerateAvatarImage(file); @@ -38,9 +36,7 @@ describe("imageProcessing - GenerateAvatarImage", function () { // Stub the sharp function to throw an error const toBufferStub = sinon.stub().rejects(new Error("Resizing failed")); const resizeStub = sinon.stub().returns({ toBuffer: toBufferStub }); - const sharpStub = sinon - .stub(sharp.prototype, "resize") - .returns({ toBuffer: toBufferStub }); + const sharpStub = sinon.stub(sharp.prototype, "resize").returns({ toBuffer: toBufferStub }); try { await GenerateAvatarImage(file); diff --git a/server/tests/utils/messages.test.js b/server/tests/utils/messages.test.js index 719af1be0..a7e98c477 100755 --- a/server/tests/utils/messages.test.js +++ b/server/tests/utils/messages.test.js @@ -3,25 +3,19 @@ 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( - `Monitor with id ${monitorId} not found` - ); + 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 () { const monitorId = "12345"; - expect(errorMessages.DB_DELETE_CHECKS(monitorId)).to.equal( - `No checks found for monitor with id ${monitorId}` - ); + 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 () { const userId = "12345"; - expect(successMessages.MONITOR_GET_BY_USER_ID(userId)).to.equal( - `Got monitor for ${userId} successfully"` - ); + expect(successMessages.MONITOR_GET_BY_USER_ID(userId)).to.equal(`Got monitor for ${userId} successfully"`); }); // Add more tests for other success messages as needed diff --git a/server/utils/dataUtils.js b/server/utils/dataUtils.js index d164b92fd..e7fd334dd 100755 --- a/server/utils/dataUtils.js +++ b/server/utils/dataUtils.js @@ -15,9 +15,7 @@ const calculatePercentileUptimeDetails = (arr, percentile) => { const upper = lower + 1; const weight = index % 1; if (upper >= sorted.length) return sorted[lower].avgResponseTime; - return ( - sorted[lower].avgResponseTime * (1 - weight) + sorted[upper].avgResponseTime * weight - ); + return sorted[lower].avgResponseTime * (1 - weight) + sorted[upper].avgResponseTime * weight; }; const NormalizeData = (checks, rangeMin, rangeMax) => { @@ -28,15 +26,11 @@ const NormalizeData = (checks, rangeMin, rangeMax) => { const normalizedChecks = checks.map((check) => { const originalResponseTime = check.responseTime; // Normalize the response time between 1 and 100 - let normalizedResponseTime = - rangeMin + ((check.responseTime - min) * (rangeMax - rangeMin)) / (max - min); + let normalizedResponseTime = rangeMin + ((check.responseTime - min) * (rangeMax - rangeMin)) / (max - min); // Put a floor on the response times so we don't have extreme outliers // Better visuals - normalizedResponseTime = Math.max( - rangeMin, - Math.min(rangeMax, normalizedResponseTime) - ); + normalizedResponseTime = Math.max(rangeMin, Math.min(rangeMax, normalizedResponseTime)); return { ...check, responseTime: normalizedResponseTime, @@ -60,15 +54,11 @@ const NormalizeDataUptimeDetails = (checks, rangeMin, rangeMax) => { const normalizedChecks = checks.map((check) => { const originalResponseTime = check.avgResponseTime; // Normalize the response time between 1 and 100 - let normalizedResponseTime = - rangeMin + ((check.avgResponseTime - min) * (rangeMax - rangeMin)) / (max - min); + let normalizedResponseTime = rangeMin + ((check.avgResponseTime - min) * (rangeMax - rangeMin)) / (max - min); // Put a floor on the response times so we don't have extreme outliers // Better visuals - normalizedResponseTime = Math.max( - rangeMin, - Math.min(rangeMax, normalizedResponseTime) - ); + normalizedResponseTime = Math.max(rangeMin, Math.min(rangeMax, normalizedResponseTime)); return { ...check, avgResponseTime: normalizedResponseTime, @@ -107,10 +97,4 @@ const safelyParseFloat = (value, defaultValue = 0) => { return parsedValue; }; -export { - safelyParseFloat, - calculatePercentile, - NormalizeData, - calculatePercentileUptimeDetails, - NormalizeDataUptimeDetails, -}; +export { safelyParseFloat, calculatePercentile, NormalizeData, calculatePercentileUptimeDetails, NormalizeDataUptimeDetails }; diff --git a/server/utils/errorUtils.js b/server/utils/errorUtils.js index fc328690a..f913746b3 100644 --- a/server/utils/errorUtils.js +++ b/server/utils/errorUtils.js @@ -10,67 +10,31 @@ class AppError extends Error { } } -export const createError = ( - message, - status = 500, - service = null, - method = null, - details = null -) => { +export const createError = (message, status = 500, service = null, method = null, details = null) => { return new AppError(message, status, service, method, details); }; -export const createValidationError = ( - message, - details = null, - service = null, - method = null -) => { +export const createValidationError = (message, details = null, service = null, method = null) => { return createError(message, 422, service, method, details); }; -export const createAuthError = ( - message, - details = null, - service = null, - method = null -) => { +export const createAuthError = (message, details = null, service = null, method = null) => { return createError(message, 401, service, method, details); }; -export const createForbiddenError = ( - message, - details = null, - service = null, - method = null -) => { +export const createForbiddenError = (message, details = null, service = null, method = null) => { return createError(message, 403, service, method, details); }; -export const createNotFoundError = ( - message, - details = null, - service = null, - method = null -) => { +export const createNotFoundError = (message, details = null, service = null, method = null) => { return createError(message, 404, service, method, details); }; -export const createConflictError = ( - message, - details = null, - service = null, - method = null -) => { +export const createConflictError = (message, details = null, service = null, method = null) => { return createError(message, 409, service, method, details); }; -export const createServerError = ( - message, - details = null, - service = null, - method = null -) => { +export const createServerError = (message, details = null, service = null, method = null) => { return createError(message, 500, service, method, details); }; @@ -81,12 +45,7 @@ export const asyncHandler = (fn, serviceName, methodName) => { } catch (error) { // Handle validation errors if (error.isJoi || error.name === "ValidationError") { - const validationError = createValidationError( - error.message, - error.details, - serviceName, - methodName - ); + const validationError = createValidationError(error.message, error.details, serviceName, methodName); return next(validationError); } @@ -107,10 +66,7 @@ export const asyncHandler = (fn, serviceName, methodName) => { } // For unknown errors, create a server error - const appError = createServerError( - error.message || "An unexpected error occurred", - { originalError: error.message, stack: error.stack } - ); + const appError = createServerError(error.message || "An unexpected error occurred", { originalError: error.message, stack: error.stack }); appError.service = serviceName; appError.method = methodName; appError.stack = error.stack; // Preserve original stack diff --git a/server/utils/logger.js b/server/utils/logger.js index 49b875d96..6fc240e4c 100755 --- a/server/utils/logger.js +++ b/server/utils/logger.js @@ -6,43 +6,41 @@ class Logger { constructor() { this.logCache = []; this.maxCacheSize = 1000; - const consoleFormat = format.printf( - ({ level, message, service, method, details, timestamp, stack }) => { - if (message instanceof Object) { - message = JSON.stringify(message, null, 2); - } - - if (details instanceof Object) { - details = JSON.stringify(details, null, 2); - } - let msg = `${timestamp} ${level}:`; - service && (msg += ` [${service}]`); - method && (msg += `(${method})`); - message && (msg += ` ${message}`); - details && (msg += ` (details: ${details})`); - - if (typeof stack !== "undefined") { - const stackTrace = stack - ?.split("\n") - .slice(1) // Remove first line (error message) - .map((line) => { - const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/); - if (match) { - return { - function: match[1], - file: match[2], - line: parseInt(match[3]), - column: parseInt(match[4]), - }; - } - return line.trim(); - }); - stack && (msg += ` (stack: ${JSON.stringify(stackTrace, null, 2)})`); - } - - return msg; + const consoleFormat = format.printf(({ level, message, service, method, details, timestamp, stack }) => { + if (message instanceof Object) { + message = JSON.stringify(message, null, 2); } - ); + + if (details instanceof Object) { + details = JSON.stringify(details, null, 2); + } + let msg = `${timestamp} ${level}:`; + service && (msg += ` [${service}]`); + method && (msg += `(${method})`); + message && (msg += ` ${message}`); + details && (msg += ` (details: ${details})`); + + if (typeof stack !== "undefined") { + const stackTrace = stack + ?.split("\n") + .slice(1) // Remove first line (error message) + .map((line) => { + const match = line.match(/at\s+(.+?)\s+\((.+?):(\d+):(\d+)\)/); + if (match) { + return { + function: match[1], + file: match[2], + line: parseInt(match[3]), + column: parseInt(match[4]), + }; + } + return line.trim(); + }); + stack && (msg += ` (stack: ${JSON.stringify(stackTrace, null, 2)})`); + } + + return msg; + }); const logLevel = process.env.LOG_LEVEL || "info"; @@ -51,12 +49,7 @@ class Logger { format: format.combine(format.timestamp()), transports: [ new transports.Console({ - format: format.combine( - format.colorize(), - format.prettyPrint(), - format.json(), - consoleFormat - ), + format: format.combine(format.colorize(), format.prettyPrint(), format.json(), consoleFormat), }), new transports.File({ format: format.combine(format.json()), diff --git a/server/utils/utils.js b/server/utils/utils.js index 57d181886..bcb4d8e5e 100755 --- a/server/utils/utils.js +++ b/server/utils/utils.js @@ -6,12 +6,7 @@ const ParseBoolean = (value) => { if (value === true || value === "true") { return true; - } else if ( - value === false || - value === "false" || - value === null || - value === undefined - ) { + } else if (value === false || value === "false" || value === null || value === undefined) { return false; } }; @@ -21,8 +16,7 @@ const getTokenFromHeaders = (headers) => { if (!authorizationHeader) throw new Error("No auth headers"); const parts = authorizationHeader.split(" "); - if (parts.length !== 2 || parts[0] !== "Bearer") - throw new Error("Invalid auth headers"); + if (parts.length !== 2 || parts[0] !== "Bearer") throw new Error("Invalid auth headers"); return parts[1]; }; diff --git a/server/validation/joi.js b/server/validation/joi.js index f555756eb..ec2274596 100755 --- a/server/validation/joi.js +++ b/server/validation/joi.js @@ -7,9 +7,7 @@ import joi from "joi"; const roleValidatior = (role) => (value, helpers) => { const hasRole = role.some((role) => value.includes(role)); if (!hasRole) { - throw new joi.ValidationError( - `You do not have the required authorization. Required roles: ${role.join(", ")}` - ); + throw new joi.ValidationError(`You do not have the required authorization. Required roles: ${role.join(", ")}`); } return value; }; @@ -58,10 +56,6 @@ const registrationBodyValidation = joi.object({ inviteToken: joi.string().allow("").required(), }); -const editUserParamValidation = joi.object({ - userId: joi.string().required(), -}); - const editUserBodyValidation = joi.object({ firstName: nameValidation.required(), lastName: nameValidation.required(), @@ -134,11 +128,7 @@ const getMonitorsByTeamIdQueryValidation = joi.object({ .alternatives() .try( joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port"), - joi - .array() - .items( - joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port") - ) + joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port")) ), page: joi.number(), rowsPerPage: joi.number(), @@ -454,8 +444,7 @@ const createStatusPageBodyValidation = joi.object({ .pattern(/^[a-zA-Z0-9_-]+$/) // Only allow alphanumeric, underscore, and hyphen .required() .messages({ - "string.pattern.base": - "URL can only contain letters, numbers, underscores, and hyphens", + "string.pattern.base": "URL can only contain letters, numbers, underscores, and hyphens", }), timezone: joi.string().optional(), color: joi.string().optional(), @@ -485,13 +474,9 @@ const imageValidation = joi fieldname: joi.string().required(), originalname: joi.string().required(), encoding: joi.string().required(), - mimetype: joi - .string() - .valid("image/jpeg", "image/png", "image/jpg") - .required() - .messages({ - "string.valid": "File must be a valid image (jpeg, jpg, or png)", - }), + mimetype: joi.string().valid("image/jpeg", "image/png", "image/jpg").required().messages({ + "string.valid": "File must be a valid image (jpeg, jpg, or png)", + }), size: joi.number().max(3145728).required().messages({ "number.max": "File size must be less than 3MB", }), @@ -578,15 +563,11 @@ const createNotificationBodyValidation = joi.object({ "any.required": "Notification name is required", }), - type: joi - .string() - .valid("email", "webhook", "slack", "discord", "pager_duty") - .required() - .messages({ - "string.empty": "Notification type is required", - "any.required": "Notification type is required", - "any.only": "Notification type must be email, webhook, or pager_duty", - }), + type: joi.string().valid("email", "webhook", "slack", "discord", "pager_duty").required().messages({ + "string.empty": "Notification type is required", + "any.required": "Notification type is required", + "any.only": "Notification type must be email, webhook, or pager_duty", + }), address: joi.when("type", { is: "email", @@ -659,7 +640,6 @@ export { editMonitorBodyValidation, pauseMonitorParamValidation, getMonitorURLByQueryValidation, - editUserParamValidation, editUserBodyValidation, createAlertParamValidation, createAlertBodyValidation,