diff --git a/client/src/Hooks/monitorHooks.js b/client/src/Hooks/monitorHooks.js
index 4f0e9c049..616a37877 100644
--- a/client/src/Hooks/monitorHooks.js
+++ b/client/src/Hooks/monitorHooks.js
@@ -300,6 +300,28 @@ const useCreateMonitor = () => {
return [createMonitor, isLoading];
};
+const useFetchGlobalSettings = () => {
+ const [isLoading, setIsLoading] = useState(true);
+ const [globalSettings, setGlobalSettings] = useState(undefined);
+ useEffect(() => {
+ const fetchGlobalSettings = async () => {
+ try {
+ const res = await networkService.getAppSettings();
+ setGlobalSettings(res?.data);
+ } catch (error) {
+ console.error("Failed to fetch global settings:", error);
+ createToast({ body: "Failed to load global settings" });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ fetchGlobalSettings();
+ }, []);
+
+ return [globalSettings, isLoading];
+};
+
const useDeleteMonitor = () => {
const [isLoading, setIsLoading] = useState(false);
const navigate = useNavigate();
@@ -499,6 +521,7 @@ export {
useFetchUptimeMonitorById,
useFetchHardwareMonitorById,
useCreateMonitor,
+ useFetchGlobalSettings,
useDeleteMonitor,
useUpdateMonitor,
usePauseMonitor,
diff --git a/client/src/Pages/Infrastructure/Create/index.jsx b/client/src/Pages/Infrastructure/Create/index.jsx
index c406b6b40..cde9315aa 100644
--- a/client/src/Pages/Infrastructure/Create/index.jsx
+++ b/client/src/Pages/Infrastructure/Create/index.jsx
@@ -3,7 +3,6 @@ import { useTheme } from "@emotion/react";
import { useParams } from "react-router-dom";
import { useState, useEffect } from "react";
import { useSelector } from "react-redux";
-
// Utility and Network
import { infrastructureMonitorValidation } from "../../../Validation/validation";
import { useFetchHardwareMonitorById } from "../../../Hooks/monitorHooks";
@@ -11,7 +10,11 @@ import { capitalizeFirstLetter } from "../../../Utils/stringUtils";
import { useTranslation } from "react-i18next";
import { useGetNotificationsByTeamId } from "../../../Hooks/useNotifications";
import NotificationsConfig from "../../../Components/NotificationConfig";
-import { useUpdateMonitor, useCreateMonitor } from "../../../Hooks/monitorHooks";
+import {
+ useUpdateMonitor,
+ useCreateMonitor,
+ useFetchGlobalSettings,
+} from "../../../Hooks/monitorHooks";
// MUI
import { Box, Stack, Typography, Button, ButtonGroup } from "@mui/material";
@@ -65,6 +68,7 @@ const CreateInfrastructureMonitor = () => {
useGetNotificationsByTeamId();
const [updateMonitor, isUpdating] = useUpdateMonitor();
const [createMonitor, isCreating] = useCreateMonitor();
+ const [globalSettings, globalSettingsLoading] = useFetchGlobalSettings();
// State
const [errors, setErrors] = useState({});
@@ -87,35 +91,64 @@ const CreateInfrastructureMonitor = () => {
});
// Populate form fields if editing
+
useEffect(() => {
- if (isCreate || !monitor) return;
+ if (isCreate) {
+ if (globalSettingsLoading) return;
- setInfrastructureMonitor({
- url: monitor.url.replace(/^https?:\/\//, ""),
- name: monitor.name || "",
- notifications: monitor.notifications,
- interval: monitor.interval / MS_PER_MINUTE,
- cpu: monitor.thresholds?.usage_cpu !== undefined,
- usage_cpu: monitor.thresholds?.usage_cpu ? monitor.thresholds.usage_cpu * 100 : "",
+ const gt = globalSettings?.data?.settings?.globalThresholds || {};
- memory: monitor.thresholds?.usage_memory !== undefined,
- usage_memory: monitor.thresholds?.usage_memory
- ? monitor.thresholds.usage_memory * 100
- : "",
+ setHttps(false);
- disk: monitor.thresholds?.usage_disk !== undefined,
- usage_disk: monitor.thresholds?.usage_disk
- ? monitor.thresholds.usage_disk * 100
- : "",
+ setInfrastructureMonitor({
+ url: "",
+ name: "",
+ notifications: [],
+ interval: 0.25,
+ cpu: gt.cpu !== undefined,
+ usage_cpu: gt.cpu !== undefined ? gt.cpu.toString() : "",
+ memory: gt.memory !== undefined,
+ usage_memory: gt.memory !== undefined ? gt.memory.toString() : "",
+ disk: gt.disk !== undefined,
+ usage_disk: gt.disk !== undefined ? gt.disk.toString() : "",
+ temperature: gt.temperature !== undefined,
+ usage_temperature: gt.temperature !== undefined ? gt.temperature.toString() : "",
+ secret: "",
+ });
+ } else if (monitor) {
+ const { thresholds = {} } = monitor;
- temperature: monitor.thresholds?.usage_temperature !== undefined,
- usage_temperature: monitor.thresholds?.usage_temperature
- ? monitor.thresholds.usage_temperature * 100
- : "",
- secret: monitor.secret || "",
- });
- setHttps(monitor.url.startsWith("https"));
- }, [isCreate, monitor]);
+ setHttps(monitor.url.startsWith("https"));
+
+ setInfrastructureMonitor({
+ url: monitor.url.replace(/^https?:\/\//, ""),
+ name: monitor.name || "",
+ notifications: monitor.notifications || [],
+ interval: monitor.interval / MS_PER_MINUTE,
+ cpu: thresholds.usage_cpu !== undefined,
+ usage_cpu:
+ thresholds.usage_cpu !== undefined
+ ? (thresholds.usage_cpu * 100).toString()
+ : "",
+ memory: thresholds.usage_memory !== undefined,
+ usage_memory:
+ thresholds.usage_memory !== undefined
+ ? (thresholds.usage_memory * 100).toString()
+ : "",
+ disk: thresholds.usage_disk !== undefined,
+ usage_disk:
+ thresholds.usage_disk !== undefined
+ ? (thresholds.usage_disk * 100).toString()
+ : "",
+ temperature: thresholds.usage_temperature !== undefined,
+ usage_temperature:
+ thresholds.usage_temperature !== undefined
+ ? (thresholds.usage_temperature * 100).toString()
+ : "",
+ secret: monitor.secret || "",
+ });
+ }
+ }, [isCreate, monitor, globalSettings, globalSettingsLoading]);
// Handlers
const onSubmit = async (event) => {
diff --git a/client/src/Pages/Settings/SettingsGlobalThresholds.jsx b/client/src/Pages/Settings/SettingsGlobalThresholds.jsx
new file mode 100644
index 000000000..acc101ab4
--- /dev/null
+++ b/client/src/Pages/Settings/SettingsGlobalThresholds.jsx
@@ -0,0 +1,95 @@
+import Box from "@mui/material/Box";
+import Stack from "@mui/material/Stack";
+import Typography from "@mui/material/Typography";
+import ConfigBox from "../../Components/ConfigBox";
+import TextInput from "../../Components/Inputs/TextInput";
+
+import { useTheme } from "@emotion/react";
+import { PropTypes } from "prop-types";
+import { useTranslation } from "react-i18next";
+
+const SettingsGlobalThresholds = ({
+ isAdmin,
+ HEADING_SX,
+ settingsData,
+ setSettingsData,
+}) => {
+ const { t } = useTranslation(); // For language translation
+ const theme = useTheme(); // MUI theme access
+
+ // Handles input change and updates parent state
+ const handleChange = (e, min, max) => {
+ const { name, value } = e.target;
+
+ const numValue = parseFloat(value);
+ const isValidNumber =
+ value === "" ||
+ (!isNaN(numValue) && isFinite(numValue) && numValue >= min && numValue <= max);
+
+ if (isValidNumber) {
+ setSettingsData((prev) => ({
+ ...prev,
+ settings: {
+ ...prev.settings,
+ globalThresholds: {
+ ...prev.settings?.globalThresholds,
+ [name]: value,
+ },
+ },
+ }));
+ }
+ };
+
+ // Only render this section for admins
+ if (!isAdmin) return null;
+
+ return (
+
+ {/* Header and description */}
+
+
+ {t("settingsPage.globalThresholds.title", "Global Thresholds")}
+
+
+ {t(
+ "settingsPage.globalThresholds.description",
+ "Configure global CPU, Memory, Disk, and Temperature thresholds."
+ )}
+
+
+
+ {/* Threshold inputs */}
+
+ {[
+ ["CPU Threshold (%)", "cpu", 1, 100],
+ ["Memory Threshold (%)", "memory", 1, 100],
+ ["Disk Threshold (%)", "disk", 1, 100],
+ ["Temperature Threshold (°C)", "temperature", 1, 150],
+ ].map(([label, name, min, max]) => (
+ handleChange(e, min, max)}
+ />
+ ))}
+
+
+ );
+};
+
+// Prop types
+SettingsGlobalThresholds.propTypes = {
+ isAdmin: PropTypes.bool,
+ HEADING_SX: PropTypes.object,
+ settingsData: PropTypes.object,
+ setSettingsData: PropTypes.func,
+};
+
+export default SettingsGlobalThresholds;
diff --git a/client/src/Pages/Settings/index.jsx b/client/src/Pages/Settings/index.jsx
index dae6bd680..92f58b4c0 100644
--- a/client/src/Pages/Settings/index.jsx
+++ b/client/src/Pages/Settings/index.jsx
@@ -8,6 +8,7 @@ import SettingsPagespeed from "./SettingsPagespeed";
import SettingsDemoMonitors from "./SettingsDemoMonitors";
import SettingsAbout from "./SettingsAbout";
import SettingsEmail from "./SettingsEmail";
+import SettingsGlobalThresholds from "./SettingsGlobalThresholds";
import Button from "@mui/material/Button";
// Utils
import { settingsValidation } from "../../Validation/validation";
@@ -48,6 +49,7 @@ const Settings = () => {
setIsApiKeySet,
setIsEmailPasswordSet,
});
+
const [addDemoMonitors, isAddingDemoMonitors] = useAddDemoMonitors();
const [isSaving, saveError, saveSettings] = useSaveSettings({
@@ -149,6 +151,7 @@ const Settings = () => {
error.details.forEach((err) => {
newErrors[err.path[0]] = err.message;
});
+
setErrors(newErrors);
}
saveSettings(settingsData?.settings);
@@ -190,6 +193,13 @@ const Settings = () => {
handleChange={handleChange}
errors={errors}
/>
+
+
{
diff --git a/client/src/locales/en.json b/client/src/locales/en.json
index 669e322a5..605a50fa0 100644
--- a/client/src/locales/en.json
+++ b/client/src/locales/en.json
@@ -874,6 +874,10 @@
"title": "Display timezone"
},
"title": "Settings",
+ "globalThresholds": {
+ "title": "Global Thresholds",
+ "description": "Configure global CPU, Memory, Disk, and Temperature thresholds. If a value is provided, it will automatically be enabled for monitoring."
+ },
"uiSettings": {
"description": "Switch between light and dark mode, or change user interface language.",
"labelLanguage": "Language",
diff --git a/server/src/config/services.js b/server/src/config/services.js
index c2ecae200..4fb9424cc 100644
--- a/server/src/config/services.js
+++ b/server/src/config/services.js
@@ -32,14 +32,42 @@ import mjml2html from "mjml";
import jwt from "jsonwebtoken";
import crypto from "crypto";
+import { fileURLToPath } from "url";
+import { ObjectId } from "mongodb";
+
// DB Modules
+import { NormalizeData, NormalizeDataUptimeDetails } from "../utils/dataUtils.js";
+import { GenerateAvatarImage } from "../utils/imageProcessing.js";
+import { ParseBoolean } from "../utils/utils.js";
+
+// Models
import Check from "../db/models/Check.js";
import HardwareCheck from "../db/models/HardwareCheck.js";
import PageSpeedCheck from "../db/models/PageSpeedCheck.js";
import Monitor from "../db/models/Monitor.js";
import User from "../db/models/User.js";
+import InviteToken from "../db/models/InviteToken.js";
+import StatusPage from "../db/models/StatusPage.js";
+import Team from "../db/models/Team.js";
+import MaintenanceWindow from "../db/models/MaintenanceWindow.js";
+import MonitorStats from "../db/models/MonitorStats.js";
+import NetworkCheck from "../db/models/NetworkCheck.js";
+import Notification from "../db/models/Notification.js";
+import RecoveryToken from "../db/models/RecoveryToken.js";
+import AppSettings from "../db/models/AppSettings.js";
+import InviteModule from "../db/mongo/modules/inviteModule.js";
import CheckModule from "../db/mongo/modules/checkModule.js";
+import StatusPageModule from "../db/mongo/modules/statusPageModule.js";
+import UserModule from "../db/mongo/modules/userModule.js";
+import HardwareCheckModule from "../db/mongo/modules/hardwareCheckModule.js";
+import MaintenanceWindowModule from "../db/mongo/modules/maintenanceWindowModule.js";
+import MonitorModule from "../db/mongo/modules/monitorModule.js";
+import NetworkCheckModule from "../db/mongo/modules/networkCheckModule.js";
+import NotificationModule from "../db/mongo/modules/notificationModule.js";
+import PageSpeedCheckModule from "../db/mongo/modules/pageSpeedCheckModule.js";
+import RecoveryModule from "../db/mongo/modules/recoveryModule.js";
+import SettingsModule from "../db/mongo/modules/settingsModule.js";
export const initializeServices = async ({ logger, envSettings, settingsService }) => {
const serviceRegistry = new ServiceRegistry({ logger });
@@ -52,7 +80,48 @@ export const initializeServices = async ({ logger, envSettings, settingsService
// Create DB
const checkModule = new CheckModule({ logger, Check, HardwareCheck, PageSpeedCheck, Monitor, User });
- const db = new MongoDB({ logger, envSettings, checkModule });
+ const inviteModule = new InviteModule({ InviteToken, crypto, stringService });
+ const statusPageModule = new StatusPageModule({ StatusPage, NormalizeData, stringService });
+ const userModule = new UserModule({ User, Team, GenerateAvatarImage, ParseBoolean, stringService });
+ const hardwareCheckModule = new HardwareCheckModule({ HardwareCheck, Monitor, logger });
+ const maintenanceWindowModule = new MaintenanceWindowModule({ MaintenanceWindow });
+ const monitorModule = new MonitorModule({
+ Monitor,
+ MonitorStats,
+ Check,
+ PageSpeedCheck,
+ HardwareCheck,
+ stringService,
+ fs,
+ path,
+ fileURLToPath,
+ ObjectId,
+ NormalizeData,
+ NormalizeDataUptimeDetails,
+ });
+ const networkCheckModule = new NetworkCheckModule({ NetworkCheck });
+ const notificationModule = new NotificationModule({ Notification, Monitor });
+ const pageSpeedCheckModule = new PageSpeedCheckModule({ PageSpeedCheck });
+ const recoveryModule = new RecoveryModule({ User, RecoveryToken, crypto, stringService });
+ const settingsModule = new SettingsModule({ AppSettings });
+
+ const db = new MongoDB({
+ logger,
+ envSettings,
+ checkModule,
+ inviteModule,
+ statusPageModule,
+ userModule,
+ hardwareCheckModule,
+ maintenanceWindowModule,
+ monitorModule,
+ networkCheckModule,
+ notificationModule,
+ pageSpeedCheckModule,
+ recoveryModule,
+ settingsModule,
+ });
+
await db.connect();
const networkService = new NetworkService(axios, ping, logger, http, Docker, net, stringService, settingsService);
diff --git a/server/src/controllers/monitorController.js b/server/src/controllers/monitorController.js
index 13890e5ea..a4f391087 100755
--- a/server/src/controllers/monitorController.js
+++ b/server/src/controllers/monitorController.js
@@ -32,7 +32,7 @@ class MonitorController extends BaseController {
}
async verifyTeamAccess(teamId, monitorId) {
- const monitor = await this.db.getMonitorById(monitorId);
+ const monitor = await this.db.monitorModule.getMonitorById(monitorId);
if (!monitor.teamId.equals(teamId)) {
throw this.errorService.createAuthorizationError();
}
@@ -150,7 +150,7 @@ class MonitorController extends BaseController {
await getCertificateParamValidation.validateAsync(req.params);
const { monitorId } = req.params;
- const monitor = await this.db.getMonitorById(monitorId);
+ const monitor = await this.db.monitorModule.getMonitorById(monitorId);
const certificate = await fetchMonitorCertificate(sslChecker, monitor);
return res.success({
diff --git a/server/src/controllers/notificationController.js b/server/src/controllers/notificationController.js
index 081968e3d..e00dd5b6f 100755
--- a/server/src/controllers/notificationController.js
+++ b/server/src/controllers/notificationController.js
@@ -53,7 +53,7 @@ class NotificationController extends BaseController {
body.userId = userId;
body.teamId = teamId;
- const notification = await this.db.createNotification(body);
+ const notification = await this.db.notificationModule.createNotification(body);
return res.success({
msg: "Notification created successfully",
data: notification,
@@ -70,7 +70,7 @@ class NotificationController extends BaseController {
throw this.errorService.createBadRequestError("Team ID is required");
}
- const notifications = await this.db.getNotificationsByTeamId(teamId);
+ const notifications = await this.db.notificationModule.getNotificationsByTeamId(teamId);
return res.success({
msg: "Notifications fetched successfully",
@@ -88,12 +88,12 @@ class NotificationController extends BaseController {
throw this.errorService.createBadRequestError("Team ID is required");
}
- const notification = await this.db.getNotificationById(req.params.id);
+ const notification = await this.db.notificationModule.getNotificationById(req.params.id);
if (!notification.teamId.equals(teamId)) {
throw this.errorService.createAuthorizationError();
}
- await this.db.deleteNotificationById(req.params.id);
+ await this.db.notificationModule.deleteNotificationById(req.params.id);
return res.success({
msg: "Notification deleted successfully",
});
@@ -104,7 +104,7 @@ class NotificationController extends BaseController {
getNotificationById = this.asyncHandler(
async (req, res) => {
- const notification = await this.db.getNotificationById(req.params.id);
+ const notification = await this.db.notificationModule.getNotificationById(req.params.id);
const teamId = req?.user?.teamId;
if (!teamId) {
@@ -134,13 +134,13 @@ class NotificationController extends BaseController {
throw this.errorService.createBadRequestError("Team ID is required");
}
- const notification = await this.db.getNotificationById(req.params.id);
+ const notification = await this.db.notificationModule.getNotificationById(req.params.id);
if (!notification.teamId.equals(teamId)) {
throw this.errorService.createAuthorizationError();
}
- const editedNotification = await this.db.editNotification(req.params.id, req.body);
+ const editedNotification = await this.db.notificationModule.editNotification(req.params.id, req.body);
return res.success({
msg: "Notification updated successfully",
data: editedNotification,
@@ -158,7 +158,7 @@ class NotificationController extends BaseController {
throw this.errorService.createBadRequestError("Team ID is required");
}
- const monitor = await this.db.getMonitorById(monitorId);
+ const monitor = await this.db.monitorModule.getMonitorById(monitorId);
if (!monitor.teamId.equals(teamId)) {
throw this.errorService.createAuthorizationError();
diff --git a/server/src/controllers/settingsController.js b/server/src/controllers/settingsController.js
index c66024025..7da724d23 100755
--- a/server/src/controllers/settingsController.js
+++ b/server/src/controllers/settingsController.js
@@ -55,7 +55,7 @@ class SettingsController extends BaseController {
async (req, res) => {
await updateAppSettingsBodyValidation.validateAsync(req.body);
- const updatedSettings = await this.db.updateAppSettings(req.body);
+ const updatedSettings = await this.db.settingsModule.updateAppSettings(req.body);
const returnSettings = this.buildAppSettings(updatedSettings);
return res.success({
msg: this.stringService.updateAppSettings,
diff --git a/server/src/controllers/statusPageController.js b/server/src/controllers/statusPageController.js
index 85211c7ff..1cbcb49a2 100755
--- a/server/src/controllers/statusPageController.js
+++ b/server/src/controllers/statusPageController.js
@@ -19,7 +19,7 @@ class StatusPageController extends BaseController {
await imageValidation.validateAsync(req.file);
const { _id, teamId } = req.user;
- const statusPage = await this.db.createStatusPage({
+ const statusPage = await this.db.statusPageModule.createStatusPage({
statusPageData: req.body,
image: req.file,
userId: _id,
@@ -39,7 +39,7 @@ class StatusPageController extends BaseController {
await createStatusPageBodyValidation.validateAsync(req.body);
await imageValidation.validateAsync(req.file);
- const statusPage = await this.db.updateStatusPage(req.body, req.file);
+ const statusPage = await this.db.statusPageModule.updateStatusPage(req.body, req.file);
if (statusPage === null) {
throw this.errorService.createNotFoundError(this.stringService.statusPageNotFound);
}
@@ -54,7 +54,7 @@ class StatusPageController extends BaseController {
getStatusPage = this.asyncHandler(
async (req, res) => {
- const statusPage = await this.db.getStatusPage();
+ const statusPage = await this.db.statusPageModule.getStatusPage();
return res.success({
msg: this.stringService.statusPageByUrl,
data: statusPage,
@@ -69,7 +69,7 @@ class StatusPageController extends BaseController {
await getStatusPageParamValidation.validateAsync(req.params);
await getStatusPageQueryValidation.validateAsync(req.query);
- const statusPage = await this.db.getStatusPageByUrl(req.params.url, req.query.type);
+ const statusPage = await this.db.statusPageModule.getStatusPageByUrl(req.params.url);
return res.success({
msg: this.stringService.statusPageByUrl,
data: statusPage,
@@ -82,7 +82,7 @@ class StatusPageController extends BaseController {
getStatusPagesByTeamId = this.asyncHandler(
async (req, res) => {
const teamId = req.user.teamId;
- const statusPages = await this.db.getStatusPagesByTeamId(teamId);
+ const statusPages = await this.db.statusPageModule.getStatusPagesByTeamId(teamId);
return res.success({
msg: this.stringService.statusPageByTeamId,
diff --git a/server/src/db/models/AppSettings.js b/server/src/db/models/AppSettings.js
index cd63118e9..2313d638c 100755
--- a/server/src/db/models/AppSettings.js
+++ b/server/src/db/models/AppSettings.js
@@ -65,6 +65,12 @@ const AppSettingsSchema = mongoose.Schema(
type: Number,
default: 1,
},
+ globalThresholds: {
+ cpu: { type: Number },
+ memory: { type: Number },
+ disk: { type: Number },
+ temperature: { type: Number },
+ },
},
{
timestamps: true,
diff --git a/server/src/db/mongo/MongoDB.js b/server/src/db/mongo/MongoDB.js
index ef7179d35..0c6274320 100755
--- a/server/src/db/mongo/MongoDB.js
+++ b/server/src/db/mongo/MongoDB.js
@@ -1,89 +1,39 @@
import mongoose from "mongoose";
import AppSettings from "../models/AppSettings.js";
-//****************************************
-// User Operations
-//****************************************
-
-import * as userModule from "./modules/userModule.js";
-
-//****************************************
-// Invite Token Operations
-//****************************************
-
-import * as inviteModule from "./modules/inviteModule.js";
-
-//****************************************
-// Recovery Operations
-//****************************************
-import * as recoveryModule from "./modules/recoveryModule.js";
-
-//****************************************
-// Monitors
-//****************************************
-
-import * as monitorModule from "./modules/monitorModule.js";
-
-//****************************************
-// Page Speed Checks
-//****************************************
-
-import * as pageSpeedCheckModule from "./modules/pageSpeedCheckModule.js";
-
-//****************************************
-// Hardware Checks
-//****************************************
-import * as hardwareCheckModule from "./modules/hardwareCheckModule.js";
-
-//****************************************
-// Checks
-//****************************************
-
-import * as checkModule from "./modules/checkModule.js";
-
-//****************************************
-// Maintenance Window
-//****************************************
-import * as maintenanceWindowModule from "./modules/maintenanceWindowModule.js";
-
-//****************************************
-// Notifications
-//****************************************
-import * as notificationModule from "./modules/notificationModule.js";
-
-//****************************************
-// AppSettings
-//****************************************
-import * as settingsModule from "./modules/settingsModule.js";
-
-//****************************************
-// Status Page
-//****************************************
-import * as statusPageModule from "./modules/statusPageModule.js";
-
-//****************************************
-// Diagnostic
-//****************************************
-import * as diagnosticModule from "./modules/diagnosticModule.js";
-
class MongoDB {
static SERVICE_NAME = "MongoDB";
- constructor({ logger, envSettings, checkModule }) {
+ constructor({
+ logger,
+ envSettings,
+ checkModule,
+ inviteModule,
+ statusPageModule,
+ userModule,
+ hardwareCheckModule,
+ maintenanceWindowModule,
+ monitorModule,
+ networkCheckModule,
+ notificationModule,
+ pageSpeedCheckModule,
+ recoveryModule,
+ settingsModule,
+ }) {
this.logger = logger;
this.envSettings = envSettings;
- Object.assign(this, userModule);
- Object.assign(this, inviteModule);
- Object.assign(this, recoveryModule);
- Object.assign(this, monitorModule);
- Object.assign(this, pageSpeedCheckModule);
- Object.assign(this, hardwareCheckModule);
+ this.userModule = userModule;
+ this.inviteModule = inviteModule;
+ this.recoveryModule = recoveryModule;
+ this.pageSpeedCheckModule = pageSpeedCheckModule;
+ this.hardwareCheckModule = hardwareCheckModule;
this.checkModule = checkModule;
- Object.assign(this, maintenanceWindowModule);
- Object.assign(this, notificationModule);
- Object.assign(this, settingsModule);
- Object.assign(this, statusPageModule);
- Object.assign(this, diagnosticModule);
+ this.maintenanceWindowModule = maintenanceWindowModule;
+ this.monitorModule = monitorModule;
+ this.notificationModule = notificationModule;
+ this.settingsModule = settingsModule;
+ this.statusPageModule = statusPageModule;
+ this.networkCheckModule = networkCheckModule;
}
get serviceName() {
diff --git a/server/src/db/mongo/modules/diagnosticModule.js b/server/src/db/mongo/modules/diagnosticModule.js
deleted file mode 100755
index 8717720a3..000000000
--- a/server/src/db/mongo/modules/diagnosticModule.js
+++ /dev/null
@@ -1,48 +0,0 @@
-import Monitor from "../../models/Monitor.js";
-import { ObjectId } from "mongodb";
-
-const SERVICE_NAME = "diagnosticModule";
-import { buildMonitorSummaryByTeamIdPipeline, buildMonitorsByTeamIdPipeline, buildFilteredMonitorsByTeamIdPipeline } from "./monitorModuleQueries.js";
-
-const getMonitorsByTeamIdExecutionStats = async (req) => {
- try {
- let { limit, type, page, rowsPerPage, filter, field, order } = req.query;
- limit = parseInt(limit);
- page = parseInt(page);
- rowsPerPage = parseInt(rowsPerPage);
- if (field === undefined) {
- field = "name";
- order = "asc";
- }
- // Build match stage
- const matchStage = { teamId: new ObjectId(req.params.teamId) };
- if (type !== undefined) {
- matchStage.type = Array.isArray(type) ? { $in: type } : type;
- }
-
- const summary = await Monitor.aggregate(buildMonitorSummaryByTeamIdPipeline({ matchStage })).explain("executionStats");
-
- const monitors = await Monitor.aggregate(buildMonitorsByTeamIdPipeline({ matchStage, field, order })).explain("executionStats");
-
- const filteredMonitors = await Monitor.aggregate(
- buildFilteredMonitorsByTeamIdPipeline({
- matchStage,
- filter,
- page,
- rowsPerPage,
- field,
- order,
- limit,
- type,
- })
- ).explain("executionStats");
-
- return { summary, monitors, filteredMonitors };
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getMonitorSummaryByTeamIdExecutionStats";
- throw error;
- }
-};
-
-export { getMonitorsByTeamIdExecutionStats };
diff --git a/server/src/db/mongo/modules/hardwareCheckModule.js b/server/src/db/mongo/modules/hardwareCheckModule.js
index 9cb228bc4..448e72fed 100755
--- a/server/src/db/mongo/modules/hardwareCheckModule.js
+++ b/server/src/db/mongo/modules/hardwareCheckModule.js
@@ -1,70 +1,22 @@
-import HardwareCheck from "../../models/HardwareCheck.js";
-import Monitor from "../../models/Monitor.js";
-import { logger } from "../../../utils/logger.js";
-
const SERVICE_NAME = "hardwareCheckModule";
-const createHardwareCheck = async (hardwareCheckData) => {
- try {
- const { monitorId, status } = hardwareCheckData;
- const n = (await HardwareCheck.countDocuments({ monitorId })) + 1;
- const monitor = await Monitor.findById(monitorId);
- if (!monitor) {
- logger.error({
- message: "Monitor not found",
- service: SERVICE_NAME,
- method: "createHardwareCheck",
- details: `monitor ID: ${monitorId}`,
- });
- return null;
+class HardwareCheckModule {
+ constructor({ HardwareCheck, Monitor, logger }) {
+ this.HardwareCheck = HardwareCheck;
+ this.Monitor = Monitor;
+ this.logger = logger;
+ }
+
+ createHardwareChecks = async (hardwareChecks) => {
+ try {
+ await this.HardwareCheck.insertMany(hardwareChecks, { ordered: false });
+ return true;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "createHardwareChecks";
+ throw error;
}
+ };
+}
- let newUptimePercentage;
- if (monitor.uptimePercentage === undefined) {
- newUptimePercentage = status === true ? 1 : 0;
- } else {
- newUptimePercentage = (monitor.uptimePercentage * (n - 1) + (status === true ? 1 : 0)) / n;
- }
-
- await Monitor.findOneAndUpdate({ _id: monitorId }, { uptimePercentage: newUptimePercentage });
-
- const hardwareCheck = await new HardwareCheck({
- ...hardwareCheckData,
- }).save();
- return hardwareCheck;
- } catch (error) {
- logger.error({
- message: "Error creating hardware check",
- service: SERVICE_NAME,
- method: "createHardwareCheck",
- stack: error.stack,
- });
- error.service = SERVICE_NAME;
- error.method = "createHardwareCheck";
- throw error;
- }
-};
-
-const createHardwareChecks = async (hardwareChecks) => {
- try {
- await HardwareCheck.insertMany(hardwareChecks, { ordered: false });
- return true;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "createHardwareChecks";
- throw error;
- }
-};
-
-const deleteHardwareChecksByMonitorId = async (monitorId) => {
- try {
- const result = await HardwareCheck.deleteMany({ monitorId });
- return result.deletedCount;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "deleteHardwareChecksByMonitorId";
- throw error;
- }
-};
-
-export { createHardwareCheck, createHardwareChecks, deleteHardwareChecksByMonitorId };
+export default HardwareCheckModule;
diff --git a/server/src/db/mongo/modules/inviteModule.js b/server/src/db/mongo/modules/inviteModule.js
index 89d8ad8d7..444e51efd 100755
--- a/server/src/db/mongo/modules/inviteModule.js
+++ b/server/src/db/mongo/modules/inviteModule.js
@@ -1,89 +1,56 @@
-import InviteToken from "../../models/InviteToken.js";
-import crypto from "crypto";
-import ServiceRegistry from "../../../service/system/serviceRegistry.js";
-import StringService from "../../../service/system/stringService.js";
-
const SERVICE_NAME = "inviteModule";
-/**
- * Request an invite token for a user.
- *
- * This function deletes any existing invite tokens for the user's email,
- * generates a new token, saves it, and then returns the new token.
- *
- * @param {Object} userData - The user data.
- * @param {string} userData.email - The user's email.
- * @param {mongoose.Schema.Types.ObjectId} userData.teamId - The ID of the team.
- * @param {Array} userData.role - The user's role(s).
- * @param {Date} [userData.expiry=Date.now] - The expiry date of the token. Defaults to the current date and time.
- * @returns {Promise} The invite token.
- * @throws {Error} If there is an error.
- */
-const requestInviteToken = async (userData) => {
- try {
- await InviteToken.deleteMany({ email: userData.email });
- userData.token = crypto.randomBytes(32).toString("hex");
- let inviteToken = new InviteToken(userData);
- await inviteToken.save();
- return inviteToken;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "requestInviteToken";
- throw error;
- }
-};
-/**
- * Retrieves an invite token
- *
- * This function searches for an invite token in the database and deletes it.
- * If the invite token is not found, it throws an error.
- *
- * @param {string} token - The invite token to search for.
- * @returns {Promise} The invite token data.
- * @throws {Error} If the invite token is not found or there is another error.
- */
-const getInviteToken = async (token) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- try {
- const invite = await InviteToken.findOne({
- token,
- });
- if (invite === null) {
- throw new Error(stringService.authInviteNotFound);
+class InviteModule {
+ constructor({ InviteToken, crypto, stringService }) {
+ this.InviteToken = InviteToken;
+ this.crypto = crypto;
+ this.stringService = stringService;
+ }
+
+ requestInviteToken = async (userData) => {
+ try {
+ await this.InviteToken.deleteMany({ email: userData.email });
+ userData.token = this.crypto.randomBytes(32).toString("hex");
+ let inviteToken = new this.InviteToken(userData);
+ await inviteToken.save();
+ return inviteToken;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "requestInviteToken";
+ throw error;
}
- return invite;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getInviteToken";
- throw error;
- }
-};
+ };
-/**
- * Retrieves and deletes an invite token
- *
- * This function searches for an invite token in the database and deletes it.
- * If the invite token is not found, it throws an error.
- *
- * @param {string} token - The invite token to search for.
- * @returns {Promise} The invite token data.
- * @throws {Error} If the invite token is not found or there is another error.
- */
-const getInviteTokenAndDelete = async (token) => {
- const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
- try {
- const invite = await InviteToken.findOneAndDelete({
- token,
- });
- if (invite === null) {
- throw new Error(stringService.authInviteNotFound);
+ getInviteToken = async (token) => {
+ try {
+ const invite = await this.InviteToken.findOne({
+ token,
+ });
+ if (invite === null) {
+ throw new Error(this.stringService.authInviteNotFound);
+ }
+ return invite;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "getInviteToken";
+ throw error;
}
- return invite;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getInviteTokenAndDelete";
- throw error;
- }
-};
+ };
+ getInviteTokenAndDelete = async (token) => {
+ try {
+ const invite = await this.InviteToken.findOneAndDelete({
+ token,
+ });
+ if (invite === null) {
+ throw new Error(this.stringService.authInviteNotFound);
+ }
+ return invite;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "getInviteTokenAndDelete";
+ throw error;
+ }
+ };
+}
-export { requestInviteToken, getInviteToken, getInviteTokenAndDelete };
+export default InviteModule;
diff --git a/server/src/db/mongo/modules/maintenanceWindowModule.js b/server/src/db/mongo/modules/maintenanceWindowModule.js
index f7fea4636..61523c9f0 100755
--- a/server/src/db/mongo/modules/maintenanceWindowModule.js
+++ b/server/src/db/mongo/modules/maintenanceWindowModule.js
@@ -1,192 +1,106 @@
-import MaintenanceWindow from "../../models/MaintenanceWindow.js";
const SERVICE_NAME = "maintenanceWindowModule";
-/**
- * Asynchronously creates a new MaintenanceWindow document and saves it to the database.
- * If the maintenance window is a one-time event, the expiry field is set to the same value as the end field.
- * @async
- * @function createMaintenanceWindow
- * @param {Object} maintenanceWindowData - The data for the new MaintenanceWindow document.
- * @param {mongoose.Schema.Types.ObjectId} maintenanceWindowData.monitorId - The ID of the monitor.
- * @param {Boolean} maintenanceWindowData.active - Indicates whether the maintenance window is active.
- * @param {Boolean} maintenanceWindowData.oneTime - Indicates whether the maintenance window is a one-time event.
- * @param {Date} maintenanceWindowData.start - The start date and time of the maintenance window.
- * @param {Date} maintenanceWindowData.end - The end date and time of the maintenance window.
- * @returns {Promise} The saved MaintenanceWindow document.
- * @throws {Error} If there is an error saving the document.
- */
-const createMaintenanceWindow = async (maintenanceWindowData) => {
- try {
- const maintenanceWindow = new MaintenanceWindow({
- ...maintenanceWindowData,
- });
+class MaintenanceWindowModule {
+ constructor({ MaintenanceWindow }) {
+ this.MaintenanceWindow = MaintenanceWindow;
+ }
+ createMaintenanceWindow = async (maintenanceWindowData) => {
+ try {
+ const maintenanceWindow = new this.MaintenanceWindow({
+ ...maintenanceWindowData,
+ });
- // If the maintenance window is a one time window, set the expiry to the end date
- if (maintenanceWindowData.oneTime) {
- maintenanceWindow.expiry = maintenanceWindowData.end;
+ // If the maintenance window is a one time window, set the expiry to the end date
+ if (maintenanceWindowData.oneTime) {
+ maintenanceWindow.expiry = maintenanceWindowData.end;
+ }
+ const result = await maintenanceWindow.save();
+ return result;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "createMaintenanceWindow";
+ throw error;
}
- const result = await maintenanceWindow.save();
- return result;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "createMaintenanceWindow";
- throw error;
- }
-};
-
-const getMaintenanceWindowById = async ({ id, teamId }) => {
- try {
- const maintenanceWindow = await MaintenanceWindow.findOne({
- _id: id,
- teamId: teamId,
- });
- return maintenanceWindow;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getMaintenanceWindowById";
- throw error;
- }
-};
-
-/**
- * Asynchronously retrieves all MaintenanceWindow documents associated with a specific team ID.
- * @async
- * @function getMaintenanceWindowByUserId
- * @param {String} teamId - The ID of the team.
- * @param {Object} query - The request body.
- * @returns {Promise>} An array of MaintenanceWindow documents.
- * @throws {Error} If there is an error retrieving the documents.
- */
-const getMaintenanceWindowsByTeamId = async (teamId, query) => {
- try {
- let { active, page, rowsPerPage, field, order } = query || {};
- const maintenanceQuery = { teamId };
-
- if (active !== undefined) maintenanceQuery.active = active;
-
- const maintenanceWindowCount = await MaintenanceWindow.countDocuments(maintenanceQuery);
-
- // Pagination
- let skip = 0;
- if (page && rowsPerPage) {
- skip = page * rowsPerPage;
+ };
+ getMaintenanceWindowById = async ({ id, teamId }) => {
+ try {
+ const maintenanceWindow = await this.MaintenanceWindow.findOne({
+ _id: id,
+ teamId: teamId,
+ });
+ return maintenanceWindow;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "getMaintenanceWindowById";
+ throw error;
}
+ };
- // Sorting
- let sort = {};
- if (field !== undefined && order !== undefined) {
- sort[field] = order === "asc" ? 1 : -1;
+ getMaintenanceWindowsByTeamId = async (teamId, query) => {
+ try {
+ let { active, page, rowsPerPage, field, order } = query || {};
+ const maintenanceQuery = { teamId };
+
+ if (active !== undefined) maintenanceQuery.active = active;
+
+ const maintenanceWindowCount = await this.MaintenanceWindow.countDocuments(maintenanceQuery);
+
+ // Pagination
+ let skip = 0;
+ if (page && rowsPerPage) {
+ skip = page * rowsPerPage;
+ }
+
+ // Sorting
+ let sort = {};
+ if (field !== undefined && order !== undefined) {
+ sort[field] = order === "asc" ? 1 : -1;
+ }
+
+ const maintenanceWindows = await this.MaintenanceWindow.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort);
+
+ return { maintenanceWindows, maintenanceWindowCount };
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "getMaintenanceWindowByUserId";
+ throw error;
}
+ };
+ getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => {
+ try {
+ const maintenanceWindows = await this.MaintenanceWindow.find({
+ monitorId: monitorId,
+ teamId: teamId,
+ });
+ return maintenanceWindows;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "getMaintenanceWindowsByMonitorId";
+ throw error;
+ }
+ };
- const maintenanceWindows = await MaintenanceWindow.find(maintenanceQuery).skip(skip).limit(rowsPerPage).sort(sort);
+ deleteMaintenanceWindowById = async ({ id, teamId }) => {
+ try {
+ const maintenanceWindow = await this.MaintenanceWindow.findOneAndDelete({ _id: id, teamId });
+ return maintenanceWindow;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "deleteMaintenanceWindowById";
+ throw error;
+ }
+ };
- return { maintenanceWindows, maintenanceWindowCount };
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getMaintenanceWindowByUserId";
- throw error;
- }
-};
+ editMaintenanceWindowById = async ({ id, body }) => {
+ try {
+ const editedMaintenanceWindow = await this.MaintenanceWindow.findByIdAndUpdate(id, body, { new: true });
+ return editedMaintenanceWindow;
+ } catch (error) {
+ error.service = SERVICE_NAME;
+ error.method = "editMaintenanceWindowById";
+ throw error;
+ }
+ };
+}
-/**
- * Asynchronously retrieves all MaintenanceWindow documents associated with a specific monitor ID.
- * @async
- * @function getMaintenanceWindowsByMonitorId
- * @param {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor.
- * @returns {Promise>} An array of MaintenanceWindow documents.
- * @throws {Error} If there is an error retrieving the documents.
- */
-const getMaintenanceWindowsByMonitorId = async ({ monitorId, teamId }) => {
- try {
- const maintenanceWindows = await MaintenanceWindow.find({
- monitorId: monitorId,
- teamId: teamId,
- });
- return maintenanceWindows;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "getMaintenanceWindowsByMonitorId";
- throw error;
- }
-};
-
-/**
- * Asynchronously deletes a MaintenanceWindow document by its ID.
- * @async
- * @function deleteMaintenanceWindowById
- * @param {mongoose.Schema.Types.ObjectId} maintenanceWindowId - The ID of the MaintenanceWindow document to delete.
- * @returns {Promise} The deleted MaintenanceWindow document.
- * @throws {Error} If there is an error deleting the document.
- */
-const deleteMaintenanceWindowById = async ({ id, teamId }) => {
- try {
- const maintenanceWindow = await MaintenanceWindow.findOneAndDelete({ _id: id, teamId });
- return maintenanceWindow;
- } catch (error) {
- error.service = SERVICE_NAME;
- error.method = "deleteMaintenanceWindowById";
- throw error;
- }
-};
-
-/**
- * Asynchronously deletes all MaintenanceWindow documents associated with a specific monitor ID.
- * @async
- * @function deleteMaintenanceWindowByMonitorId
- * @param {mongoose.Schema.Types.ObjectId} monitorId - The ID of the monitor.
- * @returns {Promise