mirror of
https://github.com/bluewave-labs/Checkmate.git
synced 2026-01-26 11:54:11 -06:00
Merge branch 'develop' into feat/fe/wallet-adapter
This commit is contained in:
96
Client/package-lock.json
generated
96
Client/package-lock.json
generated
@@ -11,10 +11,10 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource/roboto": "^5.0.13",
|
||||
"@hello-pangea/dnd": "^17.0.0",
|
||||
"@mui/icons-material": "6.4.3",
|
||||
"@mui/lab": "6.0.0-beta.26",
|
||||
"@mui/material": "6.4.3",
|
||||
"@hello-pangea/dnd": "^18.0.0",
|
||||
"@mui/icons-material": "6.4.4",
|
||||
"@mui/lab": "6.0.0-beta.27",
|
||||
"@mui/material": "6.4.4",
|
||||
"@mui/x-charts": "^7.5.1",
|
||||
"@mui/x-data-grid": "7.26.0",
|
||||
"@mui/x-date-pickers": "7.26.0",
|
||||
@@ -450,6 +450,10 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.25.9.tgz",
|
||||
"integrity": "sha512-2qUwwfAFpJLZqxd02YW9btUCZHl+RFvdDkNfZwaIJrvB8Tesjsk8pEQkTvGwZXLqXUx/2oyY3ySRhm6HOXuCug==",
|
||||
"node_modules/@babel/runtime": {
|
||||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz",
|
||||
"integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -1161,6 +1165,21 @@
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
"node_modules/@hello-pangea/dnd": {
|
||||
"version": "18.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@hello-pangea/dnd/-/dnd-18.0.1.tgz",
|
||||
"integrity": "sha512-xojVWG8s/TGrKT1fC8K2tIWeejJYTAeJuj36zM//yEm/ZrnZUSFGS15BpO+jGZT1ybWvyXmeDJwPYb4dhWlbZQ==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.26.7",
|
||||
"css-box-model": "^1.2.1",
|
||||
"raf-schd": "^4.0.3",
|
||||
"react-redux": "^9.2.0",
|
||||
"redux": "^5.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-json-strings": {
|
||||
@@ -1349,6 +1368,10 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-numeric-separator/-/plugin-transform-numeric-separator-7.25.9.tgz",
|
||||
"integrity": "sha512-TlprrJ1GBZ3r6s96Yq8gEQv82s8/5HnCVHtEJScUj90thHQbwe+E5MLhi2bbNHBEJuzrvltXSru+BUxHDoog7Q==",
|
||||
"node_modules/@mui/core-downloads-tracker": {
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.4.tgz",
|
||||
"integrity": "sha512-r+J0EditrekkTtO2CnCBCOGpNaDYwJqz8lH4rj6o/anDcskZFJodBlG8aCJkS8DL/CF/9EHS+Gz53EbmYEnQbw==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -1365,6 +1388,10 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-rest-spread/-/plugin-transform-object-rest-spread-7.25.9.tgz",
|
||||
"integrity": "sha512-fSaXafEE9CVHPweLYw4J0emp1t8zYTXyzN3UuG+lylqkvYd7RMrsOQ8TYx5RF231be0vqtFC6jnx3UmpJmKBYg==",
|
||||
"node_modules/@mui/icons-material": {
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.4.tgz",
|
||||
"integrity": "sha512-uF1chGaoFmYdRUomK6f8kgJfWosk9A3HXWiVD0vQm+2mE7f25eTQ1E8RRO11LXpnUBqu8Rbv/uGlpnjT/u1Ksg==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -1383,6 +1410,20 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-object-super/-/plugin-transform-object-super-7.25.9.tgz",
|
||||
"integrity": "sha512-Kj/Gh+Rw2RNLbCK1VAWj2U48yxxqL2x0k10nPtSdRa0O2xnHXalD0s+o1A6a0W43gJ00ANo38jxkQreckOzv5A==",
|
||||
"@mui/material": "^6.4.4",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/lab": {
|
||||
"version": "6.0.0-beta.27",
|
||||
"resolved": "https://registry.npmjs.org/@mui/lab/-/lab-6.0.0-beta.27.tgz",
|
||||
"integrity": "sha512-weLxPsCs2wJKgWKf46shXHE+x7qlf5VxMK3P+4HsWasMakV/uTmxsoT7PG3QCvakGQ2TdpZtQLE2umJKC0mvKQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
@@ -1400,10 +1441,49 @@
|
||||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-optional-catch-binding/-/plugin-transform-optional-catch-binding-7.25.9.tgz",
|
||||
"integrity": "sha512-qM/6m6hQZzDcZF3onzIhZeDHDO43bkNNlOX0i8n3lR6zLbu0GN2d8qfM/IERJZYauhAHSLHy39NF0Ctdvcid7g==",
|
||||
"@emotion/react": "^11.5.0",
|
||||
"@emotion/styled": "^11.3.0",
|
||||
"@mui/material": "^6.4.4",
|
||||
"@mui/material-pigment-css": "^6.4.3",
|
||||
"@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react": "^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@emotion/styled": {
|
||||
"optional": true
|
||||
},
|
||||
"@mui/material-pigment-css": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@mui/material": {
|
||||
"version": "6.4.4",
|
||||
"resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.4.tgz",
|
||||
"integrity": "sha512-ISVPrIsPQsxnwvS40C4u03AuNSPigFeS2+n1qpuEZ94hDsdMi19dQM2JcC9CHEhXecSIQjP1RTyY0mPiSpSrFQ==",
|
||||
"license": "MIT",
|
||||
"peer": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.25.9"
|
||||
"@babel/runtime": "^7.26.0",
|
||||
"@mui/core-downloads-tracker": "^6.4.4",
|
||||
"@mui/system": "^6.4.3",
|
||||
"@mui/types": "^7.2.21",
|
||||
"@mui/utils": "^6.4.3",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@types/react-transition-group": "^4.4.12",
|
||||
"clsx": "^2.1.1",
|
||||
"csstype": "^3.1.3",
|
||||
"prop-types": "^15.8.1",
|
||||
"react-is": "^19.0.0",
|
||||
"react-transition-group": "^4.4.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
@@ -16881,6 +16961,10 @@
|
||||
"version": "10.0.6",
|
||||
"resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz",
|
||||
"integrity": "sha512-yYjp+omCDf9lhZcrZHKbSq7YMuK0zcYkDFTzfRFgTXkTFHZ1ToxwAonzA4JI5CxA91JpjFLmwEsZEgfYfOqI1A==",
|
||||
"node_modules/mime-db": {
|
||||
"version": "1.52.0",
|
||||
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
|
||||
"integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"clsx": "^2.1.0"
|
||||
@@ -17517,6 +17601,10 @@
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz",
|
||||
"integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==",
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
|
||||
"integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
"@emotion/react": "^11.13.3",
|
||||
"@emotion/styled": "^11.13.0",
|
||||
"@fontsource/roboto": "^5.0.13",
|
||||
"@hello-pangea/dnd": "^17.0.0",
|
||||
"@mui/icons-material": "6.4.3",
|
||||
"@mui/lab": "6.0.0-beta.26",
|
||||
"@mui/material": "6.4.3",
|
||||
"@hello-pangea/dnd": "^18.0.0",
|
||||
"@mui/icons-material": "6.4.4",
|
||||
"@mui/lab": "6.0.0-beta.27",
|
||||
"@mui/material": "6.4.4",
|
||||
"@mui/x-charts": "^7.5.1",
|
||||
"@mui/x-data-grid": "7.26.0",
|
||||
"@mui/x-date-pickers": "7.26.0",
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Box, MenuItem, Select, Stack } from "@mui/material";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import "flag-icons/css/flag-icons.min.css";
|
||||
import { useSelector } from "react-redux";
|
||||
|
||||
const LanguageSelector = () => {
|
||||
const { i18n } = useTranslation();
|
||||
const theme = useTheme();
|
||||
const [language, setLanguage] = useState(i18n.language || "gb");
|
||||
const { language } = useSelector((state) => state.ui);
|
||||
|
||||
const handleChange = (event) => {
|
||||
const newLang = event.target.value;
|
||||
setLanguage(newLang);
|
||||
i18n.changeLanguage(newLang);
|
||||
};
|
||||
|
||||
|
||||
14
Client/src/Components/Skeletons/FullPage/index.jsx
Normal file
14
Client/src/Components/Skeletons/FullPage/index.jsx
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Stack, Skeleton } from "@mui/material";
|
||||
|
||||
export const SkeletonLayout = () => {
|
||||
return (
|
||||
<Stack>
|
||||
<Skeleton
|
||||
variant="rectangular"
|
||||
height={"90vh"}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default SkeletonLayout;
|
||||
@@ -23,6 +23,7 @@ const initialState = {
|
||||
greeting: { index: 0, lastUpdate: null },
|
||||
timezone: "America/Toronto",
|
||||
distributedUptimeEnabled: false,
|
||||
language: "gb",
|
||||
};
|
||||
|
||||
const uiSlice = createSlice({
|
||||
@@ -51,6 +52,9 @@ const uiSlice = createSlice({
|
||||
setTimezone(state, action) {
|
||||
state.timezone = action.payload.timezone;
|
||||
},
|
||||
setLanguage(state, action) {
|
||||
state.language = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -62,4 +66,5 @@ export const {
|
||||
setGreeting,
|
||||
setTimezone,
|
||||
setDistributedUptimeEnabled,
|
||||
setLanguage,
|
||||
} = uiSlice.actions;
|
||||
|
||||
@@ -92,7 +92,7 @@ export const updateUptimeMonitor = createAsyncThunk(
|
||||
const res = await networkService.updateMonitor({
|
||||
authToken: authToken,
|
||||
monitorId: monitor._id,
|
||||
updatedFields: updatedFields,
|
||||
monitor,
|
||||
});
|
||||
return res.data;
|
||||
} catch (error) {
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
import { useState } from "react";
|
||||
import { networkService } from "../../../../main";
|
||||
import { useSelector } from "react-redux";
|
||||
import { createToast } from "../../../../Utils/toastUtils";
|
||||
|
||||
const useCreateDistributedUptimeMonitor = ({ isCreate, monitorId }) => {
|
||||
const { authToken, user } = useSelector((state) => state.auth);
|
||||
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [networkError, setNetworkError] = useState(false);
|
||||
const createDistributedUptimeMonitor = async ({ form }) => {
|
||||
setIsLoading(true);
|
||||
try {
|
||||
if (isCreate) {
|
||||
await networkService.createMonitor({ authToken, monitor: form });
|
||||
} else {
|
||||
await networkService.updateMonitor({ authToken, monitor: form, monitorId });
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
setNetworkError(true);
|
||||
createToast({ body: error?.response?.data?.msg ?? error.message });
|
||||
return false;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [createDistributedUptimeMonitor, isLoading, networkError];
|
||||
};
|
||||
|
||||
export { useCreateDistributedUptimeMonitor };
|
||||
@@ -0,0 +1,32 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { networkService } from "../../../../main";
|
||||
import { createToast } from "../../../../Utils/toastUtils";
|
||||
|
||||
export const useMonitorFetch = ({ authToken, monitorId, isCreate }) => {
|
||||
const [networkError, setNetworkError] = useState(false);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [monitor, setMonitor] = useState(undefined);
|
||||
|
||||
useEffect(() => {
|
||||
const fetchMonitors = async () => {
|
||||
try {
|
||||
if (isCreate) return;
|
||||
const res = await networkService.getUptimeDetailsById({
|
||||
authToken: authToken,
|
||||
monitorId: monitorId,
|
||||
normalize: true,
|
||||
});
|
||||
setMonitor(res?.data?.data ?? {});
|
||||
} catch (error) {
|
||||
setNetworkError(true);
|
||||
createToast({ body: error.message });
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
fetchMonitors();
|
||||
}, [authToken, monitorId, isCreate]);
|
||||
return [monitor, isLoading, networkError];
|
||||
};
|
||||
|
||||
export default useMonitorFetch;
|
||||
@@ -13,18 +13,15 @@ import { createToast } from "../../../Utils/toastUtils";
|
||||
|
||||
// Utility
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useState } from "react";
|
||||
import { useState, useEffect } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useSelector, useDispatch } from "react-redux";
|
||||
import { useSelector } from "react-redux";
|
||||
import { monitorValidation } from "../../../Validation/validation";
|
||||
import { createUptimeMonitor } from "../../../Features/UptimeMonitors/uptimeMonitorsSlice";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useCreateDistributedUptimeMonitor } from "./Hooks/useCreateDistributedUptimeMonitor";
|
||||
import { useMonitorFetch } from "./Hooks/useMonitorFetch";
|
||||
|
||||
// Constants
|
||||
const BREADCRUMBS = [
|
||||
{ name: `distributed uptime`, path: "/distributed-uptime" },
|
||||
{ name: "create", path: `/distributed-uptime/create` },
|
||||
];
|
||||
const MS_PER_MINUTE = 60000;
|
||||
const SELECT_VALUES = [
|
||||
{ _id: 1, name: "1 minute" },
|
||||
@@ -34,18 +31,30 @@ const SELECT_VALUES = [
|
||||
{ _id: 5, name: "5 minutes" },
|
||||
];
|
||||
|
||||
const parseUrl = (url) => {
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
const CreateDistributedUptime = () => {
|
||||
const location = useLocation();
|
||||
const isCreate = location.pathname.startsWith("/distributed-uptime/create");
|
||||
const { monitorId } = useParams();
|
||||
const isCreate = typeof monitorId === "undefined";
|
||||
|
||||
const BREADCRUMBS = [
|
||||
{ name: `distributed uptime`, path: "/distributed-uptime" },
|
||||
{ name: isCreate ? "create" : "configure", path: `` },
|
||||
];
|
||||
|
||||
// Redux state
|
||||
const { user, authToken } = useSelector((state) => state.auth);
|
||||
const isLoading = useSelector((state) => state.uptimeMonitors.isLoading);
|
||||
|
||||
// Local state
|
||||
const [https, setHttps] = useState(true);
|
||||
const [notifications, setNotifications] = useState([]);
|
||||
const [monitor, setMonitor] = useState({
|
||||
const [form, setForm] = useState({
|
||||
type: "distributed_http",
|
||||
name: "",
|
||||
url: "",
|
||||
@@ -55,12 +64,37 @@ const CreateDistributedUptime = () => {
|
||||
|
||||
//utils
|
||||
const theme = useTheme();
|
||||
const dispatch = useDispatch();
|
||||
const navigate = useNavigate();
|
||||
const [createDistributedUptimeMonitor, isLoading, networkError] =
|
||||
useCreateDistributedUptimeMonitor({ isCreate, monitorId });
|
||||
|
||||
const [monitor, monitorIsLoading, monitorNetworkError] = useMonitorFetch({
|
||||
authToken,
|
||||
monitorId,
|
||||
isCreate,
|
||||
});
|
||||
|
||||
// Effect to set monitor to fetched monitor
|
||||
useEffect(() => {
|
||||
if (typeof monitor !== "undefined") {
|
||||
const parsedUrl = parseUrl(monitor?.url);
|
||||
const protocol = parsedUrl?.protocol?.replace(":", "") || "";
|
||||
setHttps(protocol === "https");
|
||||
|
||||
const newForm = {
|
||||
name: monitor.name,
|
||||
interval: monitor.interval / MS_PER_MINUTE,
|
||||
url: parsedUrl.host,
|
||||
type: monitor.type,
|
||||
};
|
||||
|
||||
setForm(newForm);
|
||||
}
|
||||
}, [monitor]);
|
||||
|
||||
// Handlers
|
||||
const handleCreateMonitor = async (event) => {
|
||||
const monitorToSubmit = { ...monitor };
|
||||
const handleCreateMonitor = async () => {
|
||||
const monitorToSubmit = { ...form };
|
||||
|
||||
// Prepend protocol to url
|
||||
monitorToSubmit.url = `http${https ? "s" : ""}://` + monitorToSubmit.url;
|
||||
@@ -68,7 +102,6 @@ const CreateDistributedUptime = () => {
|
||||
const { error } = monitorValidation.validate(monitorToSubmit, {
|
||||
abortEarly: false,
|
||||
});
|
||||
|
||||
if (error) {
|
||||
const newErrors = {};
|
||||
error.details.forEach((err) => {
|
||||
@@ -80,16 +113,14 @@ const CreateDistributedUptime = () => {
|
||||
}
|
||||
|
||||
// Append needed fields
|
||||
monitorToSubmit.description = monitor.name;
|
||||
monitorToSubmit.interval = monitor.interval * MS_PER_MINUTE;
|
||||
monitorToSubmit.description = form.name;
|
||||
monitorToSubmit.interval = form.interval * MS_PER_MINUTE;
|
||||
monitorToSubmit.teamId = user.teamId;
|
||||
monitorToSubmit.userId = user._id;
|
||||
monitorToSubmit.notifications = notifications;
|
||||
|
||||
const action = await dispatch(
|
||||
createUptimeMonitor({ authToken, monitor: monitorToSubmit })
|
||||
);
|
||||
if (action.meta.requestStatus === "fulfilled") {
|
||||
const success = await createDistributedUptimeMonitor({ form: monitorToSubmit });
|
||||
if (success) {
|
||||
createToast({ body: "Monitor created successfully!" });
|
||||
navigate("/distributed-uptime");
|
||||
} else {
|
||||
@@ -98,9 +129,10 @@ const CreateDistributedUptime = () => {
|
||||
};
|
||||
|
||||
const handleChange = (event) => {
|
||||
const { name, value } = event.target;
|
||||
setMonitor({
|
||||
...monitor,
|
||||
let { name, value } = event.target;
|
||||
|
||||
setForm({
|
||||
...form,
|
||||
[name]: value,
|
||||
});
|
||||
const { error } = monitorValidation.validate(
|
||||
@@ -178,7 +210,8 @@ const CreateDistributedUptime = () => {
|
||||
label="URL to monitor"
|
||||
https={https}
|
||||
placeholder={"www.google.com"}
|
||||
value={monitor.url}
|
||||
disabled={!isCreate}
|
||||
value={form.url}
|
||||
name="url"
|
||||
onChange={handleChange}
|
||||
error={errors["url"] ? true : false}
|
||||
@@ -190,7 +223,7 @@ const CreateDistributedUptime = () => {
|
||||
label="Display name"
|
||||
isOptional={true}
|
||||
placeholder={"Google"}
|
||||
value={monitor.name}
|
||||
value={form.name}
|
||||
name="name"
|
||||
onChange={handleChange}
|
||||
error={errors["name"] ? true : false}
|
||||
@@ -217,8 +250,11 @@ const CreateDistributedUptime = () => {
|
||||
checked={true}
|
||||
onChange={handleChange}
|
||||
/>
|
||||
{monitor.type === "http" || monitor.type === "distributed_http" ? (
|
||||
<ButtonGroup sx={{ ml: theme.spacing(16) }}>
|
||||
{form.type === "http" || form.type === "distributed_http" ? (
|
||||
<ButtonGroup
|
||||
disabled={!isCreate}
|
||||
sx={{ ml: theme.spacing(16) }}
|
||||
>
|
||||
<Button
|
||||
variant="group"
|
||||
filled={https.toString()}
|
||||
@@ -282,7 +318,7 @@ const CreateDistributedUptime = () => {
|
||||
id="monitor-interval"
|
||||
label="Check frequency"
|
||||
name="interval"
|
||||
value={monitor.interval || 1}
|
||||
value={form.interval}
|
||||
onChange={handleChange}
|
||||
items={SELECT_VALUES}
|
||||
/>
|
||||
@@ -299,7 +335,7 @@ const CreateDistributedUptime = () => {
|
||||
disabled={!Object.values(errors).every((value) => value === undefined)}
|
||||
loading={isLoading}
|
||||
>
|
||||
Create monitor
|
||||
{isCreate ? "Create monitor" : "Configure monitor"}
|
||||
</LoadingButton>
|
||||
</Stack>
|
||||
</Stack>
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
// Components
|
||||
import { Box, Stack, Typography, Button } from "@mui/material";
|
||||
import Image from "../../../../../Components/Image";
|
||||
import SettingsIcon from "../../../../../assets/icons/settings-bold.svg?react";
|
||||
|
||||
//Utils
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
const Controls = ({ isDeleteOpen, setIsDeleteOpen, isDeleting, monitorId }) => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="error"
|
||||
onClick={() => setIsDeleteOpen(!isDeleteOpen)}
|
||||
loading={isDeleting}
|
||||
>
|
||||
Delete
|
||||
</Button>
|
||||
</Box>
|
||||
<Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
navigate(`/distributed-uptime/configure/${monitorId}`);
|
||||
}}
|
||||
sx={{
|
||||
px: theme.spacing(5),
|
||||
"& svg": {
|
||||
mr: theme.spacing(3),
|
||||
"& path": {
|
||||
stroke: theme.palette.secondary.contrastText,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
<SettingsIcon /> Configure
|
||||
</Button>
|
||||
</Box>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
Controls.propTypes = {
|
||||
isDeleting: PropTypes.bool,
|
||||
monitorId: PropTypes.string,
|
||||
isDeleteOpen: PropTypes.bool.isRequired,
|
||||
setIsDeleteOpen: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
const ControlsHeader = ({ isDeleting, isDeleteOpen, setIsDeleteOpen, monitorId }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<Stack
|
||||
alignSelf="flex-start"
|
||||
direction="row"
|
||||
width="100%"
|
||||
gap={theme.spacing(2)}
|
||||
justifyContent="flex-end"
|
||||
alignItems="flex-end"
|
||||
>
|
||||
<Controls
|
||||
isDeleting={isDeleting}
|
||||
isDeleteOpen={isDeleteOpen}
|
||||
setIsDeleteOpen={setIsDeleteOpen}
|
||||
monitorId={monitorId}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
ControlsHeader.propTypes = {
|
||||
monitorId: PropTypes.string,
|
||||
isDeleting: PropTypes.bool,
|
||||
isDeleteOpen: PropTypes.bool.isRequired,
|
||||
setIsDeleteOpen: PropTypes.func.isRequired,
|
||||
};
|
||||
|
||||
export default ControlsHeader;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useSelector } from "react-redux";
|
||||
import { useState } from "react";
|
||||
import { networkService } from "../../../../main";
|
||||
import { createToast } from "../../../../Utils/toastUtils";
|
||||
|
||||
const useDeleteMonitor = ({ monitorId }) => {
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const { authToken } = useSelector((state) => state.auth);
|
||||
const deleteMonitor = async () => {
|
||||
try {
|
||||
setIsLoading(true);
|
||||
await networkService.deleteMonitorById({ authToken, monitorId });
|
||||
return true;
|
||||
} catch (error) {
|
||||
createToast({
|
||||
body: error.message,
|
||||
});
|
||||
return false;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return [deleteMonitor, isLoading];
|
||||
};
|
||||
|
||||
export { useDeleteMonitor };
|
||||
@@ -12,23 +12,31 @@ import MonitorTimeFrameHeader from "../../../Components/MonitorTimeFrameHeader";
|
||||
import GenericFallback from "../../../Components/GenericFallback";
|
||||
import MonitorCreateHeader from "../../../Components/MonitorCreateHeader";
|
||||
import SkeletonLayout from "./Components/Skeleton";
|
||||
import ControlsHeader from "./Components/ControlsHeader";
|
||||
import Dialog from "../../../Components/Dialog";
|
||||
//Utils
|
||||
import { useTheme } from "@mui/material/styles";
|
||||
import { useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
|
||||
import { useSubscribeToDetails } from "./Hooks/useSubscribeToDetails";
|
||||
import { useDeleteMonitor } from "./Hooks/useDeleteMonitor";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
const DistributedUptimeDetails = () => {
|
||||
const { monitorId } = useParams();
|
||||
// Local State
|
||||
const [dateRange, setDateRange] = useState("day");
|
||||
const [isDeleteOpen, setIsDeleteOpen] = useState(false);
|
||||
|
||||
// Utils
|
||||
const theme = useTheme();
|
||||
const isAdmin = useIsAdmin();
|
||||
const navigate = useNavigate();
|
||||
const [isLoading, networkError, connectionStatus, monitor, lastUpdateTrigger] =
|
||||
useSubscribeToDetails({ monitorId, dateRange });
|
||||
|
||||
const [deleteMonitor, isDeleting] = useDeleteMonitor({ monitorId });
|
||||
// Constants
|
||||
const BREADCRUMBS = [
|
||||
{ name: "Distributed Uptime", path: "/distributed-uptime" },
|
||||
@@ -76,6 +84,12 @@ const DistributedUptimeDetails = () => {
|
||||
isAdmin={isAdmin}
|
||||
path={`/status/distributed/create/${monitorId}`}
|
||||
/>
|
||||
<ControlsHeader
|
||||
isDeleting={isDeleting}
|
||||
isDeleteOpen={isDeleteOpen}
|
||||
setIsDeleteOpen={setIsDeleteOpen}
|
||||
monitorId={monitorId}
|
||||
/>
|
||||
<MonitorHeader monitor={monitor} />
|
||||
<StatBoxes
|
||||
monitor={monitor}
|
||||
@@ -107,6 +121,21 @@ const DistributedUptimeDetails = () => {
|
||||
/>
|
||||
</Stack>
|
||||
<Footer />
|
||||
<Dialog
|
||||
title="Do you want to delete this monitor?"
|
||||
onConfirm={() => {
|
||||
deleteMonitor();
|
||||
setIsDeleteOpen(false);
|
||||
navigate("/distributed-uptime");
|
||||
}}
|
||||
onCancel={() => {
|
||||
setIsDeleteOpen(false);
|
||||
}}
|
||||
open={isDeleteOpen}
|
||||
confirmationButtonLabel="Yes, delete monitor"
|
||||
description="Once deleted, your monitor cannot be retrieved."
|
||||
isLoading={isDeleting || isLoading}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -29,7 +29,7 @@ const MonitorTable = ({ isLoading, monitors }) => {
|
||||
<Host
|
||||
key={row._id}
|
||||
url={row.url}
|
||||
title={row.title}
|
||||
title={row.name}
|
||||
percentageColor={row.percentageColor}
|
||||
percentage={row.percentage}
|
||||
/>
|
||||
|
||||
@@ -41,7 +41,7 @@ const DistributedUptimeMonitors = () => {
|
||||
);
|
||||
}
|
||||
|
||||
if (typeof monitorsSummary !== "undefined" && monitorsSummary.totalMonitors === 0) {
|
||||
if (typeof monitorsSummary === "undefined" || monitorsSummary.totalMonitors === 0) {
|
||||
return (
|
||||
<Fallback
|
||||
vowelStart={false}
|
||||
|
||||
@@ -13,7 +13,6 @@ import { useState, useEffect } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useStatusPageFetch } from "../../StatusPage/Status/Hooks/useStatusPageFetch";
|
||||
import { useCreateStatusPage } from "../../StatusPage/Create/Hooks/useCreateStatusPage";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { statusPageValidation } from "../../../Validation/validation";
|
||||
import { buildErrors } from "../../../Validation/error";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
@@ -23,8 +22,7 @@ const CreateStatus = () => {
|
||||
const theme = useTheme();
|
||||
const { monitorId, url } = useParams();
|
||||
const navigate = useNavigate();
|
||||
const location = useLocation();
|
||||
const isCreate = location.pathname.startsWith("/status/distributed/create");
|
||||
const isCreate = typeof url === "undefined";
|
||||
const [createStatusPage, isLoading, networkError] = useCreateStatusPage(isCreate);
|
||||
|
||||
const [statusPage, statusPageMonitors, statusPageIsLoading, statusPageNetworkError] =
|
||||
|
||||
@@ -75,9 +75,9 @@ const DistributedUptimeStatus = () => {
|
||||
marginY={theme.spacing(4)}
|
||||
color={theme.palette.primary.contrastTextTertiary}
|
||||
>
|
||||
A public status page is not set up.
|
||||
A status page is not set up.
|
||||
</Typography>
|
||||
<Typography>Please contact to your administrator</Typography>
|
||||
<Typography>Please contact your administrator</Typography>
|
||||
</GenericFallback>
|
||||
</Stack>
|
||||
);
|
||||
|
||||
@@ -34,7 +34,6 @@ const MaintenanceTable = ({
|
||||
updateCallback,
|
||||
}) => {
|
||||
const { rowsPerPage } = useSelector((state) => state.ui.maintenance);
|
||||
console.log(rowsPerPage);
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const handleChangePage = (event, newPage) => {
|
||||
@@ -175,8 +174,6 @@ const MaintenanceTable = ({
|
||||
setSort({ field, order });
|
||||
};
|
||||
|
||||
console.log(handleChangePage);
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTable
|
||||
|
||||
@@ -12,7 +12,6 @@ import { useMonitorsFetch } from "./Hooks/useMonitorsFetch";
|
||||
import { useCreateStatusPage } from "./Hooks/useCreateStatusPage";
|
||||
import { createToast } from "../../../Utils/toastUtils";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useStatusPageFetch } from "../Status/Hooks/useStatusPageFetch";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
@@ -47,8 +46,7 @@ const CreateStatusPage = () => {
|
||||
const intervalRef = useRef(null);
|
||||
|
||||
// Setup
|
||||
const location = useLocation();
|
||||
const isCreate = location.pathname === "/status/uptime/create";
|
||||
const isCreate = typeof url === "undefined";
|
||||
|
||||
//Utils
|
||||
const theme = useTheme();
|
||||
@@ -134,7 +132,11 @@ const CreateStatusPage = () => {
|
||||
if (typeof error === "undefined") {
|
||||
const success = await createStatusPage({ form });
|
||||
if (success) {
|
||||
createToast({ body: "Status page created successfully" });
|
||||
createToast({
|
||||
body: isCreate
|
||||
? "Status page created successfully"
|
||||
: "Status page updated successfully",
|
||||
});
|
||||
navigate(`/status/uptime/${form.url}`);
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import DataTable from "../../../../../Components/Table";
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { ColoredLabel } from "../../../../../Components/Label";
|
||||
import ArrowOutwardIcon from "@mui/icons-material/ArrowOutward";
|
||||
import { Stack, Typography } from "@mui/material";
|
||||
const StatusPagesTable = ({ data }) => {
|
||||
const theme = useTheme();
|
||||
const navigate = useNavigate();
|
||||
const headers = [
|
||||
{
|
||||
id: "name",
|
||||
content: "Status page name",
|
||||
render: (row) => {
|
||||
return row.companyName;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "url",
|
||||
content: "URL",
|
||||
render: (row) => {
|
||||
return (
|
||||
<Stack
|
||||
direction="row"
|
||||
alignItems="center"
|
||||
gap={theme.spacing(2)}
|
||||
>
|
||||
<Typography>{`/${row.url}`}</Typography>
|
||||
<ArrowOutwardIcon />
|
||||
</Stack>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "type",
|
||||
content: "Type",
|
||||
render: (row) => {
|
||||
return row.type;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
content: "Status",
|
||||
render: (row) => {
|
||||
return (
|
||||
<ColoredLabel
|
||||
label={row.isPublished ? "Published" : "Unpublished"}
|
||||
color={
|
||||
row.isPublished ? theme.palette.success.main : theme.palette.warning.main
|
||||
}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
const handleRowClick = (statusPage) => {
|
||||
if (statusPage.type === "distributed") {
|
||||
navigate(`/status/distributed/${statusPage.url}`);
|
||||
} else if (statusPage.type === "uptime") {
|
||||
navigate(`/status/uptime/${statusPage.url}`);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<DataTable
|
||||
config={{
|
||||
rowSX: {
|
||||
cursor: "pointer",
|
||||
"&:hover td": {
|
||||
backgroundColor: theme.palette.tertiary.main,
|
||||
transition: "background-color .3s ease",
|
||||
},
|
||||
},
|
||||
onRowClick: (row) => {
|
||||
handleRowClick(row);
|
||||
},
|
||||
}}
|
||||
headers={headers}
|
||||
data={data}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default StatusPagesTable;
|
||||
@@ -20,7 +20,9 @@ const useStatusPagesFetch = () => {
|
||||
setStatusPages(res?.data?.data);
|
||||
} catch (error) {
|
||||
setNetworkError(true);
|
||||
createToast(error.message, "error");
|
||||
createToast({
|
||||
body: error.message,
|
||||
});
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
|
||||
@@ -4,11 +4,12 @@ import Breadcrumbs from "../../../Components/Breadcrumbs";
|
||||
import Fallback from "../../../Components/Fallback";
|
||||
import MonitorCreateHeader from "../../../Components/MonitorCreateHeader";
|
||||
import GenericFallback from "../../../Components/GenericFallback";
|
||||
import StatusPagesTable from "./Components/StatusPagesTable";
|
||||
import SkeletonLayout from "../../../Components/Skeletons/FullPage";
|
||||
// Utils
|
||||
import { useTheme } from "@emotion/react";
|
||||
import { useStatusPagesFetch } from "./Hooks/useStatusPagesFetch";
|
||||
import { useIsAdmin } from "../../../Hooks/useIsAdmin";
|
||||
import { useNavigate } from "react-router";
|
||||
const BREADCRUMBS = [{ name: `Status Pages`, path: "" }];
|
||||
|
||||
const StatusPages = () => {
|
||||
@@ -16,29 +17,9 @@ const StatusPages = () => {
|
||||
const theme = useTheme();
|
||||
const isAdmin = useIsAdmin();
|
||||
const [isLoading, networkError, statusPages] = useStatusPagesFetch();
|
||||
const navigate = useNavigate();
|
||||
|
||||
// Handlers
|
||||
const handleStatusPageClick = (statusPage) => {
|
||||
if (statusPage.type === "distributed") {
|
||||
navigate(`/status/distributed/${statusPage.url}`);
|
||||
} else if (statusPage.type === "uptime") {
|
||||
navigate(`/status/uptime/${statusPage.url}`);
|
||||
}
|
||||
};
|
||||
|
||||
if (!isLoading && typeof statusPages === "undefined") {
|
||||
return (
|
||||
<Fallback
|
||||
title="status page"
|
||||
checks={[
|
||||
"Display a list of monitors to track",
|
||||
"Share your monitors with the public",
|
||||
]}
|
||||
link="/status/uptime/create"
|
||||
isAdmin={isAdmin}
|
||||
/>
|
||||
);
|
||||
if (isLoading) {
|
||||
return <SkeletonLayout />;
|
||||
}
|
||||
|
||||
if (networkError === true) {
|
||||
@@ -55,6 +36,21 @@ const StatusPages = () => {
|
||||
</GenericFallback>
|
||||
);
|
||||
}
|
||||
|
||||
if (!isLoading && typeof statusPages !== "undefined" && statusPages.length === 0) {
|
||||
return (
|
||||
<Fallback
|
||||
title="status page"
|
||||
checks={[
|
||||
"Monitor and display the health of your services in real time",
|
||||
"Track multiple services and share their status",
|
||||
"Keep users informed about outages and performance",
|
||||
]}
|
||||
link="/status/uptime/create"
|
||||
isAdmin={isAdmin}
|
||||
/>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<Stack gap={theme.spacing(10)}>
|
||||
<Breadcrumbs list={BREADCRUMBS} />
|
||||
@@ -63,19 +59,7 @@ const StatusPages = () => {
|
||||
isAdmin={isAdmin}
|
||||
path="/status/uptime/create"
|
||||
/>
|
||||
{statusPages?.map((statusPage) => {
|
||||
return (
|
||||
<Stack
|
||||
key={statusPage._id}
|
||||
onClick={() => handleStatusPageClick(statusPage)}
|
||||
sx={{ cursor: "pointer" }}
|
||||
>
|
||||
<Typography variant="h2">Company Name: {statusPage.companyName}</Typography>
|
||||
<Typography variant="h2">Status page URL: {statusPage.url}</Typography>
|
||||
<Typography variant="h2">Type: {statusPage.type}</Typography>
|
||||
</Stack>
|
||||
);
|
||||
})}
|
||||
<StatusPagesTable data={statusPages} />
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -85,9 +85,9 @@ const ResponseGaugeChart = ({ avgResponseTime }) => {
|
||||
</text>
|
||||
<text
|
||||
x="50%"
|
||||
y="55%"
|
||||
y="70%"
|
||||
textAnchor="middle"
|
||||
dominantBaseline="hanging"
|
||||
alignmentBaseline="hanging"
|
||||
fontSize={25}
|
||||
>
|
||||
<tspan fontWeight={600}>{responseTime}</tspan> <tspan opacity={0.8}>ms</tspan>
|
||||
|
||||
@@ -101,7 +101,7 @@ const UptimeDataTable = ({
|
||||
<Host
|
||||
key={row._id}
|
||||
url={row.url}
|
||||
title={row.title}
|
||||
title={row.name}
|
||||
percentageColor={row.percentageColor}
|
||||
percentage={row.percentage}
|
||||
/>
|
||||
|
||||
@@ -107,6 +107,14 @@ const Routes = () => {
|
||||
</ProtectedDistributedUptimeRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/distributed-uptime/configure/:monitorId"
|
||||
element={
|
||||
<ProtectedDistributedUptimeRoute>
|
||||
<CreateDistributedUptime />
|
||||
</ProtectedDistributedUptimeRoute>
|
||||
}
|
||||
/>
|
||||
<Route
|
||||
path="/distributed-uptime/:monitorId"
|
||||
element={
|
||||
|
||||
@@ -99,9 +99,11 @@ class NetworkService {
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios POST request.
|
||||
*/
|
||||
async createMonitor(config) {
|
||||
return this.axiosInstance.post(`/monitors`, config.monitor, {
|
||||
const { authToken, monitor } = config;
|
||||
|
||||
return this.axiosInstance.post(`/monitors`, monitor, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.authToken}`,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
@@ -284,9 +286,16 @@ class NetworkService {
|
||||
* @returns {Promise<AxiosResponse>} The response from the axios PUT request.
|
||||
*/
|
||||
async updateMonitor(config) {
|
||||
return this.axiosInstance.put(`/monitors/${config.monitorId}`, config.updatedFields, {
|
||||
const { authToken, monitorId, monitor } = config;
|
||||
const updatedFields = {
|
||||
name: monitor.name,
|
||||
description: monitor.description,
|
||||
interval: monitor.interval,
|
||||
notifications: monitor.notifications,
|
||||
};
|
||||
return this.axiosInstance.put(`/monitors/${monitorId}`, updatedFields, {
|
||||
headers: {
|
||||
Authorization: `Bearer ${config.authToken}`,
|
||||
Authorization: `Bearer ${authToken}`,
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
@@ -1015,11 +1024,12 @@ class NetworkService {
|
||||
|
||||
async createStatusPage(config) {
|
||||
const { authToken, user, form, isCreate } = config;
|
||||
|
||||
const fd = new FormData();
|
||||
fd.append("teamId", user.teamId);
|
||||
fd.append("userId", user._id);
|
||||
fd.append("type", form.type);
|
||||
form.isPublished && fd.append("isPublished", form.isPublished);
|
||||
form.isPublished !== undefined && fd.append("isPublished", form.isPublished);
|
||||
form.companyName && fd.append("companyName", form.companyName);
|
||||
form.url && fd.append("url", form.url);
|
||||
form.timezone && fd.append("timezone", form.timezone);
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import i18n from "i18next";
|
||||
import { initReactI18next } from "react-i18next";
|
||||
import { setLanguage } from "../Features/UI/uiSlice";
|
||||
import store from "../store";
|
||||
|
||||
const primaryLanguage = "gb";
|
||||
|
||||
@@ -13,11 +15,12 @@ Object.keys(translations).forEach((path) => {
|
||||
};
|
||||
});
|
||||
|
||||
const savedLanguage = localStorage.getItem("language") || primaryLanguage;
|
||||
const savedLanguage = store.getState()?.ui?.language;
|
||||
const initialLanguage = savedLanguage || primaryLanguage;
|
||||
|
||||
i18n.use(initReactI18next).init({
|
||||
resources,
|
||||
lng: savedLanguage,
|
||||
lng: initialLanguage,
|
||||
fallbackLng: primaryLanguage,
|
||||
debug: import.meta.env.MODE === "development",
|
||||
ns: ["translation"],
|
||||
@@ -28,7 +31,7 @@ i18n.use(initReactI18next).init({
|
||||
});
|
||||
|
||||
i18n.on("languageChanged", (lng) => {
|
||||
localStorage.setItem("language", lng);
|
||||
store.dispatch(setLanguage(lng));
|
||||
});
|
||||
|
||||
export default i18n;
|
||||
|
||||
@@ -8,7 +8,6 @@ import {
|
||||
newPasswordValidation,
|
||||
} from "../validation/joi.js";
|
||||
import logger from "../utils/logger.js";
|
||||
import { errorMessages, successMessages } from "../utils/messages.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { getTokenFromHeaders, tokenType } from "../utils/utils.js";
|
||||
import crypto from "crypto";
|
||||
@@ -16,11 +15,12 @@ import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
const SERVICE_NAME = "authController";
|
||||
|
||||
class AuthController {
|
||||
constructor(db, settingsService, emailService, jobQueue) {
|
||||
constructor(db, settingsService, emailService, jobQueue, stringService) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.emailService = emailService;
|
||||
this.jobQueue = jobQueue;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -85,7 +85,7 @@ class AuthController {
|
||||
|
||||
const newUser = await this.db.insertUser({ ...req.body }, req.file);
|
||||
logger.info({
|
||||
message: successMessages.AUTH_CREATE_USER,
|
||||
message: this.stringService.authCreateUser,
|
||||
service: SERVICE_NAME,
|
||||
details: newUser._id,
|
||||
});
|
||||
@@ -116,7 +116,7 @@ class AuthController {
|
||||
});
|
||||
|
||||
res.success({
|
||||
msg: successMessages.AUTH_CREATE_USER,
|
||||
msg: this.stringService.authCreateUser,
|
||||
data: { user: newUser, token: token, refreshToken: refreshToken },
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -153,7 +153,7 @@ class AuthController {
|
||||
// Compare password
|
||||
const match = await user.comparePassword(password);
|
||||
if (match !== true) {
|
||||
const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD);
|
||||
const error = new Error(this.stringService.authIncorrectPassword);
|
||||
error.status = 401;
|
||||
next(error);
|
||||
return;
|
||||
@@ -176,7 +176,7 @@ class AuthController {
|
||||
userWithoutPassword.avatarImage = user.avatarImage;
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.AUTH_LOGIN_USER,
|
||||
msg: this.stringService.authLoginUser,
|
||||
data: {
|
||||
user: userWithoutPassword,
|
||||
token: token,
|
||||
@@ -200,13 +200,14 @@ class AuthController {
|
||||
* @throws {Error} If there is an error during the process such as any of the token is not received
|
||||
*/
|
||||
refreshAuthToken = async (req, res, next) => {
|
||||
|
||||
try {
|
||||
// check for refreshToken
|
||||
const refreshToken = req.headers["x-refresh-token"];
|
||||
|
||||
if (!refreshToken) {
|
||||
// No refresh token provided
|
||||
const error = new Error(errorMessages.NO_REFRESH_TOKEN);
|
||||
const error = new Error(this.stringService.noRefreshToken);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "refreshAuthToken";
|
||||
@@ -221,8 +222,8 @@ class AuthController {
|
||||
// Invalid or expired refresh token, trigger logout
|
||||
const errorMessage =
|
||||
refreshErr.name === "TokenExpiredError"
|
||||
? errorMessages.EXPIRED_REFRESH_TOKEN
|
||||
: errorMessages.INVALID_REFRESH_TOKEN;
|
||||
? this.stringService.expiredAuthToken
|
||||
: this.stringService.invalidAuthToken;
|
||||
const error = new Error(errorMessage);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
@@ -243,7 +244,7 @@ class AuthController {
|
||||
);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.AUTH_TOKEN_REFRESHED,
|
||||
msg: this.stringService.authTokenRefreshed,
|
||||
data: { user: payloadData, token: newAuthToken, refreshToken: refreshToken },
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -265,6 +266,7 @@ class AuthController {
|
||||
* @throws {Error} If there is an error during the process, especially if there is a validation error (422), the user is unauthorized (401), or the password is incorrect (403).
|
||||
*/
|
||||
editUser = async (req, res, next) => {
|
||||
|
||||
try {
|
||||
await editUserParamValidation.validateAsync(req.params);
|
||||
await editUserBodyValidation.validateAsync(req.body);
|
||||
@@ -276,7 +278,7 @@ class AuthController {
|
||||
|
||||
// TODO is this neccessary any longer? Verify ownership middleware should handle this
|
||||
if (req.params.userId !== req.user._id.toString()) {
|
||||
const error = new Error(errorMessages.AUTH_UNAUTHORIZED);
|
||||
const error = new Error(this.stringService.unauthorized);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
@@ -300,7 +302,7 @@ class AuthController {
|
||||
// If not a match, throw a 403
|
||||
// 403 instead of 401 to avoid triggering axios interceptor
|
||||
if (!match) {
|
||||
const error = new Error(errorMessages.AUTH_INCORRECT_PASSWORD);
|
||||
const error = new Error(this.stringService.authIncorrectPassword);
|
||||
error.status = 403;
|
||||
next(error);
|
||||
return;
|
||||
@@ -311,7 +313,7 @@ class AuthController {
|
||||
|
||||
const updatedUser = await this.db.updateUser(req, res);
|
||||
res.success({
|
||||
msg: successMessages.AUTH_UPDATE_USER,
|
||||
msg: this.stringService.authUpdateUser,
|
||||
data: updatedUser,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -333,7 +335,7 @@ class AuthController {
|
||||
const superAdminExists = await this.db.checkSuperadmin(req, res);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.AUTH_ADMIN_EXISTS,
|
||||
msg: this.stringService.authAdminExists,
|
||||
data: superAdminExists,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -379,7 +381,7 @@ class AuthController {
|
||||
);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.AUTH_CREATE_RECOVERY_TOKEN,
|
||||
msg: this.stringService.authCreateRecoveryToken,
|
||||
data: msgId,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -410,7 +412,7 @@ class AuthController {
|
||||
await this.db.validateRecoveryToken(req, res);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.AUTH_VERIFY_RECOVERY_TOKEN,
|
||||
msg: this.stringService.authVerifyRecoveryToken,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "validateRecoveryTokenController"));
|
||||
@@ -443,7 +445,7 @@ class AuthController {
|
||||
const token = this.issueToken(user._doc, tokenType.ACCESS_TOKEN, appSettings);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.AUTH_RESET_PASSWORD,
|
||||
msg: this.stringService.authResetPassword,
|
||||
data: { user, token },
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -497,7 +499,7 @@ class AuthController {
|
||||
await this.db.deleteUser(user._id);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.AUTH_DELETE_USER,
|
||||
msg: this.stringService.authDeleteUser,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteUserController"));
|
||||
@@ -509,7 +511,7 @@ class AuthController {
|
||||
const allUsers = await this.db.getAllUsers(req, res);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.AUTH_GET_ALL_USERS,
|
||||
msg: this.stringService.authGetAllUsers,
|
||||
data: allUsers,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
deleteChecksByTeamIdParamValidation,
|
||||
updateChecksTTLBodyValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { successMessages } from "../utils/messages.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { getTokenFromHeaders } from "../utils/utils.js";
|
||||
import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
@@ -17,9 +16,10 @@ import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
const SERVICE_NAME = "checkController";
|
||||
|
||||
class CheckController {
|
||||
constructor(db, settingsService) {
|
||||
constructor(db, settingsService, stringService) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
createCheck = async (req, res, next) => {
|
||||
@@ -36,7 +36,7 @@ class CheckController {
|
||||
const check = await this.db.createCheck(checkData);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.CHECK_CREATE,
|
||||
msg: this.stringService.checkCreate,
|
||||
data: check,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -57,7 +57,7 @@ class CheckController {
|
||||
const result = await this.db.getChecksByMonitor(req);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.CHECK_GET,
|
||||
msg: this.stringService.checkGet,
|
||||
data: result,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -77,7 +77,7 @@ class CheckController {
|
||||
const checkData = await this.db.getChecksByTeam(req);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.CHECK_GET,
|
||||
msg: this.stringService.checkGet,
|
||||
data: checkData,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -97,7 +97,7 @@ class CheckController {
|
||||
const deletedCount = await this.db.deleteChecks(req.params.monitorId);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.CHECK_DELETE,
|
||||
msg: this.stringService.checkDelete,
|
||||
data: { deletedCount },
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -117,7 +117,7 @@ class CheckController {
|
||||
const deletedCount = await this.db.deleteChecksByTeamId(req.params.teamId);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.CHECK_DELETE,
|
||||
msg: this.stringService.checkDelete,
|
||||
data: { deletedCount },
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -144,7 +144,7 @@ class CheckController {
|
||||
await this.db.updateChecksTTL(teamId, ttl);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.CHECK_UPDATE_TTL,
|
||||
msg: this.stringService.checkUpdateTTL,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "updateTTL"));
|
||||
|
||||
@@ -7,14 +7,15 @@ import logger from "../utils/logger.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { handleError, handleValidationError } from "./controllerUtils.js";
|
||||
import { getTokenFromHeaders } from "../utils/utils.js";
|
||||
import { successMessages } from "../utils/messages.js";
|
||||
|
||||
const SERVICE_NAME = "inviteController";
|
||||
|
||||
class InviteController {
|
||||
constructor(db, settingsService, emailService) {
|
||||
constructor(db, settingsService, emailService, stringService) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.emailService = emailService;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -66,7 +67,7 @@ class InviteController {
|
||||
});
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.INVITE_ISSUED,
|
||||
msg: this.stringService.inviteIssued,
|
||||
data: inviteToken,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -86,7 +87,7 @@ class InviteController {
|
||||
const invite = await this.db.getInviteToken(req.body.token);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.INVITE_VERIFIED,
|
||||
msg: this.stringService.inviteVerified,
|
||||
data: invite,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -9,14 +9,15 @@ import {
|
||||
} from "../validation/joi.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { getTokenFromHeaders } from "../utils/utils.js";
|
||||
import { successMessages } from "../utils/messages.js";
|
||||
import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
|
||||
const SERVICE_NAME = "maintenanceWindowController";
|
||||
|
||||
class MaintenanceWindowController {
|
||||
constructor(db, settingsService) {
|
||||
constructor(db, settingsService, stringService) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
createMaintenanceWindows = async (req, res, next) => {
|
||||
@@ -45,7 +46,7 @@ class MaintenanceWindowController {
|
||||
await Promise.all(dbTransactions);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.MAINTENANCE_WINDOW_CREATE,
|
||||
msg: this.stringService.maintenanceWindowCreate,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "createMaintenanceWindow"));
|
||||
@@ -63,7 +64,7 @@ class MaintenanceWindowController {
|
||||
const maintenanceWindow = await this.db.getMaintenanceWindowById(req.params.id);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.MAINTENANCE_WINDOW_GET_BY_ID,
|
||||
msg: this.stringService.maintenanceWindowGetById,
|
||||
data: maintenanceWindow,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -89,7 +90,7 @@ class MaintenanceWindowController {
|
||||
);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.MAINTENANCE_WINDOW_GET_BY_TEAM,
|
||||
msg: this.stringService.maintenanceWindowGetByTeam,
|
||||
data: maintenanceWindows,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -111,7 +112,7 @@ class MaintenanceWindowController {
|
||||
);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.MAINTENANCE_WINDOW_GET_BY_USER,
|
||||
msg: this.stringService.maintenanceWindowGetByUser,
|
||||
data: maintenanceWindows,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -129,7 +130,7 @@ class MaintenanceWindowController {
|
||||
try {
|
||||
await this.db.deleteMaintenanceWindowById(req.params.id);
|
||||
return res.success({
|
||||
msg: successMessages.MAINTENANCE_WINDOW_DELETE,
|
||||
msg: this.stringService.maintenanceWindowDelete,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteMaintenanceWindow"));
|
||||
@@ -150,7 +151,7 @@ class MaintenanceWindowController {
|
||||
req.body
|
||||
);
|
||||
return res.success({
|
||||
msg: successMessages.MAINTENANCE_WINDOW_EDIT,
|
||||
msg: this.stringService.maintenanceWindowEdit,
|
||||
data: editedMaintenanceWindow,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -14,7 +14,6 @@ import {
|
||||
getHardwareDetailsByIdQueryValidation,
|
||||
} from "../validation/joi.js";
|
||||
import sslChecker from "ssl-checker";
|
||||
import { successMessages } from "../utils/messages.js";
|
||||
import jwt from "jsonwebtoken";
|
||||
import { getTokenFromHeaders } from "../utils/utils.js";
|
||||
import logger from "../utils/logger.js";
|
||||
@@ -24,10 +23,11 @@ import seedDb from "../db/mongo/utils/seedDb.js";
|
||||
const SERVICE_NAME = "monitorController";
|
||||
|
||||
class MonitorController {
|
||||
constructor(db, settingsService, jobQueue) {
|
||||
constructor(db, settingsService, jobQueue, stringService) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.jobQueue = jobQueue;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,7 +43,7 @@ class MonitorController {
|
||||
try {
|
||||
const monitors = await this.db.getAllMonitors();
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_GET_ALL,
|
||||
msg: this.stringService.monitorGetAll,
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -64,7 +64,7 @@ class MonitorController {
|
||||
try {
|
||||
const monitors = await this.db.getAllMonitorsWithUptimeStats();
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_GET_ALL,
|
||||
msg: this.stringService.monitorGetAll,
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -76,7 +76,7 @@ class MonitorController {
|
||||
try {
|
||||
const monitor = await this.db.getUptimeDetailsById(req);
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_GET_BY_ID,
|
||||
msg: this.stringService.monitorGetById,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -105,7 +105,7 @@ class MonitorController {
|
||||
try {
|
||||
const monitorStats = await this.db.getMonitorStatsById(req);
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_STATS_BY_ID,
|
||||
msg: this.stringService.monitorStatsById,
|
||||
data: monitorStats,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -133,7 +133,7 @@ class MonitorController {
|
||||
try {
|
||||
const monitor = await this.db.getHardwareDetailsById(req);
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_GET_BY_ID,
|
||||
msg: this.stringService.monitorGetById,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -154,7 +154,7 @@ class MonitorController {
|
||||
const certificate = await fetchMonitorCertificate(sslChecker, monitor);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_CERTIFICATE,
|
||||
msg: this.stringService.monitorCertificate,
|
||||
data: {
|
||||
certificateDate: new Date(certificate.validTo),
|
||||
},
|
||||
@@ -187,7 +187,7 @@ class MonitorController {
|
||||
try {
|
||||
const monitor = await this.db.getMonitorById(req.params.monitorId);
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_GET_BY_ID,
|
||||
msg: this.stringService.monitorGetById,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -231,7 +231,7 @@ class MonitorController {
|
||||
// Add monitor to job queue
|
||||
this.jobQueue.addJob(monitor._id, monitor);
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_CREATE,
|
||||
msg: this.stringService.monitorCreate,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -295,12 +295,49 @@ class MonitorController {
|
||||
try {
|
||||
const monitor = await this.db.deleteMonitor(req, res, next);
|
||||
// Delete associated checks,alerts,and notifications
|
||||
|
||||
try {
|
||||
await this.jobQueue.deleteJob(monitor);
|
||||
await this.db.deleteChecks(monitor._id);
|
||||
await this.db.deletePageSpeedChecksByMonitorId(monitor._id);
|
||||
await this.db.deleteNotificationsByMonitorId(monitor._id);
|
||||
await this.db.deleteHardwareChecksByMonitorId(monitor._id);
|
||||
const operations = [
|
||||
{ name: "deleteJob", fn: () => this.jobQueue.deleteJob(monitor) },
|
||||
{ name: "deleteChecks", fn: () => this.db.deleteChecks(monitor._id) },
|
||||
{
|
||||
name: "deletePageSpeedChecks",
|
||||
fn: () => this.db.deletePageSpeedChecksByMonitorId(monitor._id),
|
||||
},
|
||||
{
|
||||
name: "deleteNotifications",
|
||||
fn: () => this.db.deleteNotificationsByMonitorId(monitor._id),
|
||||
},
|
||||
{
|
||||
name: "deleteHardwareChecks",
|
||||
fn: () => this.db.deleteHardwareChecksByMonitorId(monitor._id),
|
||||
},
|
||||
{
|
||||
name: "deleteDistributedUptimeChecks",
|
||||
fn: () => this.db.deleteDistributedChecksByMonitorId(monitor._id),
|
||||
},
|
||||
|
||||
// TODO We don't actually want to delete the status page if there are other monitors in it
|
||||
// We actually just want to remove the monitor being deleted from the status page.
|
||||
// Only delete he status page if there are no other monitors in it.
|
||||
{
|
||||
name: "deleteStatusPages",
|
||||
fn: () => this.db.deleteStatusPagesByMonitorId(monitor._id),
|
||||
},
|
||||
];
|
||||
const results = await Promise.allSettled(operations.map((op) => op.fn()));
|
||||
|
||||
results.forEach((result, index) => {
|
||||
if (result.status === "rejected") {
|
||||
const operationName = operations[index].name;
|
||||
logger.error({
|
||||
message: `Failed to ${operationName} for monitor ${monitor._id}`,
|
||||
service: SERVICE_NAME,
|
||||
method: "deleteMonitor",
|
||||
stack: result.reason.stack,
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error({
|
||||
message: `Error deleting associated records for monitor ${monitor._id} with name ${monitor.name}`,
|
||||
@@ -309,7 +346,7 @@ class MonitorController {
|
||||
stack: error.stack,
|
||||
});
|
||||
}
|
||||
return res.success({ msg: successMessages.MONITOR_DELETE });
|
||||
return res.success({ msg: this.stringService.monitorDelete });
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteMonitor"));
|
||||
}
|
||||
@@ -401,7 +438,7 @@ class MonitorController {
|
||||
// Add the new job back to the queue
|
||||
await this.jobQueue.addJob(editedMonitor._id, editedMonitor);
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_EDIT,
|
||||
msg: this.stringService.monitorEdit,
|
||||
data: editedMonitor,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -438,8 +475,8 @@ class MonitorController {
|
||||
monitor.save();
|
||||
return res.success({
|
||||
msg: monitor.isActive
|
||||
? successMessages.MONITOR_RESUME
|
||||
: successMessages.MONITOR_PAUSE,
|
||||
? this.stringService.monitorResume
|
||||
: this.stringService.monitorPause,
|
||||
data: monitor,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -469,7 +506,7 @@ class MonitorController {
|
||||
);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_DEMO_ADDED,
|
||||
msg: this.stringService.monitorDemoAdded,
|
||||
data: demoMonitors.length,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -488,7 +525,7 @@ class MonitorController {
|
||||
try {
|
||||
const monitors = await this.db.getMonitorsByTeamId(req);
|
||||
return res.success({
|
||||
msg: successMessages.MONITOR_GET_BY_TEAM_ID,
|
||||
msg: this.stringService.monitorGetByTeamId,
|
||||
data: monitors,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { handleError } from "./controllerUtils.js";
|
||||
import { successMessages } from "../utils/messages.js";
|
||||
|
||||
const SERVICE_NAME = "JobQueueController";
|
||||
|
||||
class JobQueueController {
|
||||
constructor(jobQueue) {
|
||||
constructor(jobQueue, stringService) {
|
||||
this.jobQueue = jobQueue;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
getMetrics = async (req, res, next) => {
|
||||
try {
|
||||
const metrics = await this.jobQueue.getMetrics();
|
||||
res.success({
|
||||
msg: successMessages.QUEUE_GET_METRICS,
|
||||
msg: this.stringService.queueGetMetrics,
|
||||
data: metrics,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -25,7 +25,7 @@ class JobQueueController {
|
||||
try {
|
||||
const jobs = await this.jobQueue.getJobStats();
|
||||
return res.success({
|
||||
msg: successMessages.QUEUE_GET_METRICS,
|
||||
msg: this.stringService.queueGetMetrics,
|
||||
data: jobs,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -38,7 +38,7 @@ class JobQueueController {
|
||||
try {
|
||||
await this.jobQueue.addJob(Math.random().toString(36).substring(7));
|
||||
return res.success({
|
||||
msg: successMessages.QUEUE_ADD_JOB,
|
||||
msg: this.stringService.queueAddJob,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "addJob"));
|
||||
@@ -50,7 +50,7 @@ class JobQueueController {
|
||||
try {
|
||||
await this.jobQueue.obliterate();
|
||||
return res.success({
|
||||
msg: successMessages.QUEUE_OBLITERATE,
|
||||
msg: this.stringService.queueObliterate,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "obliterateQueue"));
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { successMessages } from "../utils/messages.js";
|
||||
import { updateAppSettingsBodyValidation } from "../validation/joi.js";
|
||||
import { handleValidationError, handleError } from "./controllerUtils.js";
|
||||
|
||||
const SERVICE_NAME = "SettingsController";
|
||||
|
||||
class SettingsController {
|
||||
constructor(db, settingsService) {
|
||||
constructor(db, settingsService, stringService) {
|
||||
this.db = db;
|
||||
this.settingsService = settingsService;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
getAppSettings = async (req, res, next) => {
|
||||
@@ -14,7 +15,7 @@ class SettingsController {
|
||||
const settings = { ...(await this.settingsService.getSettings()) };
|
||||
delete settings.jwtSecret;
|
||||
return res.success({
|
||||
msg: successMessages.GET_APP_SETTINGS,
|
||||
msg: this.stringService.getAppSettings,
|
||||
data: settings,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -35,7 +36,7 @@ class SettingsController {
|
||||
const updatedSettings = { ...(await this.settingsService.reloadSettings()) };
|
||||
delete updatedSettings.jwtSecret;
|
||||
return res.success({
|
||||
msg: successMessages.UPDATE_APP_SETTINGS,
|
||||
msg: this.stringService.updateAppSettings,
|
||||
data: updatedSettings,
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -5,13 +5,13 @@ import {
|
||||
getStatusPageQueryValidation,
|
||||
imageValidation,
|
||||
} from "../validation/joi.js";
|
||||
import { successMessages, errorMessages } from "../utils/messages.js";
|
||||
|
||||
const SERVICE_NAME = "statusPageController";
|
||||
|
||||
class StatusPageController {
|
||||
constructor(db) {
|
||||
constructor(db, stringService) {
|
||||
this.db = db;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
createStatusPage = async (req, res, next) => {
|
||||
@@ -26,7 +26,7 @@ class StatusPageController {
|
||||
try {
|
||||
const statusPage = await this.db.createStatusPage(req.body, req.file);
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_CREATE,
|
||||
msg: this.stringService.statusPageCreate,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -46,12 +46,12 @@ class StatusPageController {
|
||||
try {
|
||||
const statusPage = await this.db.updateStatusPage(req.body, req.file);
|
||||
if (statusPage === null) {
|
||||
const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND);
|
||||
const error = new Error(this.stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_UPDATE,
|
||||
msg: this.stringService.statusPageUpdate,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -63,7 +63,7 @@ class StatusPageController {
|
||||
try {
|
||||
const statusPage = await this.db.getStatusPage();
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE,
|
||||
msg: this.stringService.statusPageByUrl,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -83,7 +83,7 @@ class StatusPageController {
|
||||
try {
|
||||
const statusPage = await this.db.getStatusPageByUrl(req.params.url, req.query.type);
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_BY_URL,
|
||||
msg: this.stringService.statusPageByUrl,
|
||||
data: statusPage,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -95,8 +95,9 @@ class StatusPageController {
|
||||
try {
|
||||
const teamId = req.params.teamId;
|
||||
const statusPages = await this.db.getStatusPagesByTeamId(teamId);
|
||||
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_BY_TEAM_ID,
|
||||
msg: this.stringService.statusPageByTeamId,
|
||||
data: statusPages,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -108,7 +109,7 @@ class StatusPageController {
|
||||
try {
|
||||
await this.db.deleteStatusPage(req.params.url);
|
||||
return res.success({
|
||||
msg: successMessages.STATUS_PAGE_DELETE,
|
||||
msg: this.stringService.statusPageDelete,
|
||||
});
|
||||
} catch (error) {
|
||||
next(handleError(error, SERVICE_NAME, "deleteStatusPage"));
|
||||
|
||||
@@ -38,6 +38,20 @@ const MonitorSchema = mongoose.Schema(
|
||||
"distributed_http",
|
||||
],
|
||||
},
|
||||
jsonPath: {
|
||||
type: String,
|
||||
},
|
||||
expectedValue: {
|
||||
type: String,
|
||||
},
|
||||
matchMethod: {
|
||||
type: String,
|
||||
enum: [
|
||||
"equal",
|
||||
"include",
|
||||
"regex",
|
||||
],
|
||||
},
|
||||
url: {
|
||||
type: String,
|
||||
required: true,
|
||||
|
||||
@@ -12,4 +12,15 @@ const createDistributedCheck = async (checkData) => {
|
||||
}
|
||||
};
|
||||
|
||||
export { createDistributedCheck };
|
||||
const deleteDistributedChecksByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
const result = await DistributedUptimeCheck.deleteMany({ monitorId });
|
||||
return result.deletedCount;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteDistributedChecksByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export { createDistributedCheck, deleteDistributedChecksByMonitorId };
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import InviteToken from "../../models/InviteToken.js";
|
||||
import crypto from "crypto";
|
||||
import { errorMessages } from "../../../utils/messages.js";
|
||||
import ServiceRegistry from "../../../service/serviceRegistry.js";
|
||||
import StringService from "../../../service/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "inviteModule";
|
||||
/**
|
||||
@@ -42,12 +43,13 @@ const requestInviteToken = async (userData) => {
|
||||
* @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(errorMessages.AUTH_INVITE_NOT_FOUND);
|
||||
throw new Error(stringService.authInviteNotFound);
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
@@ -68,12 +70,13 @@ const getInviteToken = async (token) => {
|
||||
* @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(errorMessages.AUTH_INVITE_NOT_FOUND);
|
||||
throw new Error(stringService.authInviteNotFound);
|
||||
}
|
||||
return invite;
|
||||
} catch (error) {
|
||||
|
||||
@@ -3,9 +3,10 @@ import Check from "../../models/Check.js";
|
||||
import PageSpeedCheck from "../../models/PageSpeedCheck.js";
|
||||
import HardwareCheck from "../../models/HardwareCheck.js";
|
||||
import DistributedUptimeCheck from "../../models/DistributedUptimeCheck.js";
|
||||
import { errorMessages } from "../../../utils/messages.js";
|
||||
import Notification from "../../models/Notification.js";
|
||||
import { NormalizeData, NormalizeDataUptimeDetails } from "../../../utils/dataUtils.js";
|
||||
import ServiceRegistry from "../../../service/serviceRegistry.js";
|
||||
import StringService from "../../../service/stringService.js";
|
||||
import fs from "fs";
|
||||
import path from "path";
|
||||
import { fileURLToPath } from "url";
|
||||
@@ -326,11 +327,12 @@ const calculateGroupStats = (group) => {
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getUptimeDetailsById = async (req) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const { monitorId } = req.params;
|
||||
const monitor = await Monitor.findById(monitorId);
|
||||
if (monitor === null || monitor === undefined) {
|
||||
throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
|
||||
throw new Error(stringService.dbFindMonitorById(monitorId));
|
||||
}
|
||||
|
||||
const { dateRange, normalize } = req.query;
|
||||
@@ -370,10 +372,13 @@ const getUptimeDetailsById = async (req) => {
|
||||
|
||||
const getDistributedUptimeDetailsById = async (req) => {
|
||||
try {
|
||||
const { monitorId } = req.params;
|
||||
const { monitorId } = req?.params ?? {};
|
||||
if (typeof monitorId === "undefined") {
|
||||
throw new Error();
|
||||
}
|
||||
const monitor = await Monitor.findById(monitorId);
|
||||
if (monitor === null || monitor === undefined) {
|
||||
throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
|
||||
throw new Error(this.stringService.dbFindMonitorById(monitorId));
|
||||
}
|
||||
|
||||
const { dateRange, normalize } = req.query;
|
||||
@@ -419,13 +424,14 @@ const getDistributedUptimeDetailsById = async (req) => {
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getMonitorStatsById = async (req) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const { monitorId } = req.params;
|
||||
|
||||
// Get monitor, if we can't find it, abort with error
|
||||
const monitor = await Monitor.findById(monitorId);
|
||||
if (monitor === null || monitor === undefined) {
|
||||
throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
|
||||
throw new Error(stringService.getDbFindMonitorById(monitorId));
|
||||
}
|
||||
|
||||
// Get query params
|
||||
@@ -516,10 +522,11 @@ const getHardwareDetailsById = async (req) => {
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getMonitorById = async (monitorId) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const monitor = await Monitor.findById(monitorId);
|
||||
if (monitor === null || monitor === undefined) {
|
||||
const error = new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
|
||||
const error = new Error(stringService.getDbFindMonitorById(monitorId));
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
@@ -783,11 +790,13 @@ const createMonitor = async (req, res) => {
|
||||
* @throws {Error}
|
||||
*/
|
||||
const deleteMonitor = async (req, res) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
const monitorId = req.params.monitorId;
|
||||
try {
|
||||
const monitor = await Monitor.findByIdAndDelete(monitorId);
|
||||
if (!monitor) {
|
||||
throw new Error(errorMessages.DB_FIND_MONITOR_BY_ID(monitorId));
|
||||
throw new Error(stringService.getDbFindMonitorById(monitorId));
|
||||
}
|
||||
return monitor;
|
||||
} catch (error) {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import UserModel from "../../models/User.js";
|
||||
import RecoveryToken from "../../models/RecoveryToken.js";
|
||||
import crypto from "crypto";
|
||||
import { errorMessages } from "../../../utils/messages.js";
|
||||
import serviceRegistry from "../../../service/serviceRegistry.js";
|
||||
import StringService from "../../../service/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "recoveryModule";
|
||||
|
||||
@@ -31,6 +32,7 @@ const requestRecoveryToken = async (req, res) => {
|
||||
};
|
||||
|
||||
const validateRecoveryToken = async (req, res) => {
|
||||
const stringService = serviceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const candidateToken = req.body.recoveryToken;
|
||||
const recoveryToken = await RecoveryToken.findOne({
|
||||
@@ -39,7 +41,7 @@ const validateRecoveryToken = async (req, res) => {
|
||||
if (recoveryToken !== null) {
|
||||
return recoveryToken;
|
||||
} else {
|
||||
throw new Error(errorMessages.DB_TOKEN_NOT_FOUND);
|
||||
throw new Error(stringService.dbTokenNotFound);
|
||||
}
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
@@ -49,6 +51,7 @@ const validateRecoveryToken = async (req, res) => {
|
||||
};
|
||||
|
||||
const resetPassword = async (req, res) => {
|
||||
const stringService = serviceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
const newPassword = req.body.password;
|
||||
|
||||
@@ -57,12 +60,12 @@ const resetPassword = async (req, res) => {
|
||||
const user = await UserModel.findOne({ email: recoveryToken.email });
|
||||
|
||||
if (user === null) {
|
||||
throw new Error(errorMessages.DB_USER_NOT_FOUND);
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
}
|
||||
|
||||
const match = await user.comparePassword(newPassword);
|
||||
if (match === true) {
|
||||
throw new Error(errorMessages.DB_RESET_PASSWORD_BAD_MATCH);
|
||||
throw new Error(stringService.dbResetPasswordBadMatch);
|
||||
}
|
||||
|
||||
user.password = newPassword;
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import StatusPage from "../../models/StatusPage.js";
|
||||
import { errorMessages } from "../../../utils/messages.js";
|
||||
import { NormalizeData } from "../../../utils/dataUtils.js";
|
||||
import ServiceRegistry from "../../../service/serviceRegistry.js";
|
||||
import StringService from "../../../service/stringService.js";
|
||||
|
||||
const SERVICE_NAME = "statusPageModule";
|
||||
|
||||
const createStatusPage = async (statusPageData, image) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const statusPage = new StatusPage({ ...statusPageData });
|
||||
if (image) {
|
||||
@@ -19,7 +22,7 @@ const createStatusPage = async (statusPageData, image) => {
|
||||
if (error?.code === 11000) {
|
||||
// Handle duplicate URL errors
|
||||
error.status = 400;
|
||||
error.message = errorMessages.STATUS_PAGE_URL_NOT_UNIQUE;
|
||||
error.message = stringService.statusPageUrlNotUnique;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "createStatusPage";
|
||||
@@ -67,13 +70,10 @@ const getStatusPageByUrl = async (url, type) => {
|
||||
};
|
||||
|
||||
const getStatusPagesByTeamId = async (teamId) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const statusPages = await StatusPage.find({ teamId });
|
||||
if (statusPages.length === 0) {
|
||||
const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
return statusPages;
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
@@ -83,6 +83,8 @@ const getStatusPagesByTeamId = async (teamId) => {
|
||||
};
|
||||
|
||||
const getStatusPage = async (url) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const statusPageQuery = await StatusPage.aggregate([
|
||||
{ $match: { url: url } },
|
||||
@@ -156,7 +158,7 @@ const getStatusPage = async (url) => {
|
||||
},
|
||||
]);
|
||||
if (!statusPageQuery.length) {
|
||||
const error = new Error(errorMessages.STATUS_PAGE_NOT_FOUND);
|
||||
const error = new Error(stringService.statusPageNotFound);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
@@ -188,6 +190,16 @@ const deleteStatusPage = async (url) => {
|
||||
}
|
||||
};
|
||||
|
||||
const deleteStatusPagesByMonitorId = async (monitorId) => {
|
||||
try {
|
||||
await StatusPage.deleteMany({ monitors: { $in: [monitorId] } });
|
||||
} catch (error) {
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "deleteStatusPageByMonitorId";
|
||||
throw error;
|
||||
}
|
||||
};
|
||||
|
||||
export {
|
||||
createStatusPage,
|
||||
updateStatusPage,
|
||||
@@ -195,4 +207,5 @@ export {
|
||||
getStatusPage,
|
||||
getStatusPageByUrl,
|
||||
deleteStatusPage,
|
||||
deleteStatusPagesByMonitorId,
|
||||
};
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
import UserModel from "../../models/User.js";
|
||||
import TeamModel from "../../models/Team.js";
|
||||
import { errorMessages } from "../../../utils/messages.js";
|
||||
import { GenerateAvatarImage } from "../../../utils/imageProcessing.js";
|
||||
|
||||
const DUPLICATE_KEY_CODE = 11000; // MongoDB error code for duplicate key
|
||||
import { ParseBoolean } from "../../../utils/utils.js";
|
||||
import ServiceRegistry from "../../../service/serviceRegistry.js";
|
||||
import StringService from "../../../service/stringService.js";
|
||||
const SERVICE_NAME = "userModule";
|
||||
|
||||
/**
|
||||
@@ -20,6 +21,7 @@ const insertUser = async (
|
||||
imageFile,
|
||||
generateAvatarImage = GenerateAvatarImage
|
||||
) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
try {
|
||||
if (imageFile) {
|
||||
// 1. Save the full size image
|
||||
@@ -50,7 +52,7 @@ const insertUser = async (
|
||||
.select("-profileImage"); // .select() doesn't work with create, need to save then find
|
||||
} catch (error) {
|
||||
if (error.code === DUPLICATE_KEY_CODE) {
|
||||
error.message = errorMessages.DB_USER_EXISTS;
|
||||
error.message = stringService.dbUserExists;
|
||||
}
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "insertUser";
|
||||
@@ -70,12 +72,14 @@ const insertUser = async (
|
||||
* @throws {Error}
|
||||
*/
|
||||
const getUserByEmail = async (email) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
// Need the password to be able to compare, removed .select()
|
||||
// We can strip the hash before returning the user
|
||||
const user = await UserModel.findOne({ email: email }).select("-profileImage");
|
||||
if (!user) {
|
||||
throw new Error(errorMessages.DB_USER_NOT_FOUND);
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
}
|
||||
return user;
|
||||
} catch (error) {
|
||||
@@ -150,10 +154,12 @@ const updateUser = async (
|
||||
* @throws {Error}
|
||||
*/
|
||||
const deleteUser = async (userId) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
|
||||
try {
|
||||
const deletedUser = await UserModel.findByIdAndDelete(userId);
|
||||
if (!deletedUser) {
|
||||
throw new Error(errorMessages.DB_USER_NOT_FOUND);
|
||||
throw new Error(stringService.dbUserNotFound);
|
||||
}
|
||||
return deletedUser;
|
||||
} catch (error) {
|
||||
|
||||
@@ -74,6 +74,10 @@ import MongoDB from "./db/mongo/MongoDB.js";
|
||||
|
||||
import IORedis from "ioredis";
|
||||
|
||||
import TranslationService from './service/translationService.js';
|
||||
import languageMiddleware from './middleware/languageMiddleware.js';
|
||||
import StringService from './service/stringService.js';
|
||||
|
||||
const SERVICE_NAME = "Server";
|
||||
const SHUTDOWN_TIMEOUT = 1000;
|
||||
let isShuttingDown = false;
|
||||
@@ -156,11 +160,17 @@ const startApp = async () => {
|
||||
}
|
||||
}
|
||||
|
||||
// Create and Register Primary services
|
||||
const translationService = new TranslationService(logger);
|
||||
const stringService = new StringService(translationService);
|
||||
ServiceRegistry.register(StringService.SERVICE_NAME, stringService);
|
||||
|
||||
// Create DB
|
||||
const db = new MongoDB();
|
||||
await db.connect();
|
||||
|
||||
// Create services
|
||||
const networkService = new NetworkService(axios, ping, logger, http, Docker, net, stringService);
|
||||
const settingsService = new SettingsService(AppSettings);
|
||||
await settingsService.loadSettings();
|
||||
const emailService = new EmailService(
|
||||
@@ -172,16 +182,17 @@ const startApp = async () => {
|
||||
nodemailer,
|
||||
logger
|
||||
);
|
||||
const networkService = new NetworkService(axios, ping, logger, http, Docker, net);
|
||||
const statusService = new StatusService(db, logger);
|
||||
const notificationService = new NotificationService(emailService, db, logger);
|
||||
|
||||
|
||||
const jobQueue = new JobQueue(
|
||||
db,
|
||||
statusService,
|
||||
networkService,
|
||||
notificationService,
|
||||
settingsService,
|
||||
stringService,
|
||||
logger,
|
||||
Queue,
|
||||
Worker
|
||||
@@ -195,6 +206,10 @@ const startApp = async () => {
|
||||
ServiceRegistry.register(NetworkService.SERVICE_NAME, networkService);
|
||||
ServiceRegistry.register(StatusService.SERVICE_NAME, statusService);
|
||||
ServiceRegistry.register(NotificationService.SERVICE_NAME, notificationService);
|
||||
ServiceRegistry.register(TranslationService.SERVICE_NAME, translationService);
|
||||
|
||||
await translationService.initialize();
|
||||
|
||||
server = app.listen(PORT, () => {
|
||||
logger.info({ message: `server started on port:${PORT}` });
|
||||
});
|
||||
@@ -208,40 +223,50 @@ const startApp = async () => {
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
||||
ServiceRegistry.get(EmailService.SERVICE_NAME),
|
||||
ServiceRegistry.get(JobQueue.SERVICE_NAME)
|
||||
ServiceRegistry.get(JobQueue.SERVICE_NAME),
|
||||
ServiceRegistry.get(StringService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const monitorController = new MonitorController(
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
||||
ServiceRegistry.get(JobQueue.SERVICE_NAME)
|
||||
ServiceRegistry.get(JobQueue.SERVICE_NAME),
|
||||
ServiceRegistry.get(StringService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const settingsController = new SettingsController(
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME)
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
||||
ServiceRegistry.get(StringService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const checkController = new CheckController(
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME)
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
||||
ServiceRegistry.get(StringService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const inviteController = new InviteController(
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
||||
ServiceRegistry.get(EmailService.SERVICE_NAME)
|
||||
ServiceRegistry.get(EmailService.SERVICE_NAME),
|
||||
ServiceRegistry.get(StringService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const maintenanceWindowController = new MaintenanceWindowController(
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME)
|
||||
ServiceRegistry.get(SettingsService.SERVICE_NAME),
|
||||
ServiceRegistry.get(StringService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const queueController = new QueueController(ServiceRegistry.get(JobQueue.SERVICE_NAME));
|
||||
const queueController = new QueueController(
|
||||
ServiceRegistry.get(JobQueue.SERVICE_NAME),
|
||||
ServiceRegistry.get(StringService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const statusPageController = new StatusPageController(
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME)
|
||||
ServiceRegistry.get(MongoDB.SERVICE_NAME),
|
||||
ServiceRegistry.get(StringService.SERVICE_NAME)
|
||||
);
|
||||
|
||||
const distributedUptimeController = new DistributedUptimeController(
|
||||
@@ -267,12 +292,10 @@ const startApp = async () => {
|
||||
// Init job queue
|
||||
await jobQueue.initJobQueue();
|
||||
// Middleware
|
||||
app.use(
|
||||
cors()
|
||||
//We will add configuration later
|
||||
);
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(helmet());
|
||||
app.use(languageMiddleware(stringService, translationService));
|
||||
// Swagger UI
|
||||
app.use("/api-docs", swaggerUi.serve, swaggerUi.setup(openApiSpec));
|
||||
|
||||
|
||||
154
Server/locales/en.json
Normal file
154
Server/locales/en.json
Normal file
@@ -0,0 +1,154 @@
|
||||
{
|
||||
"dontHaveAccount": "Don't have account",
|
||||
"email": "E-mail",
|
||||
"forgotPassword": "Forgot Password",
|
||||
"password": "password",
|
||||
"signUp": "Sign up",
|
||||
"submit": "Submit",
|
||||
"title": "Title",
|
||||
"continue": "Continue",
|
||||
"enterEmail": "Enter your email",
|
||||
"authLoginTitle": "Log In",
|
||||
"authLoginEnterPassword": "Enter your password",
|
||||
"commonPassword": "Password",
|
||||
"commonBack": "Back",
|
||||
"authForgotPasswordTitle": "Forgot password?",
|
||||
"authForgotPasswordResetPassword": "Reset password",
|
||||
"createPassword": "Create your password",
|
||||
"createAPassword": "Create a password",
|
||||
"authRegisterAlreadyHaveAccount": "Already have an account?",
|
||||
"commonAppName": "BlueWave Uptime",
|
||||
"authLoginEnterEmail": "Enter your email",
|
||||
"authRegisterTitle": "Create an account",
|
||||
"authRegisterStepOneTitle": "Create your account",
|
||||
"authRegisterStepOneDescription": "Enter your details to get started",
|
||||
"authRegisterStepTwoTitle": "Set up your profile",
|
||||
"authRegisterStepTwoDescription": "Tell us more about yourself",
|
||||
"authRegisterStepThreeTitle": "Almost done!",
|
||||
"authRegisterStepThreeDescription": "Review your information",
|
||||
"authForgotPasswordDescription": "No worries, we'll send you reset instructions.",
|
||||
"authForgotPasswordSendInstructions": "Send instructions",
|
||||
"authForgotPasswordBackTo": "Back to",
|
||||
"authCheckEmailTitle": "Check your email",
|
||||
"authCheckEmailDescription": "We sent a password reset link to {{email}}",
|
||||
"authCheckEmailResendEmail": "Resend email",
|
||||
"authCheckEmailBackTo": "Back to",
|
||||
"goBackTo": "Go back to",
|
||||
"authCheckEmailDidntReceiveEmail": "Didn't receive the email?",
|
||||
"authCheckEmailClickToResend": "Click to resend",
|
||||
"authSetNewPasswordTitle": "Set new password",
|
||||
"authSetNewPasswordDescription": "Your new password must be different from previously used passwords.",
|
||||
"authSetNewPasswordNewPassword": "New password",
|
||||
"authSetNewPasswordConfirmPassword": "Confirm password",
|
||||
"confirmPassword": "Confirm your password",
|
||||
"authSetNewPasswordResetPassword": "Reset password",
|
||||
"authSetNewPasswordBackTo": "Back to",
|
||||
"authPasswordMustBeAtLeast": "Must be at least",
|
||||
"authPasswordCharactersLong": "8 characters long",
|
||||
"authPasswordMustContainAtLeast": "Must contain at least",
|
||||
"authPasswordSpecialCharacter": "one special character",
|
||||
"authPasswordOneNumber": "one number",
|
||||
"authPasswordUpperCharacter": "one upper character",
|
||||
"authPasswordLowerCharacter": "one lower character",
|
||||
"authPasswordConfirmAndPassword": "Confirm password and password",
|
||||
"authPasswordMustMatch": "must match",
|
||||
"friendlyError": "Something went wrong...",
|
||||
"unknownError": "An unknown error occurred",
|
||||
"unauthorized": "Unauthorized access",
|
||||
"authAdminExists": "Admin already exists",
|
||||
"authInviteNotFound": "Invite not found",
|
||||
"unknownService": "Unknown service",
|
||||
"noAuthToken": "No auth token provided",
|
||||
"invalidAuthToken": "Invalid auth token",
|
||||
"expiredAuthToken": "Token expired",
|
||||
"noRefreshToken": "No refresh token provided",
|
||||
"invalidRefreshToken": "Invalid refresh token",
|
||||
"expiredRefreshToken": "Refresh token expired",
|
||||
"requestNewAccessToken": "Request new access token",
|
||||
"invalidPayload": "Invalid payload",
|
||||
"verifyOwnerNotFound": "Document not found",
|
||||
"verifyOwnerUnauthorized": "Unauthorized access",
|
||||
"insufficientPermissions": "Insufficient permissions",
|
||||
"dbUserExists": "User already exists",
|
||||
"dbUserNotFound": "User not found",
|
||||
"dbTokenNotFound": "Token not found",
|
||||
"dbResetPasswordBadMatch": "New password must be different from old password",
|
||||
"dbFindMonitorById": "Monitor with id ${monitorId} not found",
|
||||
"dbDeleteChecks": "No checks found for monitor with id ${monitorId}",
|
||||
"authIncorrectPassword": "Incorrect password",
|
||||
"authUnauthorized": "Unauthorized access",
|
||||
"monitorGetById": "Monitor not found",
|
||||
"monitorGetByUserId": "No monitors found for user",
|
||||
"jobQueueWorkerClose": "Error closing worker",
|
||||
"jobQueueDeleteJob": "Job not found in queue",
|
||||
"jobQueueObliterate": "Error obliterating queue",
|
||||
"pingCannotResolve": "No response",
|
||||
"statusPageNotFound": "Status page not found",
|
||||
"statusPageUrlNotUnique": "Status page url must be unique",
|
||||
"dockerFail": "Failed to fetch Docker container information",
|
||||
"dockerNotFound": "Docker container not found",
|
||||
"portFail": "Failed to connect to port",
|
||||
"alertCreate": "Alert created successfully",
|
||||
"alertGetByUser": "Got alerts successfully",
|
||||
"alertGetByMonitor": "Got alerts by Monitor successfully",
|
||||
"alertGetById": "Got alert by Id successfully",
|
||||
"alertEdit": "Alert edited successfully",
|
||||
"alertDelete": "Alert deleted successfully",
|
||||
"authCreateUser": "User created successfully",
|
||||
"authLoginUser": "User logged in successfully",
|
||||
"authLogoutUser": "User logged out successfully",
|
||||
"authUpdateUser": "User updated successfully",
|
||||
"authCreateRecoveryToken": "Recovery token created successfully",
|
||||
"authVerifyRecoveryToken": "Recovery token verified successfully",
|
||||
"authResetPassword": "Password reset successfully",
|
||||
"authAdminCheck": "Admin check completed successfully",
|
||||
"authDeleteUser": "User deleted successfully",
|
||||
"authTokenRefreshed": "Auth token is refreshed",
|
||||
"authGetAllUsers": "Got all users successfully",
|
||||
"inviteIssued": "Invite sent successfully",
|
||||
"inviteVerified": "Invite verified successfully",
|
||||
"checkCreate": "Check created successfully",
|
||||
"checkGet": "Got checks successfully",
|
||||
"checkDelete": "Checks deleted successfully",
|
||||
"checkUpdateTtl": "Checks TTL updated successfully",
|
||||
"monitorGetAll": "Got all monitors successfully",
|
||||
"monitorStatsById": "Got monitor stats by Id successfully",
|
||||
"monitorGetByIdSuccess": "Got monitor by Id successfully",
|
||||
"monitorGetByTeamId": "Got monitors by Team Id successfully",
|
||||
"monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully",
|
||||
"monitorCreate": "Monitor created successfully",
|
||||
"monitorDelete": "Monitor deleted successfully",
|
||||
"monitorEdit": "Monitor edited successfully",
|
||||
"monitorCertificate": "Got monitor certificate successfully",
|
||||
"monitorDemoAdded": "Successfully added demo monitors",
|
||||
"queueGetMetrics": "Got metrics successfully",
|
||||
"queueAddJob": "Job added successfully",
|
||||
"queueObliterate": "Queue obliterated",
|
||||
"jobQueueDeleteJobSuccess": "Job removed successfully",
|
||||
"jobQueuePauseJob": "Job paused successfully",
|
||||
"jobQueueResumeJob": "Job resumed successfully",
|
||||
"maintenanceWindowGetById": "Got Maintenance Window by Id successfully",
|
||||
"maintenanceWindowCreate": "Maintenance Window created successfully",
|
||||
"maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully",
|
||||
"maintenanceWindowDelete": "Maintenance Window deleted successfully",
|
||||
"maintenanceWindowEdit": "Maintenance Window edited successfully",
|
||||
"pingSuccess": "Success",
|
||||
"getAppSettings": "Got app settings successfully",
|
||||
"updateAppSettings": "Updated app settings successfully",
|
||||
"statusPageByUrl": "Got status page by url successfully",
|
||||
"statusPageCreate": "Status page created successfully",
|
||||
"newTermsAdded": "New terms added to POEditor",
|
||||
"dockerSuccess": "Docker container status fetched successfully",
|
||||
"portSuccess": "Port connected successfully",
|
||||
"monitorPause": "Monitor paused successfully",
|
||||
"monitorResume": "Monitor resumed successfully",
|
||||
"statusPageDelete": "Status page deleted successfully",
|
||||
"statusPageUpdate": "Status page updated successfully",
|
||||
"statusPageByTeamId": "Got status pages by team id successfully",
|
||||
"httpNetworkError": "Network error",
|
||||
"httpNotJson": "Response data is not json",
|
||||
"httpJsonPathError": "Failed to parse json data",
|
||||
"httpEmptyResult": "Result is empty",
|
||||
"httpMatchSuccess": "Response data match successfully",
|
||||
"httpMatchFail": "Failed to match response data"
|
||||
}
|
||||
147
Server/locales/en.json.bak
Normal file
147
Server/locales/en.json.bak
Normal file
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"dontHaveAccount": "Don't have account",
|
||||
"email": "E-mail",
|
||||
"forgotPassword": "Forgot Password",
|
||||
"password": "password",
|
||||
"signUp": "Sign up",
|
||||
"submit": "Submit",
|
||||
"title": "Title",
|
||||
"continue": "Continue",
|
||||
"enterEmail": "Enter your email",
|
||||
"authLoginTitle": "Log In",
|
||||
"authLoginEnterPassword": "Enter your password",
|
||||
"commonPassword": "Password",
|
||||
"commonBack": "Back",
|
||||
"authForgotPasswordTitle": "Forgot password?",
|
||||
"authForgotPasswordResetPassword": "Reset password",
|
||||
"createPassword": "Create your password",
|
||||
"createAPassword": "Create a password",
|
||||
"authRegisterAlreadyHaveAccount": "Already have an account?",
|
||||
"commonAppName": "BlueWave Uptime",
|
||||
"authLoginEnterEmail": "Enter your email",
|
||||
"authRegisterTitle": "Create an account",
|
||||
"authRegisterStepOneTitle": "Create your account",
|
||||
"authRegisterStepOneDescription": "Enter your details to get started",
|
||||
"authRegisterStepTwoTitle": "Set up your profile",
|
||||
"authRegisterStepTwoDescription": "Tell us more about yourself",
|
||||
"authRegisterStepThreeTitle": "Almost done!",
|
||||
"authRegisterStepThreeDescription": "Review your information",
|
||||
"authForgotPasswordDescription": "No worries, we'll send you reset instructions.",
|
||||
"authForgotPasswordSendInstructions": "Send instructions",
|
||||
"authForgotPasswordBackTo": "Back to",
|
||||
"authCheckEmailTitle": "Check your email",
|
||||
"authCheckEmailDescription": "We sent a password reset link to {{email}}",
|
||||
"authCheckEmailResendEmail": "Resend email",
|
||||
"authCheckEmailBackTo": "Back to",
|
||||
"goBackTo": "Go back to",
|
||||
"authCheckEmailDidntReceiveEmail": "Didn't receive the email?",
|
||||
"authCheckEmailClickToResend": "Click to resend",
|
||||
"authSetNewPasswordTitle": "Set new password",
|
||||
"authSetNewPasswordDescription": "Your new password must be different from previously used passwords.",
|
||||
"authSetNewPasswordNewPassword": "New password",
|
||||
"authSetNewPasswordConfirmPassword": "Confirm password",
|
||||
"confirmPassword": "Confirm your password",
|
||||
"authSetNewPasswordResetPassword": "Reset password",
|
||||
"authSetNewPasswordBackTo": "Back to",
|
||||
"authPasswordMustBeAtLeast": "Must be at least",
|
||||
"authPasswordCharactersLong": "8 characters long",
|
||||
"authPasswordMustContainAtLeast": "Must contain at least",
|
||||
"authPasswordSpecialCharacter": "one special character",
|
||||
"authPasswordOneNumber": "one number",
|
||||
"authPasswordUpperCharacter": "one upper character",
|
||||
"authPasswordLowerCharacter": "one lower character",
|
||||
"authPasswordConfirmAndPassword": "Confirm password and password",
|
||||
"authPasswordMustMatch": "must match",
|
||||
"friendlyError": "Something went wrong...",
|
||||
"unknownError": "An unknown error occurred",
|
||||
"unauthorized": "Unauthorized access",
|
||||
"authAdminExists": "Admin already exists",
|
||||
"authInviteNotFound": "Invite not found",
|
||||
"unknownService": "Unknown service",
|
||||
"noAuthToken": "No auth token provided",
|
||||
"invalidAuthToken": "Invalid auth token",
|
||||
"expiredAuthToken": "Token expired",
|
||||
"noRefreshToken": "No refresh token provided",
|
||||
"invalidRefreshToken": "Invalid refresh token",
|
||||
"expiredRefreshToken": "Refresh token expired",
|
||||
"requestNewAccessToken": "Request new access token",
|
||||
"invalidPayload": "Invalid payload",
|
||||
"verifyOwnerNotFound": "Document not found",
|
||||
"verifyOwnerUnauthorized": "Unauthorized access",
|
||||
"insufficientPermissions": "Insufficient permissions",
|
||||
"dbUserExists": "User already exists",
|
||||
"dbUserNotFound": "User not found",
|
||||
"dbTokenNotFound": "Token not found",
|
||||
"dbResetPasswordBadMatch": "New password must be different from old password",
|
||||
"dbFindMonitorById": "Monitor with id ${monitorId} not found",
|
||||
"dbDeleteChecks": "No checks found for monitor with id ${monitorId}",
|
||||
"authIncorrectPassword": "Incorrect password",
|
||||
"authUnauthorized": "Unauthorized access",
|
||||
"monitorGetById": "Monitor not found",
|
||||
"monitorGetByUserId": "No monitors found for user",
|
||||
"jobQueueWorkerClose": "Error closing worker",
|
||||
"jobQueueDeleteJob": "Job not found in queue",
|
||||
"jobQueueObliterate": "Error obliterating queue",
|
||||
"pingCannotResolve": "No response",
|
||||
"statusPageNotFound": "Status page not found",
|
||||
"statusPageUrlNotUnique": "Status page url must be unique",
|
||||
"dockerFail": "Failed to fetch Docker container information",
|
||||
"dockerNotFound": "Docker container not found",
|
||||
"portFail": "Failed to connect to port",
|
||||
"alertCreate": "Alert created successfully",
|
||||
"alertGetByUser": "Got alerts successfully",
|
||||
"alertGetByMonitor": "Got alerts by Monitor successfully",
|
||||
"alertGetById": "Got alert by Id successfully",
|
||||
"alertEdit": "Alert edited successfully",
|
||||
"alertDelete": "Alert deleted successfully",
|
||||
"authCreateUser": "User created successfully",
|
||||
"authLoginUser": "User logged in successfully",
|
||||
"authLogoutUser": "User logged out successfully",
|
||||
"authUpdateUser": "User updated successfully",
|
||||
"authCreateRecoveryToken": "Recovery token created successfully",
|
||||
"authVerifyRecoveryToken": "Recovery token verified successfully",
|
||||
"authResetPassword": "Password reset successfully",
|
||||
"authAdminCheck": "Admin check completed successfully",
|
||||
"authDeleteUser": "User deleted successfully",
|
||||
"authTokenRefreshed": "Auth token is refreshed",
|
||||
"authGetAllUsers": "Got all users successfully",
|
||||
"inviteIssued": "Invite sent successfully",
|
||||
"inviteVerified": "Invite verified successfully",
|
||||
"checkCreate": "Check created successfully",
|
||||
"checkGet": "Got checks successfully",
|
||||
"checkDelete": "Checks deleted successfully",
|
||||
"checkUpdateTtl": "Checks TTL updated successfully",
|
||||
"monitorGetAll": "Got all monitors successfully",
|
||||
"monitorStatsById": "Got monitor stats by Id successfully",
|
||||
"monitorGetByIdSuccess": "Got monitor by Id successfully",
|
||||
"monitorGetByTeamId": "Got monitors by Team Id successfully",
|
||||
"monitorGetByUserIdSuccess": "Got monitor for ${userId} successfully",
|
||||
"monitorCreate": "Monitor created successfully",
|
||||
"monitorDelete": "Monitor deleted successfully",
|
||||
"monitorEdit": "Monitor edited successfully",
|
||||
"monitorCertificate": "Got monitor certificate successfully",
|
||||
"monitorDemoAdded": "Successfully added demo monitors",
|
||||
"queueGetMetrics": "Got metrics successfully",
|
||||
"queueAddJob": "Job added successfully",
|
||||
"queueObliterate": "Queue obliterated",
|
||||
"jobQueueDeleteJobSuccess": "Job removed successfully",
|
||||
"jobQueuePauseJob": "Job paused successfully",
|
||||
"jobQueueResumeJob": "Job resumed successfully",
|
||||
"maintenanceWindowGetById": "Got Maintenance Window by Id successfully",
|
||||
"maintenanceWindowCreate": "Maintenance Window created successfully",
|
||||
"maintenanceWindowGetByTeam": "Got Maintenance Windows by Team successfully",
|
||||
"maintenanceWindowDelete": "Maintenance Window deleted successfully",
|
||||
"maintenanceWindowEdit": "Maintenance Window edited successfully",
|
||||
"pingSuccess": "Success",
|
||||
"getAppSettings": "Got app settings successfully",
|
||||
"updateAppSettings": "Updated app settings successfully",
|
||||
"statusPageByUrl": "Got status page by url successfully",
|
||||
"statusPageCreate": "Status page created successfully",
|
||||
"newTermsAdded": "New terms added to POEditor",
|
||||
"dockerSuccess": "Docker container status fetched successfully",
|
||||
"portSuccess": "Port connected successfully",
|
||||
"monitorPause": "Monitor paused successfully",
|
||||
"monitorResume": "Monitor resumed successfully",
|
||||
"statusPageDelete": "Status page deleted successfully",
|
||||
"statusPageUpdate": "Status page updated successfully"
|
||||
}
|
||||
@@ -1,10 +1,12 @@
|
||||
import logger from "../utils/logger.js";
|
||||
import { errorMessages } from "../utils/messages.js";
|
||||
import ServiceRegistry from "../service/serviceRegistry.js";
|
||||
import StringService from "../service/stringService.js";
|
||||
|
||||
const handleErrors = (error, req, res, next) => {
|
||||
const status = error.status || 500;
|
||||
const message = error.message || errorMessages.FRIENDLY_ERROR;
|
||||
const service = error.service || errorMessages.UNKNOWN_SERVICE;
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
const message = error.message || stringService.friendlyError;
|
||||
const service = error.service || stringService.unknownService;
|
||||
logger.error({
|
||||
message: message,
|
||||
service: service,
|
||||
|
||||
@@ -2,17 +2,17 @@ import jwt from "jsonwebtoken";
|
||||
const TOKEN_PREFIX = "Bearer ";
|
||||
const SERVICE_NAME = "allowedRoles";
|
||||
import ServiceRegistry from "../service/serviceRegistry.js";
|
||||
import StringService from "../service/stringService.js";
|
||||
import SettingsService from "../service/settingsService.js";
|
||||
|
||||
import { errorMessages } from "../utils/messages.js";
|
||||
|
||||
const isAllowed = (allowedRoles) => {
|
||||
return (req, res, next) => {
|
||||
const token = req.headers["authorization"];
|
||||
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
// If no token is pressent, return an error
|
||||
if (!token) {
|
||||
const error = new Error(errorMessages.NO_AUTH_TOKEN);
|
||||
const error = new Error(stringService.noAuthToken);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
@@ -21,7 +21,7 @@ const isAllowed = (allowedRoles) => {
|
||||
|
||||
// If the token is improperly formatted, return an error
|
||||
if (!token.startsWith(TOKEN_PREFIX)) {
|
||||
const error = new Error(errorMessages.INVALID_AUTH_TOKEN);
|
||||
const error = new Error(stringService.invalidAuthToken);
|
||||
error.status = 400;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
@@ -41,7 +41,7 @@ const isAllowed = (allowedRoles) => {
|
||||
next();
|
||||
return;
|
||||
} else {
|
||||
const error = new Error(errorMessages.INSUFFICIENT_PERMISSIONS);
|
||||
const error = new Error(stringService.insufficientPermissions);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
|
||||
11
Server/middleware/languageMiddleware.js
Normal file
11
Server/middleware/languageMiddleware.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const languageMiddleware = (stringService, translationService) => (req, res, next) => {
|
||||
const acceptLanguage = req.headers['accept-language'] || 'en';
|
||||
const language = acceptLanguage.split(',')[0].slice(0, 2).toLowerCase();
|
||||
|
||||
translationService.setLanguage(language);
|
||||
stringService.setLanguage(language);
|
||||
|
||||
next();
|
||||
};
|
||||
|
||||
export default languageMiddleware;
|
||||
@@ -1,7 +1,7 @@
|
||||
import jwt from "jsonwebtoken";
|
||||
import { errorMessages } from "../utils/messages.js";
|
||||
import ServiceRegistry from "../service/serviceRegistry.js";
|
||||
import SettingsService from "../service/settingsService.js";
|
||||
import StringService from "../service/stringService.js";
|
||||
const SERVICE_NAME = "verifyJWT";
|
||||
const TOKEN_PREFIX = "Bearer ";
|
||||
|
||||
@@ -14,10 +14,11 @@ const TOKEN_PREFIX = "Bearer ";
|
||||
* @returns {express.Response}
|
||||
*/
|
||||
const verifyJWT = (req, res, next) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
const token = req.headers["authorization"];
|
||||
// Make sure a token is provided
|
||||
if (!token) {
|
||||
const error = new Error(errorMessages.NO_AUTH_TOKEN);
|
||||
const error = new Error(stringService.noAuthToken);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
@@ -25,7 +26,7 @@ const verifyJWT = (req, res, next) => {
|
||||
}
|
||||
// Make sure it is properly formatted
|
||||
if (!token.startsWith(TOKEN_PREFIX)) {
|
||||
const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token
|
||||
const error = new Error(stringService.invalidAuthToken); // Instantiate a new Error object for improperly formatted token
|
||||
error.status = 400;
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "verifyJWT";
|
||||
@@ -43,7 +44,7 @@ const verifyJWT = (req, res, next) => {
|
||||
handleExpiredJwtToken(req, res, next);
|
||||
} else {
|
||||
// Invalid token (signature or token altered or other issue)
|
||||
const errorMessage = errorMessages.INVALID_AUTH_TOKEN;
|
||||
const errorMessage = stringService.invalidAuthToken;
|
||||
return res.status(401).json({ success: false, msg: errorMessage });
|
||||
}
|
||||
} else {
|
||||
@@ -55,12 +56,13 @@ const verifyJWT = (req, res, next) => {
|
||||
};
|
||||
|
||||
function handleExpiredJwtToken(req, res, next) {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
// check for refreshToken
|
||||
const refreshToken = req.headers["x-refresh-token"];
|
||||
|
||||
if (!refreshToken) {
|
||||
// No refresh token provided
|
||||
const error = new Error(errorMessages.NO_REFRESH_TOKEN);
|
||||
const error = new Error(stringService.noRefreshToken);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "handleExpiredJwtToken";
|
||||
@@ -76,8 +78,8 @@ function handleExpiredJwtToken(req, res, next) {
|
||||
// Invalid or expired refresh token, trigger logout
|
||||
const errorMessage =
|
||||
refreshErr.name === "TokenExpiredError"
|
||||
? errorMessages.EXPIRED_REFRESH_TOKEN
|
||||
: errorMessages.INVALID_REFRESH_TOKEN;
|
||||
? stringService.expiredRefreshToken
|
||||
: stringService.invalidRefreshToken;
|
||||
const error = new Error(errorMessage);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
@@ -87,7 +89,7 @@ function handleExpiredJwtToken(req, res, next) {
|
||||
// Refresh token is valid and unexpired, request for new access token
|
||||
res.status(403).json({
|
||||
success: false,
|
||||
msg: errorMessages.REQUEST_NEW_ACCESS_TOKEN,
|
||||
msg: stringService.requestNewAccessToken,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
import logger from "../utils/logger.js";
|
||||
import { errorMessages } from "../utils/messages.js";
|
||||
import ServiceRegistry from "../service/serviceRegistry.js";
|
||||
import StringService from "../service/stringService.js";
|
||||
const SERVICE_NAME = "verifyOwnership";
|
||||
|
||||
const verifyOwnership = (Model, paramName) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
return async (req, res, next) => {
|
||||
const userId = req.user._id;
|
||||
const documentId = req.params[paramName];
|
||||
@@ -11,11 +13,11 @@ const verifyOwnership = (Model, paramName) => {
|
||||
//If the document is not found, return a 404 error
|
||||
if (!doc) {
|
||||
logger.error({
|
||||
message: errorMessages.VERIFY_OWNER_NOT_FOUND,
|
||||
message: stringService.verifyOwnerNotFound,
|
||||
service: SERVICE_NAME,
|
||||
method: "verifyOwnership",
|
||||
});
|
||||
const error = new Error(errorMessages.VERIFY_OWNER_NOT_FOUND);
|
||||
const error = new Error(stringService.verifyOwnerNotFound);
|
||||
error.status = 404;
|
||||
throw error;
|
||||
}
|
||||
@@ -23,7 +25,7 @@ const verifyOwnership = (Model, paramName) => {
|
||||
// Special case for User model, as it will not have a `userId` field as other docs will
|
||||
if (Model.modelName === "User") {
|
||||
if (userId.toString() !== doc._id.toString()) {
|
||||
const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED);
|
||||
const error = new Error(stringService.verifyOwnerUnauthorized);
|
||||
error.status = 403;
|
||||
throw error;
|
||||
}
|
||||
@@ -33,7 +35,7 @@ const verifyOwnership = (Model, paramName) => {
|
||||
|
||||
// If the userID does not match the document's userID, return a 403 error
|
||||
if (userId.toString() !== doc.userId.toString()) {
|
||||
const error = new Error(errorMessages.VERIFY_OWNER_UNAUTHORIZED);
|
||||
const error = new Error(stringService.verifyOwnerUnauthorized);
|
||||
error.status = 403;
|
||||
throw error;
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ const jwt = require("jsonwebtoken");
|
||||
const logger = require("../utils/logger");
|
||||
const SERVICE_NAME = "verifyAdmin";
|
||||
const TOKEN_PREFIX = "Bearer ";
|
||||
const { errorMessages } = require("../utils/messages");
|
||||
import ServiceRegistry from "../service/serviceRegistry.js";
|
||||
import SettingsService from "../service/settingsService.js";
|
||||
import StringService from "../service/stringService.js";
|
||||
/**
|
||||
* Verifies the JWT token
|
||||
* @function
|
||||
@@ -14,10 +14,11 @@ import SettingsService from "../service/settingsService.js";
|
||||
* @returns {express.Response}
|
||||
*/
|
||||
const verifySuperAdmin = (req, res, next) => {
|
||||
const stringService = ServiceRegistry.get(StringService.SERVICE_NAME);
|
||||
const token = req.headers["authorization"];
|
||||
// Make sure a token is provided
|
||||
if (!token) {
|
||||
const error = new Error(errorMessages.NO_AUTH_TOKEN);
|
||||
const error = new Error(stringService.noAuthToken);
|
||||
error.status = 401;
|
||||
error.service = SERVICE_NAME;
|
||||
next(error);
|
||||
@@ -25,7 +26,7 @@ const verifySuperAdmin = (req, res, next) => {
|
||||
}
|
||||
// Make sure it is properly formatted
|
||||
if (!token.startsWith(TOKEN_PREFIX)) {
|
||||
const error = new Error(errorMessages.INVALID_AUTH_TOKEN); // Instantiate a new Error object for improperly formatted token
|
||||
const error = new Error(stringService.invalidAuthToken); // Instantiate a new Error object for improperly formatted token
|
||||
error.status = 400;
|
||||
error.service = SERVICE_NAME;
|
||||
error.method = "verifySuperAdmin";
|
||||
@@ -44,21 +45,21 @@ const verifySuperAdmin = (req, res, next) => {
|
||||
service: SERVICE_NAME,
|
||||
method: "verifySuperAdmin",
|
||||
stack: err.stack,
|
||||
details: errorMessages.INVALID_AUTH_TOKEN,
|
||||
details: stringService.invalidAuthToken,
|
||||
});
|
||||
return res
|
||||
.status(401)
|
||||
.json({ success: false, msg: errorMessages.INVALID_AUTH_TOKEN });
|
||||
.json({ success: false, msg: stringService.invalidAuthToken });
|
||||
}
|
||||
|
||||
if (decoded.role.includes("superadmin") === false) {
|
||||
logger.error({
|
||||
message: errorMessages.INVALID_AUTH_TOKEN,
|
||||
message: stringService.invalidAuthToken,
|
||||
service: SERVICE_NAME,
|
||||
method: "verifySuperAdmin",
|
||||
stack: err.stack,
|
||||
});
|
||||
return res.status(401).json({ success: false, msg: errorMessages.UNAUTHORIZED });
|
||||
return res.status(401).json({ success: false, msg: stringService.unauthorized });
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
12
Server/nodemon.json
Normal file
12
Server/nodemon.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"ignore": [
|
||||
"locales/*",
|
||||
"*.log",
|
||||
"node_modules/*"
|
||||
],
|
||||
"watch": [
|
||||
"*.js",
|
||||
"*.json"
|
||||
],
|
||||
"ext": "js,json"
|
||||
}
|
||||
37
Server/package-lock.json
generated
37
Server/package-lock.json
generated
@@ -11,7 +11,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"bcrypt": "5.1.1",
|
||||
"bullmq": "5.40.2",
|
||||
"bullmq": "5.40.4",
|
||||
"cors": "^2.8.5",
|
||||
"dockerode": "4.0.4",
|
||||
"dotenv": "^16.4.5",
|
||||
@@ -19,6 +19,7 @@
|
||||
"handlebars": "^4.7.8",
|
||||
"helmet": "^8.0.0",
|
||||
"ioredis": "^5.4.2",
|
||||
"jmespath": "^0.16.0",
|
||||
"joi": "^17.13.1",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
"mailersend": "^2.2.0",
|
||||
@@ -1748,9 +1749,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/bullmq": {
|
||||
"version": "5.40.2",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.2.tgz",
|
||||
"integrity": "sha512-Cn4NUpwGAF4WnuXR2kTZCTAUEUHajSCn/IqiDG9ry1kVvAwwwg1Ati3J5HN2uZjqD5PBfNDXYnsc2+0PzakDwg==",
|
||||
"version": "5.40.4",
|
||||
"resolved": "https://registry.npmjs.org/bullmq/-/bullmq-5.40.4.tgz",
|
||||
"integrity": "sha512-MaIOhc31ZbVi9HbY0VAalsXoywelzEPNr6dojoKSMCXDnEVTQ27LkT5LA0Mlpr7ZunMLfpH94SLYrWNsPMsQrg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cron-parser": "^4.9.0",
|
||||
@@ -3071,9 +3072,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/eslint": {
|
||||
"version": "9.20.0",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.0.tgz",
|
||||
"integrity": "sha512-aL4F8167Hg4IvsW89ejnpTwx+B/UQRzJPGgbIOl+4XqffWsahVVsLEWoZvnrVuwpWmnRd7XeXmQI1zlKcFDteA==",
|
||||
"version": "9.20.1",
|
||||
"resolved": "https://registry.npmjs.org/eslint/-/eslint-9.20.1.tgz",
|
||||
"integrity": "sha512-m1mM33o6dBUjxl2qb6wv6nGNwCAsns1eKtaQ4l/NPHeTvhiUPbtdfMyktxN4B3fgHIgsYh1VT3V9txblpQHq+g==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
@@ -3868,10 +3869,11 @@
|
||||
}
|
||||
},
|
||||
"node_modules/globals": {
|
||||
"version": "15.14.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.14.0.tgz",
|
||||
"integrity": "sha512-OkToC372DtlQeje9/zHIo5CT8lRP/FUgEOKBEhU4e0abL7J7CD24fD9ohiLN5hagG/kWCYj4K5oaxxtj2Z0Dig==",
|
||||
"version": "15.15.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz",
|
||||
"integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
@@ -4441,6 +4443,15 @@
|
||||
"@pkgjs/parseargs": "^0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/jmespath": {
|
||||
"version": "0.16.0",
|
||||
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.16.0.tgz",
|
||||
"integrity": "sha512-9FzQjJ7MATs1tSpnco1K6ayiYE3figslrXA72G2HQ/n76RzvYlofyi5QM+iX4YRs/pu3yzxlVQSST23+dMDknw==",
|
||||
"license": "Apache-2.0",
|
||||
"engines": {
|
||||
"node": ">= 0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/joi": {
|
||||
"version": "17.13.3",
|
||||
"resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz",
|
||||
@@ -6882,9 +6893,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prettier": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.0.tgz",
|
||||
"integrity": "sha512-quyMrVt6svPS7CjQ9gKb3GLEX/rl3BCL2oa/QkNcXv4YNVBC9olt3s+H7ukto06q7B1Qz46PbrKLO34PR6vXcA==",
|
||||
"version": "3.5.1",
|
||||
"resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.1.tgz",
|
||||
"integrity": "sha512-hPpFQvHwL3Qv5AdRvBFMhnKo4tYxp0ReXiPn2bxkiohEX6mBeBwEpBSQTkD458RaaDKQMYSp4hX4UtfUTA5wDw==",
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"prettier": "bin/prettier.cjs"
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"dependencies": {
|
||||
"axios": "^1.7.2",
|
||||
"bcrypt": "5.1.1",
|
||||
"bullmq": "5.40.2",
|
||||
"bullmq": "5.40.4",
|
||||
"cors": "^2.8.5",
|
||||
"dockerode": "4.0.4",
|
||||
"dotenv": "^16.4.5",
|
||||
@@ -26,6 +26,7 @@
|
||||
"handlebars": "^4.7.8",
|
||||
"helmet": "^8.0.0",
|
||||
"ioredis": "^5.4.2",
|
||||
"jmespath": "^0.16.0",
|
||||
"joi": "^17.13.1",
|
||||
"jsonwebtoken": "9.0.2",
|
||||
"mailersend": "^2.2.0",
|
||||
|
||||
@@ -12,7 +12,7 @@ const QUEUE_LOOKUP = {
|
||||
};
|
||||
const getSchedulerId = (monitor) => `scheduler:${monitor.type}:${monitor._id}`;
|
||||
|
||||
import { successMessages, errorMessages } from "../utils/messages.js";
|
||||
|
||||
class NewJobQueue {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
|
||||
@@ -22,6 +22,7 @@ class NewJobQueue {
|
||||
networkService,
|
||||
notificationService,
|
||||
settingsService,
|
||||
stringService,
|
||||
logger,
|
||||
Queue,
|
||||
Worker
|
||||
@@ -44,6 +45,7 @@ class NewJobQueue {
|
||||
this.settingsService = settingsService;
|
||||
this.logger = logger;
|
||||
this.Worker = Worker;
|
||||
this.stringService = stringService;
|
||||
|
||||
QUEUE_NAMES.forEach((name) => {
|
||||
this.queues[name] = new Queue(name, { connection });
|
||||
@@ -455,7 +457,7 @@ class NewJobQueue {
|
||||
|
||||
if (wasDeleted === true) {
|
||||
this.logger.info({
|
||||
message: successMessages.JOB_QUEUE_DELETE_JOB,
|
||||
message: this.stringService.jobQueueDeleteJob,
|
||||
service: SERVICE_NAME,
|
||||
method: "deleteJob",
|
||||
details: `Deleted job ${monitor._id}`,
|
||||
@@ -464,7 +466,7 @@ class NewJobQueue {
|
||||
await this.scaleWorkers(workerStats, queue);
|
||||
} else {
|
||||
this.logger.error({
|
||||
message: errorMessages.JOB_QUEUE_DELETE_JOB,
|
||||
message: this.stringService.jobQueueDeleteJob,
|
||||
service: SERVICE_NAME,
|
||||
method: "deleteJob",
|
||||
details: `Failed to delete job ${monitor._id}`,
|
||||
@@ -587,7 +589,7 @@ class NewJobQueue {
|
||||
|
||||
const metrics = await this.getMetrics();
|
||||
this.logger.info({
|
||||
message: successMessages.JOB_QUEUE_OBLITERATE,
|
||||
message: this.stringService.jobQueueObliterate,
|
||||
service: SERVICE_NAME,
|
||||
method: "obliterate",
|
||||
details: metrics,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { errorMessages, successMessages } from "../utils/messages.js";
|
||||
import jmespath from 'jmespath';
|
||||
const SERVICE_NAME = "NetworkService";
|
||||
const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push";
|
||||
|
||||
@@ -13,7 +13,8 @@ const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push";
|
||||
*/
|
||||
class NetworkService {
|
||||
static SERVICE_NAME = SERVICE_NAME;
|
||||
constructor(axios, ping, logger, http, Docker, net) {
|
||||
|
||||
constructor(axios, ping, logger, http, Docker, net, stringService) {
|
||||
this.TYPE_PING = "ping";
|
||||
this.TYPE_HTTP = "http";
|
||||
this.TYPE_PAGESPEED = "pagespeed";
|
||||
@@ -30,6 +31,7 @@ class NetworkService {
|
||||
this.http = http;
|
||||
this.Docker = Docker;
|
||||
this.net = net;
|
||||
this.stringService = stringService;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -87,13 +89,13 @@ class NetworkService {
|
||||
if (error) {
|
||||
pingResponse.status = false;
|
||||
pingResponse.code = this.PING_ERROR;
|
||||
pingResponse.message = errorMessages.PING_CANNOT_RESOLVE;
|
||||
pingResponse.message = "No response";
|
||||
return pingResponse;
|
||||
}
|
||||
|
||||
pingResponse.code = 200;
|
||||
pingResponse.status = response.alive;
|
||||
pingResponse.message = successMessages.PING_SUCCESS;
|
||||
pingResponse.message = "Success";
|
||||
return pingResponse;
|
||||
} catch (error) {
|
||||
error.service = this.SERVICE_NAME;
|
||||
@@ -121,20 +123,19 @@ class NetworkService {
|
||||
*/
|
||||
async requestHttp(job) {
|
||||
try {
|
||||
const url = job.data.url;
|
||||
const { url, secret, _id, name, teamId, type, jsonPath, matchMethod, expectedValue } = job.data;
|
||||
const config = {};
|
||||
|
||||
job.data.secret !== undefined &&
|
||||
(config.headers = { Authorization: `Bearer ${job.data.secret}` });
|
||||
secret !== undefined && (config.headers = { Authorization: `Bearer ${secret}` });
|
||||
|
||||
const { response, responseTime, error } = await this.timeRequest(() =>
|
||||
this.axios.get(url, config)
|
||||
);
|
||||
|
||||
const httpResponse = {
|
||||
monitorId: job.data._id,
|
||||
teamId: job.data.teamId,
|
||||
type: job.data.type,
|
||||
monitorId: _id,
|
||||
teamId,
|
||||
type,
|
||||
responseTime,
|
||||
payload: response?.data,
|
||||
};
|
||||
@@ -143,12 +144,62 @@ class NetworkService {
|
||||
const code = error.response?.status || this.NETWORK_ERROR;
|
||||
httpResponse.code = code;
|
||||
httpResponse.status = false;
|
||||
httpResponse.message = this.http.STATUS_CODES[code] || "Network Error";
|
||||
httpResponse.message = this.http.STATUS_CODES[code] || this.stringService.httpNetworkError;
|
||||
return httpResponse;
|
||||
}
|
||||
httpResponse.status = true;
|
||||
|
||||
httpResponse.code = response.status;
|
||||
httpResponse.message = this.http.STATUS_CODES[response.status];
|
||||
|
||||
if (!expectedValue) {
|
||||
// not configure expected value, return
|
||||
httpResponse.status = true;
|
||||
httpResponse.message = this.http.STATUS_CODES[response.status];
|
||||
return httpResponse;
|
||||
}
|
||||
|
||||
// validate if response data match expected value
|
||||
let result = response?.data;
|
||||
|
||||
this.logger.info({
|
||||
service: this.SERVICE_NAME,
|
||||
method: "requestHttp",
|
||||
message: `Job: [${name}](${_id}) match result with expected value`,
|
||||
details: { expectedValue, result, jsonPath, matchMethod }
|
||||
});
|
||||
|
||||
if (jsonPath) {
|
||||
const contentType = response.headers['content-type'];
|
||||
|
||||
const isJson = contentType?.includes('application/json');
|
||||
if (!isJson) {
|
||||
httpResponse.status = false;
|
||||
httpResponse.message = this.stringService.httpNotJson;
|
||||
return httpResponse;
|
||||
}
|
||||
|
||||
try {
|
||||
result = jmespath.search(result, jsonPath);
|
||||
} catch (error) {
|
||||
httpResponse.status = false;
|
||||
httpResponse.message = this.stringService.httpJsonPathError;
|
||||
return httpResponse;
|
||||
}
|
||||
}
|
||||
|
||||
if (result === null || result === undefined) {
|
||||
httpResponse.status = false;
|
||||
httpResponse.message = this.stringService.httpEmptyResult;
|
||||
return httpResponse;
|
||||
}
|
||||
|
||||
let match;
|
||||
result = typeof result === "object" ? JSON.stringify(result) : result.toString();
|
||||
if (matchMethod === "include") match = result.includes(expectedValue);
|
||||
else if (matchMethod === "regex") match = new RegExp(expectedValue).test(result);
|
||||
else match = result === expectedValue;
|
||||
|
||||
httpResponse.status = match;
|
||||
httpResponse.message = match ? this.stringService.httpMatchSuccess : this.stringService.httpMatchFail;
|
||||
return httpResponse;
|
||||
} catch (error) {
|
||||
error.service = this.SERVICE_NAME;
|
||||
@@ -240,7 +291,7 @@ class NetworkService {
|
||||
const containers = await docker.listContainers({ all: true });
|
||||
const containerExists = containers.some((c) => c.Id.startsWith(job.data.url));
|
||||
if (!containerExists) {
|
||||
throw new Error(errorMessages.DOCKER_NOT_FOUND);
|
||||
throw new Error(this.stringService.dockerNotFound);
|
||||
}
|
||||
const container = docker.getContainer(job.data.url);
|
||||
|
||||
@@ -257,12 +308,12 @@ class NetworkService {
|
||||
if (error) {
|
||||
dockerResponse.status = false;
|
||||
dockerResponse.code = error.statusCode || this.NETWORK_ERROR;
|
||||
dockerResponse.message = error.reason || errorMessages.DOCKER_FAIL;
|
||||
dockerResponse.message = error.reason || "Failed to fetch Docker container information";
|
||||
return dockerResponse;
|
||||
}
|
||||
dockerResponse.status = response?.State?.Status === "running" ? true : false;
|
||||
dockerResponse.code = 200;
|
||||
dockerResponse.message = successMessages.DOCKER_SUCCESS;
|
||||
dockerResponse.message = "Docker container status fetched successfully";
|
||||
return dockerResponse;
|
||||
} catch (error) {
|
||||
error.service = this.SERVICE_NAME;
|
||||
@@ -310,13 +361,13 @@ class NetworkService {
|
||||
if (error) {
|
||||
portResponse.status = false;
|
||||
portResponse.code = this.NETWORK_ERROR;
|
||||
portResponse.message = errorMessages.PORT_FAIL;
|
||||
portResponse.message = this.stringService.portFail;
|
||||
return portResponse;
|
||||
}
|
||||
|
||||
portResponse.status = response.success;
|
||||
portResponse.code = 200;
|
||||
portResponse.message = successMessages.PORT_SUCCESS;
|
||||
portResponse.message = this.stringService.portSuccess;
|
||||
return portResponse;
|
||||
} catch (error) {
|
||||
error.service = this.SERVICE_NAME;
|
||||
|
||||
358
Server/service/stringService.js
Normal file
358
Server/service/stringService.js
Normal file
@@ -0,0 +1,358 @@
|
||||
class StringService {
|
||||
static SERVICE_NAME = "StringService";
|
||||
|
||||
constructor(translationService) {
|
||||
if (StringService.instance) {
|
||||
return StringService.instance;
|
||||
}
|
||||
|
||||
this.translationService = translationService;
|
||||
this._language = 'en'; // default language
|
||||
StringService.instance = this;
|
||||
}
|
||||
|
||||
setLanguage(language) {
|
||||
this._language = language;
|
||||
}
|
||||
|
||||
get language() {
|
||||
return this._language;
|
||||
}
|
||||
|
||||
// Auth Messages
|
||||
get dontHaveAccount() {
|
||||
return this.translationService.getTranslation('dontHaveAccount');
|
||||
}
|
||||
|
||||
get email() {
|
||||
return this.translationService.getTranslation('email');
|
||||
}
|
||||
|
||||
get forgotPassword() {
|
||||
return this.translationService.getTranslation('forgotPassword');
|
||||
}
|
||||
|
||||
get password() {
|
||||
return this.translationService.getTranslation('password');
|
||||
}
|
||||
|
||||
get signUp() {
|
||||
return this.translationService.getTranslation('signUp');
|
||||
}
|
||||
|
||||
get submit() {
|
||||
return this.translationService.getTranslation('submit');
|
||||
}
|
||||
|
||||
get title() {
|
||||
return this.translationService.getTranslation('title');
|
||||
}
|
||||
|
||||
get continue() {
|
||||
return this.translationService.getTranslation('continue');
|
||||
}
|
||||
|
||||
get enterEmail() {
|
||||
return this.translationService.getTranslation('enterEmail');
|
||||
}
|
||||
|
||||
get authLoginTitle() {
|
||||
return this.translationService.getTranslation('authLoginTitle');
|
||||
}
|
||||
|
||||
get authLoginEnterPassword() {
|
||||
return this.translationService.getTranslation('authLoginEnterPassword');
|
||||
}
|
||||
|
||||
get commonPassword() {
|
||||
return this.translationService.getTranslation('commonPassword');
|
||||
}
|
||||
|
||||
get commonBack() {
|
||||
return this.translationService.getTranslation('commonBack');
|
||||
}
|
||||
|
||||
get authForgotPasswordTitle() {
|
||||
return this.translationService.getTranslation('authForgotPasswordTitle');
|
||||
}
|
||||
|
||||
get authForgotPasswordResetPassword() {
|
||||
return this.translationService.getTranslation('authForgotPasswordResetPassword');
|
||||
}
|
||||
|
||||
get createPassword() {
|
||||
return this.translationService.getTranslation('createPassword');
|
||||
}
|
||||
|
||||
get createAPassword() {
|
||||
return this.translationService.getTranslation('createAPassword');
|
||||
}
|
||||
|
||||
get authRegisterAlreadyHaveAccount() {
|
||||
return this.translationService.getTranslation('authRegisterAlreadyHaveAccount');
|
||||
}
|
||||
|
||||
get commonAppName() {
|
||||
return this.translationService.getTranslation('commonAppName');
|
||||
}
|
||||
|
||||
get authLoginEnterEmail() {
|
||||
return this.translationService.getTranslation('authLoginEnterEmail');
|
||||
}
|
||||
|
||||
get authRegisterTitle() {
|
||||
return this.translationService.getTranslation('authRegisterTitle');
|
||||
}
|
||||
|
||||
get monitorGetAll() {
|
||||
return this.translationService.getTranslation('monitorGetAll');
|
||||
}
|
||||
|
||||
get monitorGetById() {
|
||||
return this.translationService.getTranslation('monitorGetById');
|
||||
}
|
||||
|
||||
get monitorCreate() {
|
||||
return this.translationService.getTranslation('monitorCreate');
|
||||
}
|
||||
|
||||
get monitorEdit() {
|
||||
return this.translationService.getTranslation('monitorEdit');
|
||||
}
|
||||
|
||||
get monitorDelete() {
|
||||
return this.translationService.getTranslation('monitorDelete');
|
||||
}
|
||||
|
||||
get monitorPause() {
|
||||
return this.translationService.getTranslation('monitorPause');
|
||||
}
|
||||
|
||||
get monitorResume() {
|
||||
return this.translationService.getTranslation('monitorResume');
|
||||
}
|
||||
|
||||
get monitorDemoAdded() {
|
||||
return this.translationService.getTranslation('monitorDemoAdded');
|
||||
}
|
||||
|
||||
get monitorStatsById() {
|
||||
return this.translationService.getTranslation('monitorStatsById');
|
||||
}
|
||||
|
||||
get monitorCertificate() {
|
||||
return this.translationService.getTranslation('monitorCertificate');
|
||||
}
|
||||
|
||||
// Maintenance Window Messages
|
||||
get maintenanceWindowCreate() {
|
||||
return this.translationService.getTranslation('maintenanceWindowCreate');
|
||||
}
|
||||
|
||||
get maintenanceWindowGetById() {
|
||||
return this.translationService.getTranslation('maintenanceWindowGetById');
|
||||
}
|
||||
|
||||
get maintenanceWindowGetByTeam() {
|
||||
return this.translationService.getTranslation('maintenanceWindowGetByTeam');
|
||||
}
|
||||
|
||||
get maintenanceWindowDelete() {
|
||||
return this.translationService.getTranslation('maintenanceWindowDelete');
|
||||
}
|
||||
|
||||
get maintenanceWindowEdit() {
|
||||
return this.translationService.getTranslation('maintenanceWindowEdit');
|
||||
}
|
||||
|
||||
// Error Messages
|
||||
get unknownError() {
|
||||
return this.translationService.getTranslation('unknownError');
|
||||
}
|
||||
|
||||
get friendlyError() {
|
||||
return this.translationService.getTranslation('friendlyError');
|
||||
}
|
||||
|
||||
get authIncorrectPassword() {
|
||||
return this.translationService.getTranslation('authIncorrectPassword');
|
||||
}
|
||||
|
||||
get unauthorized() {
|
||||
return this.translationService.getTranslation('unauthorized');
|
||||
}
|
||||
|
||||
get authAdminExists() {
|
||||
return this.translationService.getTranslation('authAdminExists');
|
||||
}
|
||||
|
||||
get authInviteNotFound() {
|
||||
return this.translationService.getTranslation('authInviteNotFound');
|
||||
}
|
||||
|
||||
get unknownService() {
|
||||
return this.translationService.getTranslation('unknownService');
|
||||
}
|
||||
|
||||
get noAuthToken() {
|
||||
return this.translationService.getTranslation('noAuthToken');
|
||||
}
|
||||
|
||||
get invalidAuthToken() {
|
||||
return this.translationService.getTranslation('invalidAuthToken');
|
||||
}
|
||||
|
||||
get expiredAuthToken() {
|
||||
return this.translationService.getTranslation('expiredAuthToken');
|
||||
}
|
||||
|
||||
// Queue Messages
|
||||
get queueGetMetrics() {
|
||||
return this.translationService.getTranslation('queueGetMetrics');
|
||||
}
|
||||
|
||||
get queueAddJob() {
|
||||
return this.translationService.getTranslation('queueAddJob');
|
||||
}
|
||||
|
||||
get queueObliterate() {
|
||||
return this.translationService.getTranslation('queueObliterate');
|
||||
}
|
||||
|
||||
// Job Queue Messages
|
||||
get jobQueueDeleteJobSuccess() {
|
||||
return this.translationService.getTranslation('jobQueueDeleteJobSuccess');
|
||||
}
|
||||
|
||||
get jobQueuePauseJob() {
|
||||
return this.translationService.getTranslation('jobQueuePauseJob');
|
||||
}
|
||||
|
||||
get jobQueueResumeJob() {
|
||||
return this.translationService.getTranslation('jobQueueResumeJob');
|
||||
}
|
||||
|
||||
// Status Page Messages
|
||||
get statusPageByUrl() {
|
||||
return this.translationService.getTranslation('statusPageByUrl');
|
||||
}
|
||||
|
||||
get statusPageCreate() {
|
||||
return this.translationService.getTranslation('statusPageCreate');
|
||||
}
|
||||
|
||||
get statusPageDelete() {
|
||||
return this.translationService.getTranslation('statusPageDelete');
|
||||
}
|
||||
|
||||
get statusPageUpdate() {
|
||||
return this.translationService.getTranslation('statusPageUpdate');
|
||||
}
|
||||
|
||||
get statusPageNotFound() {
|
||||
return this.translationService.getTranslation('statusPageNotFound');
|
||||
}
|
||||
|
||||
get statusPageByTeamId() {
|
||||
return this.translationService.getTranslation('statusPageByTeamId');
|
||||
}
|
||||
|
||||
get statusPageUrlNotUnique() {
|
||||
return this.translationService.getTranslation('statusPageUrlNotUnique');
|
||||
}
|
||||
|
||||
// Docker Messages
|
||||
get dockerFail() {
|
||||
return this.translationService.getTranslation('dockerFail');
|
||||
}
|
||||
|
||||
get dockerNotFound() {
|
||||
return this.translationService.getTranslation('dockerNotFound');
|
||||
}
|
||||
|
||||
get dockerSuccess() {
|
||||
return this.translationService.getTranslation('dockerSuccess');
|
||||
}
|
||||
|
||||
// Port Messages
|
||||
get portFail() {
|
||||
return this.translationService.getTranslation('portFail');
|
||||
}
|
||||
|
||||
get portSuccess() {
|
||||
return this.translationService.getTranslation('portSuccess');
|
||||
}
|
||||
|
||||
// Alert Messages
|
||||
get alertCreate() {
|
||||
return this.translationService.getTranslation('alertCreate');
|
||||
}
|
||||
|
||||
get alertGetByUser() {
|
||||
return this.translationService.getTranslation('alertGetByUser');
|
||||
}
|
||||
|
||||
get alertGetByMonitor() {
|
||||
return this.translationService.getTranslation('alertGetByMonitor');
|
||||
}
|
||||
|
||||
get alertGetById() {
|
||||
return this.translationService.getTranslation('alertGetById');
|
||||
}
|
||||
|
||||
get alertEdit() {
|
||||
return this.translationService.getTranslation('alertEdit');
|
||||
}
|
||||
|
||||
get alertDelete() {
|
||||
return this.translationService.getTranslation('alertDelete');
|
||||
}
|
||||
|
||||
getDeletedCount(count) {
|
||||
return this.translationService.getTranslation('deletedCount')
|
||||
.replace('{count}', count);
|
||||
}
|
||||
|
||||
get pingSuccess() {
|
||||
return this.translationService.getTranslation('pingSuccess');
|
||||
}
|
||||
|
||||
get getAppSettings() {
|
||||
return this.translationService.getTranslation('getAppSettings');
|
||||
}
|
||||
|
||||
get httpNetworkError() {
|
||||
return this.translationService.getTranslation('httpNetworkError');
|
||||
}
|
||||
|
||||
get httpNotJson() {
|
||||
return this.translationService.getTranslation('httpNotJson');
|
||||
}
|
||||
|
||||
get httpJsonPathError() {
|
||||
return this.translationService.getTranslation('httpJsonPathError');
|
||||
}
|
||||
|
||||
get httpEmptyResult() {
|
||||
return this.translationService.getTranslation('httpEmptyResult');
|
||||
}
|
||||
|
||||
get httpMatchSuccess() {
|
||||
return this.translationService.getTranslation('httpMatchSuccess');
|
||||
}
|
||||
|
||||
get httpMatchFail() {
|
||||
return this.translationService.getTranslation('httpMatchFail');
|
||||
}
|
||||
|
||||
get updateAppSettings() {
|
||||
return this.translationService.getTranslation('updateAppSettings');
|
||||
}
|
||||
|
||||
getDbFindMonitorById(monitorId) {
|
||||
return this.translationService.getTranslation('dbFindMonitorById')
|
||||
.replace('${monitorId}', monitorId);
|
||||
}
|
||||
}
|
||||
|
||||
export default StringService;
|
||||
90
Server/service/translationService.js
Normal file
90
Server/service/translationService.js
Normal file
@@ -0,0 +1,90 @@
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
|
||||
class TranslationService {
|
||||
static SERVICE_NAME = 'TranslationService';
|
||||
|
||||
constructor(logger) {
|
||||
this.logger = logger;
|
||||
this.translations = {};
|
||||
this._language = 'en';
|
||||
this.localesDir = path.join(process.cwd(), 'locales');
|
||||
}
|
||||
|
||||
setLanguage(language) {
|
||||
this._language = language;
|
||||
}
|
||||
|
||||
get language() {
|
||||
return this._language;
|
||||
}
|
||||
|
||||
async initialize() {
|
||||
try {
|
||||
await this.loadFromFiles();
|
||||
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: 'TranslationService',
|
||||
method: 'initialize',
|
||||
stack: error.stack
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
async loadFromFiles() {
|
||||
try {
|
||||
if (!fs.existsSync(this.localesDir)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(this.localesDir).filter(file => file.endsWith('.json'));
|
||||
|
||||
if (files.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const file of files) {
|
||||
const language = file.replace('.json', '');
|
||||
const filePath = path.join(this.localesDir, file);
|
||||
const content = fs.readFileSync(filePath, 'utf8');
|
||||
this.translations[language] = JSON.parse(content);
|
||||
}
|
||||
|
||||
this.logger.info({
|
||||
message: 'Translations loaded from files successfully',
|
||||
service: 'TranslationService',
|
||||
method: 'loadFromFiles'
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: 'TranslationService',
|
||||
method: 'loadFromFiles',
|
||||
stack: error.stack
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getTranslation(key) {
|
||||
let language = this._language;
|
||||
|
||||
try {
|
||||
return this.translations[language]?.[key] || this.translations['en']?.[key] || key;
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
message: error.message,
|
||||
service: 'TranslationService',
|
||||
method: 'getTranslation',
|
||||
stack: error.stack
|
||||
});
|
||||
return key;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default TranslationService;
|
||||
@@ -1,150 +0,0 @@
|
||||
const errorMessages = {
|
||||
// General Errors:
|
||||
FRIENDLY_ERROR: "Something went wrong...",
|
||||
UNKNOWN_ERROR: "An unknown error occurred",
|
||||
|
||||
// Auth Controller
|
||||
UNAUTHORIZED: "Unauthorized access",
|
||||
AUTH_ADMIN_EXISTS: "Admin already exists",
|
||||
AUTH_INVITE_NOT_FOUND: "Invite not found",
|
||||
|
||||
//Error handling middleware
|
||||
UNKNOWN_SERVICE: "Unknown service",
|
||||
NO_AUTH_TOKEN: "No auth token provided",
|
||||
INVALID_AUTH_TOKEN: "Invalid auth token",
|
||||
EXPIRED_AUTH_TOKEN: "Token expired",
|
||||
NO_REFRESH_TOKEN: "No refresh token provided",
|
||||
INVALID_REFRESH_TOKEN: "Invalid refresh token",
|
||||
EXPIRED_REFRESH_TOKEN: "Refresh token expired",
|
||||
REQUEST_NEW_ACCESS_TOKEN: "Request new access token",
|
||||
|
||||
//Payload
|
||||
INVALID_PAYLOAD: "Invalid payload",
|
||||
|
||||
//Ownership Middleware
|
||||
VERIFY_OWNER_NOT_FOUND: "Document not found",
|
||||
VERIFY_OWNER_UNAUTHORIZED: "Unauthorized access",
|
||||
|
||||
//Permissions Middleware
|
||||
INSUFFICIENT_PERMISSIONS: "Insufficient permissions",
|
||||
|
||||
//DB Errors
|
||||
DB_USER_EXISTS: "User already exists",
|
||||
DB_USER_NOT_FOUND: "User not found",
|
||||
DB_TOKEN_NOT_FOUND: "Token not found",
|
||||
DB_RESET_PASSWORD_BAD_MATCH: "New password must be different from old password",
|
||||
DB_FIND_MONITOR_BY_ID: (monitorId) => `Monitor with id ${monitorId} not found`,
|
||||
DB_DELETE_CHECKS: (monitorId) => `No checks found for monitor with id ${monitorId}`,
|
||||
|
||||
//Auth errors
|
||||
AUTH_INCORRECT_PASSWORD: "Incorrect password",
|
||||
AUTH_UNAUTHORIZED: "Unauthorized access",
|
||||
|
||||
// Monitor Errors
|
||||
MONITOR_GET_BY_ID: "Monitor not found",
|
||||
MONITOR_GET_BY_USER_ID: "No monitors found for user",
|
||||
|
||||
// Job Queue Errors
|
||||
JOB_QUEUE_WORKER_CLOSE: "Error closing worker",
|
||||
JOB_QUEUE_DELETE_JOB: "Job not found in queue",
|
||||
JOB_QUEUE_OBLITERATE: "Error obliterating queue",
|
||||
|
||||
// PING Operations
|
||||
PING_CANNOT_RESOLVE: "No response",
|
||||
|
||||
// Status Page Errors
|
||||
STATUS_PAGE_NOT_FOUND: "Status page not found",
|
||||
STATUS_PAGE_URL_NOT_UNIQUE: "Status page url must be unique",
|
||||
|
||||
// Docker
|
||||
DOCKER_FAIL: "Failed to fetch Docker container information",
|
||||
DOCKER_NOT_FOUND: "Docker container not found",
|
||||
|
||||
// Port
|
||||
PORT_FAIL: "Failed to connect to port",
|
||||
};
|
||||
|
||||
const successMessages = {
|
||||
//Alert Controller
|
||||
ALERT_CREATE: "Alert created successfully",
|
||||
ALERT_GET_BY_USER: "Got alerts successfully",
|
||||
ALERT_GET_BY_MONITOR: "Got alerts by Monitor successfully",
|
||||
ALERT_GET_BY_ID: "Got alert by Id successfully",
|
||||
ALERT_EDIT: "Alert edited successfully",
|
||||
ALERT_DELETE: "Alert deleted successfully",
|
||||
|
||||
// Auth Controller
|
||||
AUTH_CREATE_USER: "User created successfully",
|
||||
AUTH_LOGIN_USER: "User logged in successfully",
|
||||
AUTH_LOGOUT_USER: "User logged out successfully",
|
||||
AUTH_UPDATE_USER: "User updated successfully",
|
||||
AUTH_CREATE_RECOVERY_TOKEN: "Recovery token created successfully",
|
||||
AUTH_VERIFY_RECOVERY_TOKEN: "Recovery token verified successfully",
|
||||
AUTH_RESET_PASSWORD: "Password reset successfully",
|
||||
AUTH_ADMIN_CHECK: "Admin check completed successfully",
|
||||
AUTH_DELETE_USER: "User deleted successfully",
|
||||
AUTH_TOKEN_REFRESHED: "Auth token is refreshed",
|
||||
AUTH_GET_ALL_USERS: "Got all users successfully",
|
||||
|
||||
// Invite Controller
|
||||
INVITE_ISSUED: "Invite sent successfully",
|
||||
INVITE_VERIFIED: "Invite verified successfully",
|
||||
|
||||
// Check Controller
|
||||
CHECK_CREATE: "Check created successfully",
|
||||
CHECK_GET: "Got checks successfully",
|
||||
CHECK_DELETE: "Checks deleted successfully",
|
||||
CHECK_UPDATE_TTL: "Checks TTL updated successfully",
|
||||
|
||||
//Monitor Controller
|
||||
MONITOR_GET_ALL: "Got all monitors successfully",
|
||||
MONITOR_STATS_BY_ID: "Got monitor stats by Id successfully",
|
||||
MONITOR_GET_BY_ID: "Got monitor by Id successfully",
|
||||
MONITOR_GET_BY_TEAM_ID: "Got monitors by Team Id successfully",
|
||||
MONITOR_GET_BY_USER_ID: (userId) => `Got monitor for ${userId} successfully"`,
|
||||
MONITOR_CREATE: "Monitor created successfully",
|
||||
MONITOR_DELETE: "Monitor deleted successfully",
|
||||
MONITOR_EDIT: "Monitor edited successfully",
|
||||
MONITOR_CERTIFICATE: "Got monitor certificate successfully",
|
||||
MONITOR_DEMO_ADDED: "Successfully added demo monitors",
|
||||
|
||||
// Queue Controller
|
||||
QUEUE_GET_METRICS: "Got metrics successfully",
|
||||
QUEUE_ADD_JOB: "Job added successfully",
|
||||
QUEUE_OBLITERATE: "Queue obliterated",
|
||||
|
||||
//Job Queue
|
||||
JOB_QUEUE_DELETE_JOB: "Job removed successfully",
|
||||
JOB_QUEUE_OBLITERATE: "Queue OBLITERATED!!!",
|
||||
JOB_QUEUE_PAUSE_JOB: "Job paused successfully",
|
||||
JOB_QUEUE_RESUME_JOB: "Job resumed successfully",
|
||||
|
||||
//Maintenance Window Controller
|
||||
MAINTENANCE_WINDOW_GET_BY_ID: "Got Maintenance Window by Id successfully",
|
||||
MAINTENANCE_WINDOW_CREATE: "Maintenance Window created successfully",
|
||||
MAINTENANCE_WINDOW_GET_BY_TEAM: "Got Maintenance Windows by Team successfully",
|
||||
MAINTENANCE_WINDOW_DELETE: "Maintenance Window deleted successfully",
|
||||
MAINTENANCE_WINDOW_EDIT: "Maintenance Window edited successfully",
|
||||
|
||||
//Ping Operations
|
||||
PING_SUCCESS: "Success",
|
||||
|
||||
// App Settings
|
||||
GET_APP_SETTINGS: "Got app settings successfully",
|
||||
UPDATE_APP_SETTINGS: "Updated app settings successfully",
|
||||
|
||||
// Status Page
|
||||
STATUS_PAGE_BY_URL: "Got status page by url successfully",
|
||||
STATUS_PAGE: "Got status page successfully",
|
||||
STATUS_PAGE_CREATE: "Status page created successfully",
|
||||
STATUS_PAGE_DELETE: "Status page deleted successfully",
|
||||
STATUS_PAGE_UPDATE: "Status page updated successfully",
|
||||
STATUS_PAGE_BY_TEAM_ID: "Got status pages by team id successfully",
|
||||
// Docker
|
||||
DOCKER_SUCCESS: "Docker container status fetched successfully",
|
||||
|
||||
// Port
|
||||
PORT_SUCCESS: "Port connected successfully",
|
||||
};
|
||||
|
||||
export { errorMessages, successMessages };
|
||||
@@ -194,6 +194,9 @@ const createMonitorBodyValidation = joi.object({
|
||||
}),
|
||||
notifications: joi.array().items(joi.object()),
|
||||
secret: joi.string(),
|
||||
jsonPath: joi.string(),
|
||||
expectedValue: joi.string(),
|
||||
matchMethod: joi.string(),
|
||||
});
|
||||
|
||||
const editMonitorBodyValidation = joi.object({
|
||||
@@ -202,6 +205,9 @@ const editMonitorBodyValidation = joi.object({
|
||||
interval: joi.number(),
|
||||
notifications: joi.array().items(joi.object()),
|
||||
secret: joi.string(),
|
||||
jsonPath: joi.string(),
|
||||
expectedValue: joi.string(),
|
||||
matchMethod: joi.string(),
|
||||
});
|
||||
|
||||
const pauseMonitorParamValidation = joi.object({
|
||||
|
||||
Reference in New Issue
Block a user