diff --git a/client/src/Components/Inputs/Select/index.jsx b/client/src/Components/Inputs/Select/index.jsx
index a132b5d77..c5c488f91 100644
--- a/client/src/Components/Inputs/Select/index.jsx
+++ b/client/src/Components/Inputs/Select/index.jsx
@@ -50,6 +50,7 @@ const Select = ({
onChange,
onBlur,
sx,
+ error = false,
name = "",
labelControlSpacing = 6,
maxWidth,
@@ -93,6 +94,7 @@ const Select = ({
onChange={onChange}
onBlur={onBlur}
displayEmpty
+ error={error}
name={name}
inputProps={{ id: id }}
IconComponent={KeyboardArrowDownIcon}
@@ -172,6 +174,7 @@ Select.propTypes = {
label: PropTypes.string,
placeholder: PropTypes.string,
isHidden: PropTypes.bool,
+ error: PropTypes.bool,
value: PropTypes.oneOfType([PropTypes.string, PropTypes.number, PropTypes.bool])
.isRequired,
items: PropTypes.arrayOf(
diff --git a/client/src/Hooks/monitorHooks.js b/client/src/Hooks/monitorHooks.js
index 616a37877..970b5d97f 100644
--- a/client/src/Hooks/monitorHooks.js
+++ b/client/src/Hooks/monitorHooks.js
@@ -202,6 +202,25 @@ const useFetchStatsByMonitorId = ({
return [monitor, audits, isLoading, networkError];
};
+const useFetchMonitorGames = ({ setGames, updateTrigger }) => {
+ const [isLoading, setIsLoading] = useState(true);
+ useEffect(() => {
+ const fetchGames = async () => {
+ try {
+ setIsLoading(true);
+ const res = await networkService.getMonitorGames();
+ setGames(res.data.data);
+ } catch (error) {
+ createToast({ body: error.message });
+ } finally {
+ setIsLoading(false);
+ }
+ };
+ fetchGames();
+ }, [setGames, updateTrigger]);
+ return [isLoading];
+};
+
const useFetchMonitorById = ({ monitorId, setMonitor, updateTrigger }) => {
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
@@ -357,7 +376,12 @@ const useUpdateMonitor = () => {
expectedValue: monitor.expectedValue,
ignoreTlsErrors: monitor.ignoreTlsErrors,
jsonPath: monitor.jsonPath,
- ...(monitor.type === "port" && { port: monitor.port }),
+ ...((monitor.type === "port" || monitor.type === "game") && {
+ port: monitor.port,
+ }),
+ ...(monitor.type == "game" && {
+ gameId: monitor.gameId,
+ }),
...(monitor.type === "hardware" && {
thresholds: monitor.thresholds,
secret: monitor.secret,
@@ -530,4 +554,5 @@ export {
useDeleteMonitorStats,
useCreateBulkMonitors,
useExportMonitors,
+ useFetchMonitorGames,
};
diff --git a/client/src/Pages/Uptime/Create/index.jsx b/client/src/Pages/Uptime/Create/index.jsx
index 2d918cb9f..f821ea9e4 100644
--- a/client/src/Pages/Uptime/Create/index.jsx
+++ b/client/src/Pages/Uptime/Create/index.jsx
@@ -41,6 +41,7 @@ import {
useUpdateMonitor,
usePauseMonitor,
useFetchMonitorById,
+ useFetchMonitorGames,
} from "../../../Hooks/monitorHooks";
/**
@@ -81,6 +82,7 @@ const UptimeCreate = ({ isClone = false }) => {
const [isOpen, setIsOpen] = useState(false);
const [useAdvancedMatching, setUseAdvancedMatching] = useState(false);
const [updateTrigger, setUpdateTrigger] = useState(false);
+ const [games, setGames] = useState({});
const triggerUpdate = () => {
setUpdateTrigger(!updateTrigger);
};
@@ -89,12 +91,22 @@ const UptimeCreate = ({ isClone = false }) => {
const [notifications, notificationsAreLoading, notificationsError] =
useGetNotificationsByTeamId();
const { determineState, statusColor } = useMonitorUtils();
- // Network
- const [isLoading] = useFetchMonitorById({
+ // Fetch monitor details
+ const [isFetchingMonitor] = useFetchMonitorById({
monitorId,
setMonitor,
updateTrigger: true,
});
+
+ // Fetch games
+ const [isFetchingGames] = useFetchMonitorGames({
+ setGames,
+ triggerUpdate: true,
+ });
+
+ // Combine the loading states
+ const isLoading = isFetchingMonitor || isFetchingGames;
+
const [createMonitor, isCreating] = useCreateMonitor();
const [pauseMonitor, isPausing] = usePauseMonitor({});
const [deleteMonitor, isDeleting] = useDeleteMonitor();
@@ -113,6 +125,12 @@ const UptimeCreate = ({ isClone = false }) => {
{ _id: 4, name: t("time.fourMinutes") },
{ _id: 5, name: t("time.fiveMinutes") },
];
+
+ const GAMELIST = Object.entries(games).map(([key, value]) => ({
+ _id: key,
+ name: value.name,
+ }));
+
const CRUMBS = [
{ name: "uptime", path: "/uptime" },
...(isCreate
@@ -153,6 +171,11 @@ const UptimeCreate = ({ isClone = false }) => {
placeholder: t("monitorType.port.placeholder"),
namePlaceholder: t("monitorType.port.namePlaceholder"),
},
+ game: {
+ label: t("monitorType.game.label"),
+ placeholder: t("monitorType.game.placeholder"),
+ namePlaceholder: t("monitorType.game.namePlaceholder"),
+ },
};
// Handlers
@@ -169,12 +192,14 @@ const UptimeCreate = ({ isClone = false }) => {
: monitor.url,
name: monitor.name || monitor.url.substring(0, 50),
type: monitor.type,
- port: monitor.type === "port" ? monitor.port : undefined,
+ port:
+ monitor.type === "port" || monitor.type === "game" ? monitor.port : undefined,
interval: monitor.interval,
matchMethod: monitor.matchMethod,
expectedValue: monitor.expectedValue,
jsonPath: monitor.jsonPath,
ignoreTlsErrors: monitor.ignoreTlsErrors,
+ gameId: monitor.gameId || undefined,
};
} else {
form = {
@@ -188,8 +213,10 @@ const UptimeCreate = ({ isClone = false }) => {
interval: monitor.interval,
teamId: monitor.teamId,
userId: monitor.userId,
- port: monitor.type === "port" ? monitor.port : undefined,
+ port:
+ monitor.type === "port" || monitor.type === "game" ? monitor.port : undefined,
ignoreTlsErrors: monitor.ignoreTlsErrors,
+ gameId: monitor.gameId || undefined,
};
}
if (!useAdvancedMatching) {
@@ -243,6 +270,11 @@ const UptimeCreate = ({ isClone = false }) => {
setMonitor((prev) => ({ ...prev, [name]: value }));
+ if (name === "type") {
+ setErrors({});
+ return;
+ }
+
const { error } = monitorValidation.validate(
{ type: monitor.type, [name]: value },
{ abortEarly: false }
@@ -250,7 +282,9 @@ const UptimeCreate = ({ isClone = false }) => {
setErrors((prev) => ({
...prev,
- ...(error ? { [name]: error.details[0].message } : { [name]: undefined }),
+ ...(error && error.details[0].path[0] === name
+ ? { [name]: error.details[0].message }
+ : { [name]: undefined }),
}));
};
@@ -486,6 +520,15 @@ const UptimeCreate = ({ isClone = false }) => {
checked={monitor.type === "port"}
onChange={onChange}
/>
+
{errors["type"] ? (
{
onChange={onChange}
error={errors["port"] ? true : false}
helperText={errors["port"]}
- hidden={monitor.type !== "port"}
+ hidden={monitor.type !== "port" && monitor.type !== "game"}
/>
+ {monitor.type === "game" && (
+
+ )}
[
{ value: "ping", label: "Ping" },
{ value: "docker", label: "Docker" },
{ value: "port", label: "Port" },
+ { value: "game", label: "Game" },
];
// These functions were moved inline to ensure translations are applied correctly
diff --git a/client/src/Pages/Uptime/Monitors/index.jsx b/client/src/Pages/Uptime/Monitors/index.jsx
index 4ebac94d3..607092785 100644
--- a/client/src/Pages/Uptime/Monitors/index.jsx
+++ b/client/src/Pages/Uptime/Monitors/index.jsx
@@ -34,7 +34,7 @@ import {
} from "../../../Hooks/monitorHooks";
import { useTranslation } from "react-i18next";
-const TYPES = ["http", "ping", "docker", "port"];
+const TYPES = ["http", "ping", "docker", "port", "game"];
const CreateMonitorButton = ({ shouldRender }) => {
// Utils
const navigate = useNavigate();
diff --git a/client/src/Utils/NetworkService.js b/client/src/Utils/NetworkService.js
index 59e2a0a21..dce0faaf5 100644
--- a/client/src/Utils/NetworkService.js
+++ b/client/src/Utils/NetworkService.js
@@ -74,6 +74,20 @@ class NetworkService {
}
}
+ /**
+ * Fetch the games associated with a monitor
+ *
+ * @async
+ * @returns {Promise} The response from the axios GET request.
+ */
+ async getMonitorGames() {
+ return this.axiosInstance.get(`/monitors/games`, {
+ headers: {
+ "Content-Type": "application/json",
+ },
+ });
+ }
+
/**
*
* ************************************
diff --git a/client/src/Validation/validation.js b/client/src/Validation/validation.js
index 9c6699d4b..de248f87b 100644
--- a/client/src/Validation/validation.js
+++ b/client/src/Validation/validation.js
@@ -184,12 +184,12 @@ const monitorValidation = joi.object({
.min(1)
.max(65535)
.when("type", {
- is: "port",
- then: joi.number().messages({
+ is: joi.valid("port", "game"),
+ then: joi.required().messages({
"number.base": "Port must be a number.",
"number.min": "Port must be at least 1.",
"number.max": "Port must be at most 65535.",
- "any.required": "Port is required for port monitors.",
+ "any.required": "Port is required for port and game monitors.",
}),
otherwise: joi.optional(),
}),
@@ -205,6 +205,14 @@ const monitorValidation = joi.object({
expectedValue: joi.string().allow(null, ""),
jsonPath: joi.string().allow(null, ""),
matchMethod: joi.string().allow(null, ""),
+ gameId: joi.when("type", {
+ is: "game",
+ then: joi.string().required().messages({
+ "string.empty": "Game selection is required for game monitors.",
+ "any.required": "Game selection is required for game monitors.",
+ }),
+ otherwise: joi.string().allow(null, ""),
+ }),
});
const imageValidation = joi.object({
diff --git a/client/src/locales/ar.json b/client/src/locales/ar.json
index 7188c3711..33f245fd5 100644
--- a/client/src/locales/ar.json
+++ b/client/src/locales/ar.json
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
+ "chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "",
diff --git a/client/src/locales/cs.json b/client/src/locales/cs.json
index 00c513203..1fb579270 100644
--- a/client/src/locales/cs.json
+++ b/client/src/locales/cs.json
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
+ "chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "Checkmate",
diff --git a/client/src/locales/de.json b/client/src/locales/de.json
index aa416bd95..4ac77ef29 100644
--- a/client/src/locales/de.json
+++ b/client/src/locales/de.json
@@ -260,6 +260,7 @@
"notifyEmails": "Benachrichtige auch per E-Mail an mehrere Adressen (kommt bald)",
"seperateEmails": "Du kannst mehrere E-Mails mit einem Komma trennen",
"checkFrequency": "Überprüfungsfrequenz",
+ "chooseGame": "",
"matchMethod": "",
"expectedValue": "Erwarteter Wert",
"deleteDialogTitle": "Möchtest du diesen Monitor wirklich löschen?",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "Checkmate",
diff --git a/client/src/locales/en.json b/client/src/locales/en.json
index b287a4229..ecb080357 100644
--- a/client/src/locales/en.json
+++ b/client/src/locales/en.json
@@ -218,6 +218,7 @@
"cancel": "Cancel",
"checkFormError": "Please check the form for errors.",
"checkFrequency": "Check frequency",
+ "chooseGame": "Choose game",
"checkHooks": {
"failureResolveAll": "Failed to resolve all incidents.",
"failureResolveMonitor": "Failed to resolve monitor incidents.",
@@ -629,6 +630,11 @@
"label": "URL to monitor",
"namePlaceholder": "Localhost:5173",
"placeholder": "localhost"
+ },
+ "game": {
+ "label": "URL to monitor",
+ "namePlaceholder": "localhost:5173",
+ "placeholder": "localhost"
}
},
"ms": "ms",
@@ -750,6 +756,8 @@
"pingMonitoringDescription": "Check whether your server is available or not.",
"portMonitoring": "Port monitoring",
"portMonitoringDescription": "Check whether your port is open or not.",
+ "gameServerMonitoring": "Game Server Monitoring",
+ "gameServerMonitoringDescription": "Check whether your game server is running or not",
"portToMonitor": "Port to monitor",
"publicLink": "Public link",
"publicURL": "Public URL",
@@ -1012,7 +1020,8 @@
"docker": "Enter the Docker ID of your container. Docker IDs must be the full 64 char Docker ID. You can run docker inspect to get the full container ID.",
"http": "Enter the URL or IP to monitor (e.g., https://example.com/ or 192.168.1.100) and add a clear display name that appears on the dashboard.",
"ping": "Enter the IP address or hostname to ping (e.g., 192.168.1.100 or example.com) and add a clear display name that appears on the dashboard.",
- "port": "Enter the URL or IP of the server, the port number and a clear display name that appears on the dashboard."
+ "port": "Enter the URL or IP of the server, the port number and a clear display name that appears on the dashboard.",
+ "game": "Enter the IP address or hostname and the port number to ping (e.g., 192.168.1.100 or example.com) and choose game type."
},
"uptimeMonitor": {
"fallback": {
diff --git a/client/src/locales/es.json b/client/src/locales/es.json
index 6ecebadf5..1e2ee3b6c 100644
--- a/client/src/locales/es.json
+++ b/client/src/locales/es.json
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
+ "chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "",
diff --git a/client/src/locales/fi.json b/client/src/locales/fi.json
index 2198e2798..cce87a89f 100644
--- a/client/src/locales/fi.json
+++ b/client/src/locales/fi.json
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
+ "chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "",
diff --git a/client/src/locales/fr.json b/client/src/locales/fr.json
index c6e263c4d..52d5d8c7e 100644
--- a/client/src/locales/fr.json
+++ b/client/src/locales/fr.json
@@ -264,6 +264,7 @@
"notifyEmails": "Envoyer également une notification par email à plusieurs adresses (bientôt).",
"seperateEmails": "Vous pouvez ajouter plusieurs adresses emails en les séparant par une virgule",
"checkFrequency": "Vérifier la fréquence",
+ "chooseGame": "",
"matchMethod": "Méthode de rapprochement",
"expectedValue": "Valeur attendue",
"deleteDialogTitle": "Voulez-vous vraiment supprimer ce moniteur ?",
@@ -441,7 +442,8 @@
"http": "Entrez l'URL ou l'IP du moniteur (par exemple https://exemple.fr ou 192.168.1.100) et ajoutez un nom familier qui apparaîtra sur le tableau de bord.",
"ping": "Entrez l'adresse IP ou le nom d'hôte à tester (par exemple, 192.168.1.100 ou exemple.fr) et ajoutez un nom familier qui apparaîtra sur le tableau de bord.",
"docker": "Entrer l'ID Docker du container. Les identifiants Docker doivent être les 64 caractères de l'ID Docker. Vous pouvez utiliser la commande docker inspect pour avoir l'ID complet.",
- "port": "Entrez l'URL ou l'adresse IP du serveur, le numéro de port et un nom d'affichage familier qui apparaîtra sur le tableau de bord."
+ "port": "Entrez l'URL ou l'adresse IP du serveur, le numéro de port et un nom d'affichage familier qui apparaîtra sur le tableau de bord.",
+ "game": ""
},
"common": {
"appName": "Checkmate",
diff --git a/client/src/locales/ja.json b/client/src/locales/ja.json
index 782fdd9e0..80245d64b 100644
--- a/client/src/locales/ja.json
+++ b/client/src/locales/ja.json
@@ -264,6 +264,7 @@
"notifyEmails": "複数のアドレスにメールでも通知(近日公開)",
"seperateEmails": "複数のメールはカンマで区切ることができます",
"checkFrequency": "チェック頻度",
+ "chooseGame": "",
"matchMethod": "マッチ方法",
"expectedValue": "期待値",
"deleteDialogTitle": "本当にこのモニターを削除しますか?",
@@ -441,7 +442,8 @@
"http": "監視するURLまたはIPを入力(例: https://example.com/ または 192.168.1.100)し、ダッシュボードに表示される明確な表示名を追加。",
"ping": "pingするIPアドレスまたはホスト名を入力(例: 192.168.1.100 または example.com)し、ダッシュボードに表示される明確な表示名を追加。",
"docker": "コンテナのDocker IDを入力。Docker IDは64文字のフルDocker IDである必要があります。docker inspect を実行してフルコンテナIDを取得できます。",
- "port": "サーバーのURLまたはIP、ポート番号、ダッシュボードに表示される明確な表示名を入力。"
+ "port": "サーバーのURLまたはIP、ポート番号、ダッシュボードに表示される明確な表示名を入力。",
+ "game": ""
},
"common": {
"appName": "Checkmate",
diff --git a/client/src/locales/pt-BR.json b/client/src/locales/pt-BR.json
index 2315a723d..4b4bd04a9 100644
--- a/client/src/locales/pt-BR.json
+++ b/client/src/locales/pt-BR.json
@@ -260,6 +260,7 @@
"notifyEmails": "Também notificar por e-mail para vários endereços (em breve)",
"seperateEmails": "Você pode separar vários e-mails com uma vírgula",
"checkFrequency": "Verifique a frequência",
+ "chooseGame": "",
"matchMethod": "Método de correspondência",
"expectedValue": "Valor esperado",
"deleteDialogTitle": "Você realmente deseja excluir este monitor?",
@@ -437,7 +438,8 @@
"http": "Insira a URL ou IP para monitorar (ex. https://exemplo.com.br/ ou 192.168.1.100) e adicione uma descrição que aparecerá na dashboard.",
"ping": "Insira o endereço IP ou nome de domínio para ping (ex. 192.168.1.100 ou exemplo.com.br) e adicione uma descrição que aparecerá na dashboard.",
"docker": "Insira o Docker Id do seu container. Docker Ids devem ser todos os 64 caracteres. Você pode executar docker inspect para descobrir o Id completo.",
- "port": "Insira a URL ou o IP do servidor, aporta e uma descrição que aparecerá na dashboard."
+ "port": "Insira a URL ou o IP do servidor, aporta e uma descrição que aparecerá na dashboard.",
+ "game": ""
},
"common": {
"appName": "Checkmate",
diff --git a/client/src/locales/ru.json b/client/src/locales/ru.json
index 814e94da1..25e1038aa 100644
--- a/client/src/locales/ru.json
+++ b/client/src/locales/ru.json
@@ -260,6 +260,7 @@
"notifyEmails": "Также уведомлять по электронной почте на несколько адресов (скоро)",
"seperateEmails": "Вы можете разделить несколько адресов электронной почты запятой.",
"checkFrequency": "Проверить частоту",
+ "chooseGame": "",
"matchMethod": "Метод сопоставления",
"expectedValue": "Ожидаемое значение",
"deleteDialogTitle": "Вы действительно хотите удалить этот монитор?",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "",
diff --git a/client/src/locales/tr.json b/client/src/locales/tr.json
index e9e2d4e29..9e20242fb 100644
--- a/client/src/locales/tr.json
+++ b/client/src/locales/tr.json
@@ -260,6 +260,7 @@
"notifyEmails": "Ayrıca birden fazla eposta adresine bildirim gönderebilirsiniz (yakında geliyor)",
"seperateEmails": "Birden fazla epostayı virgülle ayırabilirsiniz",
"checkFrequency": "Frekansı denetle",
+ "chooseGame": "Oyun seç",
"matchMethod": "Eşleşme yöntemi",
"expectedValue": "Beklenen değer",
"deleteDialogTitle": "Gerçekten bu monitörü silmek istiyor musunuz?",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "Checkmate",
diff --git a/client/src/locales/uk.json b/client/src/locales/uk.json
index b3b8fc4ce..22b96487c 100644
--- a/client/src/locales/uk.json
+++ b/client/src/locales/uk.json
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
+ "chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -435,7 +436,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "",
diff --git a/client/src/locales/vi.json b/client/src/locales/vi.json
index b3b8fc4ce..22b96487c 100644
--- a/client/src/locales/vi.json
+++ b/client/src/locales/vi.json
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
+ "chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -435,7 +436,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "",
diff --git a/client/src/locales/zh-TW.json b/client/src/locales/zh-TW.json
index 56f1841d4..90a1aa30c 100644
--- a/client/src/locales/zh-TW.json
+++ b/client/src/locales/zh-TW.json
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
+ "chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
- "port": ""
+ "port": "",
+ "game": ""
},
"common": {
"appName": "",
diff --git a/server/openapi.json b/server/openapi.json
old mode 100755
new mode 100644
index 465bf948a..6cc2e7de0
--- a/server/openapi.json
+++ b/server/openapi.json
@@ -1,7 +1,7 @@
{
"openapi": "3.1.0",
"info": {
- "title": "Checkmate",
+ "title": "Checkmate API",
"summary": "Checkmate OpenAPI Specifications",
"description": "Checkmate is an open source monitoring tool used to track the operational status and performance of servers and websites. It regularly checks whether a server/website is accessible and performs optimally, providing real-time alerts and reports on the monitored services' availability, downtime, and response time.",
"contact": {
@@ -13,7 +13,7 @@
"name": "AGPLv3",
"url": "https://github.com/bluewave-labs/checkmate/tree/HEAD/LICENSE"
},
- "version": "1.0"
+ "version": "2.3"
},
"servers": [
{
@@ -22,8 +22,8 @@
"variables": {
"PORT": {
"description": "API Port",
- "enum": ["5000"],
- "default": "5000"
+ "enum": ["52345"],
+ "default": "52345"
},
"API_PATH": {
"description": "API Base Path",
@@ -32,26 +32,10 @@
}
}
},
- {
- "url": "http://localhost/{API_PATH}",
- "description": "Distribution Local Development Server",
- "variables": {
- "API_PATH": {
- "description": "API Base Path",
- "enum": ["api/v1"],
- "default": "api/v1"
- }
- }
- },
{
"url": "https://checkmate-demo.bluewavelabs.ca/{API_PATH}",
"description": "Checkmate Demo Server",
"variables": {
- "PORT": {
- "description": "API Port",
- "enum": ["5000"],
- "default": "5000"
- },
"API_PATH": {
"description": "API Base Path",
"enum": ["api/v1"],
@@ -63,107 +47,81 @@
"tags": [
{
"name": "auth",
- "description": "Authentication"
+ "description": "Authentication and user management"
},
{
"name": "invite",
- "description": "Invite"
+ "description": "Team invitation management"
},
{
"name": "monitors",
- "description": "Monitors"
+ "description": "Monitor management (uptime, page speed, hardware)"
},
{
"name": "checks",
- "description": "Checks"
+ "description": "Monitor check results and history"
},
{
"name": "maintenance-window",
- "description": "Maintenance window"
+ "description": "Scheduled maintenance windows"
},
{
"name": "queue",
- "description": "Queue"
+ "description": "Job queue management"
},
{
"name": "status-page",
- "description": "Status Page"
+ "description": "Public status page management"
+ },
+ {
+ "name": "settings",
+ "description": "Application configuration settings"
+ },
+ {
+ "name": "logs",
+ "description": "Application logs and diagnostics"
+ },
+ {
+ "name": "notifications",
+ "description": "Notification integrations (email, slack, discord, etc.)"
+ },
+ {
+ "name": "diagnostic",
+ "description": "System health and performance diagnostics"
}
],
"paths": {
"/auth/register": {
"post": {
"tags": ["auth"],
- "description": "Register a new user",
+ "summary": "Register new user",
+ "description": "Register a new user account with profile information",
"requestBody": {
+ "required": true,
"content": {
"application/json": {
"schema": {
- "type": "object",
- "required": ["firstName", "lastName", "email", "password", "role", "teamId"],
- "properties": {
- "firstName": {
- "type": "string"
- },
- "lastName": {
- "type": "string"
- },
- "email": {
- "type": "string",
- "format": "email"
- },
- "password": {
- "type": "string",
- "format": "password"
- },
- "profileImage": {
- "type": "file",
- "format": "file"
- },
- "role": {
- "type": "array",
- "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]],
- "default": ["superadmin"]
- },
- "teamId": {
- "type": "string",
- "format": "uuid"
- }
- }
+ "$ref": "#/components/schemas/RegisterRequest"
}
}
}
},
"responses": {
"200": {
- "description": "OK",
+ "description": "User registered successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SuccessResponse"
+ "$ref": "#/components/schemas/AuthResponse"
}
}
}
},
"422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/ValidationError"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
}
}
@@ -171,57 +129,37 @@
"/auth/login": {
"post": {
"tags": ["auth"],
- "description": "Login with credentials",
+ "summary": "User login",
+ "description": "Authenticate user with email and password",
"requestBody": {
+ "required": true,
"content": {
"application/json": {
"schema": {
- "type": "object",
- "required": ["email", "password"],
- "properties": {
- "email": {
- "type": "string",
- "format": "email"
- },
- "password": {
- "type": "string",
- "format": "password"
- }
- }
+ "$ref": "#/components/schemas/LoginRequest"
}
}
}
},
"responses": {
"200": {
- "description": "OK",
+ "description": "Login successful",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SuccessResponse"
+ "$ref": "#/components/schemas/AuthResponse"
}
}
}
},
+ "401": {
+ "$ref": "#/components/responses/Unauthorized"
+ },
"422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/ValidationError"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
}
}
@@ -229,23 +167,13 @@
"/auth/refresh": {
"post": {
"tags": ["auth"],
- "description": "Generates a new auth token if the refresh token is valid.",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "properties": {}
- }
- }
- },
- "required": false
- },
+ "summary": "Refresh access token",
+ "description": "Generate new access token using refresh token",
"parameters": [
{
"name": "x-refresh-token",
"in": "header",
- "description": "Refresh token required to generate a new auth token.",
+ "description": "Refresh token",
"required": true,
"schema": {
"type": "string"
@@ -254,7 +182,7 @@
{
"name": "authorization",
"in": "header",
- "description": "Old access token, used to extract payload).",
+ "description": "Bearer token (old access token)",
"required": true,
"schema": {
"type": "string"
@@ -263,34 +191,20 @@
],
"responses": {
"200": {
- "description": "New access token generated.",
+ "description": "Token refreshed successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/SuccessResponse"
+ "$ref": "#/components/schemas/AuthResponse"
}
}
}
},
"401": {
- "description": "Unauthorized or invalid refresh token.",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Unauthorized"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
}
}
@@ -298,7 +212,1472 @@
"/auth/user/{userId}": {
"put": {
"tags": ["auth"],
- "description": "Change user information",
+ "summary": "Update user profile",
+ "description": "Update user information including profile image",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/UserUpdateRequest"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "delete": {
+ "tags": ["auth"],
+ "summary": "Delete user account",
+ "description": "Permanently delete user account and associated data",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/auth/users": {
+ "get": {
+ "tags": ["auth"],
+ "summary": "Get all users",
+ "description": "Retrieve all users (admin/superadmin only)",
+ "responses": {
+ "200": {
+ "description": "Users retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/auth/users/superadmin": {
+ "get": {
+ "tags": ["auth"],
+ "summary": "Check superadmin exists",
+ "description": "Check if a superadmin account exists in the system",
+ "responses": {
+ "200": {
+ "description": "Check completed",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "properties": {
+ "exists": {
+ "type": "boolean"
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ }
+ }
+ },
+ "/auth/recovery/request": {
+ "post": {
+ "tags": ["auth"],
+ "summary": "Request password reset",
+ "description": "Send password reset email to user",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": ["email"],
+ "properties": {
+ "email": {
+ "type": "string",
+ "format": "email",
+ "description": "User email address"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "description": "User not found"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ }
+ }
+ },
+ "/auth/recovery/validate": {
+ "post": {
+ "tags": ["auth"],
+ "summary": "Validate recovery token",
+ "description": "Validate password reset token",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": ["recoveryToken"],
+ "properties": {
+ "recoveryToken": {
+ "type": "string",
+ "description": "Password reset token"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "400": {
+ "description": "Invalid or expired token"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ }
+ }
+ },
+ "/auth/recovery/reset": {
+ "post": {
+ "tags": ["auth"],
+ "summary": "Reset password",
+ "description": "Reset user password with recovery token",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": ["recoveryToken", "password"],
+ "properties": {
+ "recoveryToken": {
+ "type": "string",
+ "description": "Password reset token"
+ },
+ "password": {
+ "type": "string",
+ "format": "password",
+ "description": "New password"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "400": {
+ "description": "Invalid token or password requirements not met"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ }
+ }
+ },
+ "/monitors": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get all monitors",
+ "description": "Retrieve all monitors for the authenticated user",
+ "responses": {
+ "200": {
+ "description": "Monitors retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Monitor"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "post": {
+ "tags": ["monitors"],
+ "summary": "Create monitor",
+ "description": "Create a new monitoring endpoint",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateMonitorRequest"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Monitor created successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "$ref": "#/components/schemas/Monitor"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "delete": {
+ "tags": ["monitors"],
+ "summary": "Delete all monitors",
+ "description": "Delete all monitors (superadmin only)",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/team": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get monitors by team",
+ "description": "Get monitors filtered by team with optional parameters",
+ "parameters": [
+ {
+ "name": "status",
+ "in": "query",
+ "description": "Filter by monitor status (up/down)",
+ "schema": {
+ "type": "boolean"
+ }
+ },
+ {
+ "name": "type",
+ "in": "query",
+ "description": "Filter by monitor type",
+ "schema": {
+ "type": "string",
+ "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port"]
+ }
+ },
+ {
+ "name": "page",
+ "in": "query",
+ "description": "Page number for pagination",
+ "schema": {
+ "type": "integer",
+ "minimum": 1,
+ "default": 1
+ }
+ },
+ {
+ "name": "rowsPerPage",
+ "in": "query",
+ "description": "Number of monitors per page",
+ "schema": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 100,
+ "default": 10
+ }
+ },
+ {
+ "name": "filter",
+ "in": "query",
+ "description": "Search filter value",
+ "schema": {
+ "type": "string"
+ }
+ },
+ {
+ "name": "field",
+ "in": "query",
+ "description": "Field to filter on",
+ "schema": {
+ "type": "string",
+ "enum": ["name", "url", "description"]
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Team monitors retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "properties": {
+ "monitors": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Monitor"
+ }
+ },
+ "totalCount": {
+ "type": "integer"
+ }
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/team/with-checks": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get monitors with recent checks",
+ "description": "Get team monitors with their most recent check results",
+ "parameters": [
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "Number of recent checks to include per monitor",
+ "schema": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 50,
+ "default": 10
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Monitors with checks retrieved successfully"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/team/summary": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get monitors summary",
+ "description": "Get team monitors with summary statistics",
+ "parameters": [
+ {
+ "name": "type",
+ "in": "query",
+ "description": "Filter by monitor type",
+ "schema": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port"]
+ }
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Monitor summary retrieved successfully"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/{monitorId}": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get monitor by ID",
+ "description": "Retrieve a specific monitor by its ID",
+ "parameters": [
+ {
+ "name": "monitorId",
+ "in": "path",
+ "required": true,
+ "description": "Monitor ID",
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Monitor retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "$ref": "#/components/schemas/Monitor"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "put": {
+ "tags": ["monitors"],
+ "summary": "Update monitor",
+ "description": "Update an existing monitor",
+ "parameters": [
+ {
+ "name": "monitorId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/UpdateMonitorRequest"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "delete": {
+ "tags": ["monitors"],
+ "summary": "Delete monitor",
+ "description": "Delete a specific monitor",
+ "parameters": [
+ {
+ "name": "monitorId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/uptime/details/{monitorId}": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get uptime details",
+ "description": "Get detailed uptime statistics for a monitor",
+ "parameters": [
+ {
+ "name": "monitorId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Uptime details retrieved successfully"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/hardware/details/{monitorId}": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get hardware monitoring details",
+ "description": "Get hardware performance metrics for a monitor",
+ "parameters": [
+ {
+ "name": "monitorId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Hardware details retrieved successfully"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/pause/{monitorId}": {
+ "post": {
+ "tags": ["monitors"],
+ "summary": "Pause/unpause monitor",
+ "description": "Toggle monitor active status",
+ "parameters": [
+ {
+ "name": "monitorId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/stats/{monitorId}": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get monitor statistics",
+ "description": "Get comprehensive statistics for a monitor",
+ "parameters": [
+ {
+ "name": "monitorId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Monitor statistics retrieved successfully"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/certificate/{monitorId}": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get SSL certificate info",
+ "description": "Get SSL certificate information for a monitor",
+ "parameters": [
+ {
+ "name": "monitorId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Certificate information retrieved successfully"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/demo": {
+ "post": {
+ "tags": ["monitors"],
+ "summary": "Add demo monitors",
+ "description": "Add preconfigured demo monitors for testing",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/export": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Export monitors to CSV",
+ "description": "Export all monitors to CSV format",
+ "responses": {
+ "200": {
+ "description": "CSV file generated successfully",
+ "content": {
+ "text/csv": {
+ "schema": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/bulk": {
+ "post": {
+ "tags": ["monitors"],
+ "summary": "Bulk import monitors",
+ "description": "Import monitors from CSV file",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "csvFile": {
+ "type": "string",
+ "format": "binary",
+ "description": "CSV file containing monitor data"
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "description": "Monitors imported successfully"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/test-email": {
+ "post": {
+ "tags": ["monitors"],
+ "summary": "Send test email",
+ "description": "Send a test email notification",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/settings": {
+ "get": {
+ "tags": ["settings"],
+ "summary": "Get application settings",
+ "description": "Retrieve current application configuration",
+ "responses": {
+ "200": {
+ "description": "Settings retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "$ref": "#/components/schemas/AppSettings"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "put": {
+ "tags": ["settings"],
+ "summary": "Update application settings",
+ "description": "Update application configuration (admin/superadmin only)",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/AppSettings"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/settings/test-email": {
+ "post": {
+ "tags": ["settings"],
+ "summary": "Send test email",
+ "description": "Send test email to verify email configuration",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/notifications": {
+ "get": {
+ "tags": ["notifications"],
+ "summary": "Get team notifications",
+ "description": "Get all notification configurations for the team",
+ "responses": {
+ "200": {
+ "description": "Notifications retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "$ref": "#/components/schemas/Notification"
+ }
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "post": {
+ "tags": ["notifications"],
+ "summary": "Create notification",
+ "description": "Create a new notification integration",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateNotificationRequest"
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Notification created successfully"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/notifications/{id}": {
+ "get": {
+ "tags": ["notifications"],
+ "summary": "Get notification by ID",
+ "description": "Retrieve a specific notification configuration",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Notification retrieved successfully"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "put": {
+ "tags": ["notifications"],
+ "summary": "Update notification",
+ "description": "Update an existing notification configuration",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/CreateNotificationRequest"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "delete": {
+ "tags": ["notifications"],
+ "summary": "Delete notification",
+ "description": "Delete a notification configuration",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/notifications/test": {
+ "post": {
+ "tags": ["notifications"],
+ "summary": "Test notification",
+ "description": "Send a test notification to verify configuration",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/notifications/test/all": {
+ "post": {
+ "tags": ["notifications"],
+ "summary": "Test all notifications",
+ "description": "Send test notifications to all configured integrations",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/logs": {
+ "get": {
+ "tags": ["logs"],
+ "summary": "Get application logs",
+ "description": "Retrieve application logs (admin/superadmin only)",
+ "parameters": [
+ {
+ "name": "level",
+ "in": "query",
+ "description": "Log level filter",
+ "schema": {
+ "type": "string",
+ "enum": ["error", "warn", "info", "debug"]
+ }
+ },
+ {
+ "name": "limit",
+ "in": "query",
+ "description": "Number of log entries to return",
+ "schema": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 1000,
+ "default": 100
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "Logs retrieved successfully"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/diagnostic/system": {
+ "get": {
+ "tags": ["diagnostic"],
+ "summary": "Get system diagnostics",
+ "description": "Get system health and performance metrics (admin/superadmin only)",
+ "responses": {
+ "200": {
+ "description": "System diagnostics retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "$ref": "#/components/schemas/SystemDiagnostics"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/auth/user": {
+ "put": {
+ "tags": ["auth"],
+ "summary": "Update current user profile",
+ "description": "Update authenticated user's profile information",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "$ref": "#/components/schemas/UserUpdateRequest"
+ }
+ }
+ }
+ },
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "delete": {
+ "tags": ["auth"],
+ "summary": "Delete current user account",
+ "description": "Delete authenticated user's account and associated data",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/auth/users/{userId}": {
+ "get": {
+ "tags": ["auth"],
+ "summary": "Get user by ID",
+ "description": "Get a specific user by ID (superadmin only)",
+ "parameters": [
+ {
+ "name": "userId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "User retrieved successfully",
+ "content": {
+ "application/json": {
+ "schema": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ ]
+ }
+ }
+ }
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ },
+ "put": {
+ "tags": ["auth"],
+ "summary": "Update user by ID",
+ "description": "Update a specific user by ID (superadmin only)",
"parameters": [
{
"name": "userId",
@@ -321,85 +1700,19 @@
},
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
},
"422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/ValidationError"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "delete": {
- "tags": ["auth"],
- "description": "Delete user",
- "parameters": [
- {
- "name": "userId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -409,97 +1722,63 @@
]
}
},
- "/auth/users/superadmin": {
- "get": {
- "tags": ["auth"],
- "description": "Checks to see if an admin account exists",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/auth/users": {
- "get": {
- "tags": ["auth"],
- "description": "Get all users",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/auth/recovery/request": {
+ "/invite": {
"post": {
- "tags": ["auth"],
- "description": "Request a recovery token",
+ "tags": ["invite"],
+ "summary": "Create invite token",
+ "description": "Create a new invitation token",
"requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": ["email", "role"],
+ "properties": {
+ "email": {
+ "type": "string",
+ "format": "email"
+ },
+ "role": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["user", "admin"]
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ "responses": {
+ "201": {
+ "description": "Invite token created successfully"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/invite/send": {
+ "post": {
+ "tags": ["invite"],
+ "summary": "Send invitation email",
+ "description": "Send invitation email to user",
+ "requestBody": {
+ "required": true,
"content": {
"application/json": {
"schema": {
@@ -517,199 +1796,13 @@
},
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/auth/recovery/validate": {
- "post": {
- "tags": ["auth"],
- "description": "Validate recovery token",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["recoveryToken"],
- "properties": {
- "recoveryToken": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/auth/recovery/reset": {
- "post": {
- "tags": ["auth"],
- "description": "Password reset",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["recoveryToken", "password"],
- "properties": {
- "recoveryToken": {
- "type": "string"
- },
- "password": {
- "type": "string"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- }
- }
- },
- "/invite": {
- "post": {
- "tags": ["invite"],
- "description": "Request an invitation",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "type": "object",
- "required": ["email", "role"],
- "properties": {
- "email": {
- "type": "string"
- },
- "role": {
- "type": "array"
- }
- }
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -722,8 +1815,10 @@
"/invite/verify": {
"post": {
"tags": ["invite"],
- "description": "Request an invitation",
+ "summary": "Verify invitation token",
+ "description": "Verify an invitation token",
"requestBody": {
+ "required": true,
"content": {
"application/json": {
"schema": {
@@ -740,851 +1835,22 @@
},
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors": {
- "get": {
- "tags": ["monitors"],
- "description": "Get all monitors",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "post": {
- "tags": ["monitors"],
- "description": "Create a new monitor",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateMonitorBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "delete": {
- "tags": ["monitors"],
- "description": "Delete all monitors",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/uptime": {
- "get": {
- "tags": ["monitors"],
- "description": "Get all monitors with uptime stats for 1, 7, 30, and 90 days",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/resolution/url": {
- "get": {
- "tags": ["monitors"],
- "description": "Check DNS resolution for a given URL",
- "parameters": [
- {
- "name": "monitorURL",
- "in": "query",
- "required": true,
- "schema": {
- "type": "string",
- "example": "https://example.com"
- },
- "description": "The URL to check DNS resolution for"
- }
- ],
- "responses": {
- "200": {
- "description": "URL resolved successfully",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
"400": {
- "description": "DNS resolution failed",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "description": "Invalid or expired token"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/{monitorId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitor by id",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "put": {
- "tags": ["monitors"],
- "description": "Update monitor by id",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/UpdateMonitorBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "delete": {
- "tags": ["monitors"],
- "description": "Delete monitor by id",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/stats/{monitorId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitor stats",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/certificate/{monitorId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitor certificate",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/team/summary/{teamId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitors and summary by teamId",
- "parameters": [
- {
- "name": "teamId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "type",
- "in": "query",
- "required": false,
- "schema": {
- "type": "array",
- "enum": ["http", "ping", "pagespeed"]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/team/{teamId}": {
- "get": {
- "tags": ["monitors"],
- "description": "Get monitors by teamId",
- "parameters": [
- {
- "name": "teamId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "status",
- "description": "Status of monitor, true for up, false for down",
- "in": "query",
- "required": false,
- "schema": {
- "type": "boolean"
- }
- },
- {
- "name": "checkOrder",
- "description": "Order of checks",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "enum": ["asc", "desc"]
- }
- },
- {
- "name": "limit",
- "description": "Number of checks to return with monitor",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer"
- }
- },
- {
- "name": "type",
- "description": "Type of monitor",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "enum": ["http", "ping", "pagespeed"]
- }
- },
- {
- "name": "page",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer"
- }
- },
- {
- "name": "rowsPerPage",
- "in": "query",
- "required": false,
- "schema": {
- "type": "integer"
- }
- },
- {
- "name": "filter",
- "description": "Value to filter by",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "field",
- "description": "Field to filter on",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string"
- }
- },
- {
- "name": "order",
- "description": "Sort order of results",
- "in": "query",
- "required": false,
- "schema": {
- "type": "string",
- "enum": ["http", "ping", "pagespeed"]
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/pause/{monitorId}": {
- "post": {
- "tags": ["monitors"],
- "description": "Pause monitor",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/monitors/demo": {
- "post": {
- "tags": ["monitors"],
- "description": "Create a demo monitor",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateMonitorBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
+ }
}
},
"/checks/{monitorId}": {
"get": {
"tags": ["checks"],
- "description": "Get all checks for a monitor",
+ "summary": "Get checks by monitor",
+ "description": "Get all checks for a specific monitor",
"parameters": [
{
"name": "monitorId",
@@ -1597,94 +1863,13 @@
],
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "description": "Checks retrieved successfully"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "404": {
+ "$ref": "#/components/responses/NotFound"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- },
- "post": {
- "tags": ["checks"],
- "description": "Create a new check",
- "parameters": [
- {
- "name": "monitorId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateCheckBody"
- }
- }
- }
- },
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -1695,7 +1880,8 @@
},
"delete": {
"tags": ["checks"],
- "description": "Delete all checks for a monitor",
+ "summary": "Delete checks by monitor",
+ "description": "Delete all checks for a specific monitor",
"parameters": [
{
"name": "monitorId",
@@ -1708,34 +1894,13 @@
],
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "404": {
+ "$ref": "#/components/responses/NotFound"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -1745,50 +1910,17 @@
]
}
},
- "/checks/team/{teamId}": {
+ "/checks/team": {
"get": {
"tags": ["checks"],
- "description": "Get all checks for a team",
- "parameters": [
- {
- "name": "teamId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
+ "summary": "Get checks by team",
+ "description": "Get all checks for team monitors",
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "description": "Team checks retrieved successfully"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -1799,47 +1931,37 @@
},
"delete": {
"tags": ["checks"],
- "description": "Delete all checks for a team",
- "parameters": [
- {
- "name": "teamId",
- "in": "path",
- "required": true,
- "schema": {
- "type": "string"
- }
- }
- ],
+ "summary": "Delete team checks",
+ "description": "Delete all checks for team (admin/superadmin only)",
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/checks/team/summary": {
+ "get": {
+ "tags": ["checks"],
+ "summary": "Get team checks summary",
+ "description": "Get summary statistics for team checks",
+ "responses": {
+ "200": {
+ "description": "Team checks summary retrieved successfully"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -1852,48 +1974,151 @@
"/checks/team/ttl": {
"put": {
"tags": ["checks"],
- "description": "Update check TTL",
+ "summary": "Update checks TTL",
+ "description": "Update time-to-live for checks (admin/superadmin only)",
"requestBody": {
+ "required": true,
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/UpdateCheckTTLBody"
+ "type": "object",
+ "required": ["ttl"],
+ "properties": {
+ "ttl": {
+ "type": "integer",
+ "minimum": 1,
+ "description": "Time to live in days"
+ }
+ }
}
}
}
},
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
},
"422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/ValidationError"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/checks/check/{checkId}": {
+ "put": {
+ "tags": ["checks"],
+ "summary": "Acknowledge check",
+ "description": "Acknowledge a specific check",
+ "parameters": [
+ {
+ "name": "checkId",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/maintenance-window": {
+ "post": {
+ "tags": ["maintenance-window"],
+ "summary": "Create maintenance window",
+ "description": "Create a new maintenance window",
+ "requestBody": {
+ "required": true,
+ "content": {
+ "application/json": {
+ "schema": {
+ "type": "object",
+ "required": ["name", "startTime", "endTime"],
+ "properties": {
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 500
+ },
+ "startTime": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "endTime": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "monitors": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
}
}
}
}
},
+ "responses": {
+ "201": {
+ "description": "Maintenance window created successfully"
+ },
+ "422": {
+ "$ref": "#/components/responses/ValidationError"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/maintenance-window/team": {
+ "get": {
+ "tags": ["maintenance-window"],
+ "summary": "Get team maintenance windows",
+ "description": "Get all maintenance windows for the team",
+ "responses": {
+ "200": {
+ "description": "Maintenance windows retrieved successfully"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
"security": [
{
"bearerAuth": []
@@ -1904,7 +2129,8 @@
"/maintenance-window/monitor/{monitorId}": {
"get": {
"tags": ["maintenance-window"],
- "description": "Get maintenance window for monitor",
+ "summary": "Get maintenance windows by monitor",
+ "description": "Get all maintenance windows for a specific monitor",
"parameters": [
{
"name": "monitorId",
@@ -1917,35 +2143,47 @@
],
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "description": "Monitor maintenance windows retrieved successfully"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "404": {
+ "$ref": "#/components/responses/NotFound"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/maintenance-window/{id}": {
+ "get": {
+ "tags": ["maintenance-window"],
+ "summary": "Get maintenance window by ID",
+ "description": "Get a specific maintenance window",
+ "parameters": [
+ {
+ "name": "id",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
}
}
+ ],
+ "responses": {
+ "200": {
+ "description": "Maintenance window retrieved successfully"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
},
"security": [
{
@@ -1953,12 +2191,13 @@
}
]
},
- "post": {
+ "put": {
"tags": ["maintenance-window"],
- "description": "Create maintenance window for monitor",
+ "summary": "Update maintenance window",
+ "description": "Update an existing maintenance window",
"parameters": [
{
- "name": "monitorId",
+ "name": "id",
"in": "path",
"required": true,
"schema": {
@@ -1967,44 +2206,51 @@
}
],
"requestBody": {
+ "required": true,
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/CreateMaintenanceWindowBody"
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 500
+ },
+ "startTime": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "endTime": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "monitors": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ }
+ }
}
}
}
},
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
},
"422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/ValidationError"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -2012,15 +2258,14 @@
"bearerAuth": []
}
]
- }
- },
- "/maintenance-window/user/{userId}": {
- "get": {
+ },
+ "delete": {
"tags": ["maintenance-window"],
- "description": "Get maintenance window for user",
+ "summary": "Delete maintenance window",
+ "description": "Delete a specific maintenance window",
"parameters": [
{
- "name": "userId",
+ "name": "id",
"in": "path",
"required": true,
"schema": {
@@ -2030,34 +2275,13 @@
],
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "404": {
+ "$ref": "#/components/responses/NotFound"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -2067,81 +2291,238 @@
]
}
},
- "/queue/jobs": {
+ "/queue/health": {
"get": {
"tags": ["queue"],
- "description": "Get all jobs in queue",
+ "summary": "Check queue health",
+ "description": "Check the health status of the job queue (admin/superadmin only)",
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "description": "Queue health status retrieved successfully"
},
- "422": {
- "description": "Unprocessable Content",
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/queue/all-metrics": {
+ "get": {
+ "tags": ["queue"],
+ "summary": "Get all queue metrics",
+ "description": "Get comprehensive queue metrics (admin/superadmin only)",
+ "responses": {
+ "200": {
+ "description": "All queue metrics retrieved successfully"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/queue/flush": {
+ "post": {
+ "tags": ["queue"],
+ "summary": "Flush queue",
+ "description": "Clear all jobs from the queue (admin/superadmin only)",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "403": {
+ "$ref": "#/components/responses/Forbidden"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/monitors/games": {
+ "get": {
+ "tags": ["monitors"],
+ "summary": "Get game server list",
+ "description": "Get available game servers for monitoring",
+ "responses": {
+ "200": {
+ "description": "Game servers retrieved successfully",
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/ErrorResponse"
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "id": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ }
+ ]
}
}
}
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
+ "security": [
+ {
+ "bearerAuth": []
+ }
+ ]
+ }
+ },
+ "/status-page": {
+ "get": {
+ "tags": ["status-page"],
+ "summary": "Get status page",
+ "description": "Get default status page information",
+ "responses": {
+ "200": {
+ "$ref": "#/components/responses/Success"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ }
+ },
+ "post": {
+ "tags": ["status-page"],
+ "summary": "Create status page",
+ "description": "Create a new status page with optional logo upload",
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "logo": {
+ "type": "string",
+ "format": "binary",
+ "description": "Logo file for the status page"
+ },
+ "title": {
+ "type": "string",
+ "description": "Status page title"
+ },
+ "description": {
+ "type": "string",
+ "description": "Status page description"
+ },
+ "url": {
+ "type": "string",
+ "description": "Custom URL for the status page"
+ }
}
}
}
}
},
+ "responses": {
+ "201": {
+ "$ref": "#/components/responses/Success"
+ },
+ "400": {
+ "$ref": "#/components/responses/BadRequest"
+ },
+ "401": {
+ "$ref": "#/components/responses/Unauthorized"
+ },
+ "500": {
+ "$ref": "#/components/responses/InternalServerError"
+ }
+ },
"security": [
{
"bearerAuth": []
}
]
},
- "post": {
- "tags": ["queue"],
- "description": "Create a new job. Useful for testing scaling workers",
+ "put": {
+ "tags": ["status-page"],
+ "summary": "Update status page",
+ "description": "Update an existing status page with optional logo upload",
+ "requestBody": {
+ "content": {
+ "multipart/form-data": {
+ "schema": {
+ "type": "object",
+ "properties": {
+ "logo": {
+ "type": "string",
+ "format": "binary",
+ "description": "Logo file for the status page"
+ },
+ "title": {
+ "type": "string",
+ "description": "Status page title"
+ },
+ "description": {
+ "type": "string",
+ "description": "Status page description"
+ },
+ "url": {
+ "type": "string",
+ "description": "Custom URL for the status page"
+ }
+ }
+ }
+ }
+ }
+ },
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "400": {
+ "$ref": "#/components/responses/BadRequest"
+ },
+ "401": {
+ "$ref": "#/components/responses/Unauthorized"
+ },
+ "404": {
+ "$ref": "#/components/responses/NotFound"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -2151,83 +2532,20 @@
]
}
},
- "/queue/metrics": {
+ "/status-page/team": {
"get": {
- "tags": ["queue"],
- "description": "Get queue metrics",
+ "tags": ["status-page"],
+ "summary": "Get status pages by team",
+ "description": "Get all status pages for the authenticated user's team",
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "401": {
+ "$ref": "#/components/responses/Unauthorized"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- }
- },
- "security": [
- {
- "bearerAuth": []
- }
- ]
- }
- },
- "/queue/obliterate": {
- "post": {
- "tags": ["queue"],
- "description": "Obliterate job queue",
- "responses": {
- "200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
- },
- "500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -2240,106 +2558,58 @@
"/status-page/{url}": {
"get": {
"tags": ["status-page"],
- "description": "Get a status page by URL",
+ "summary": "Get status page by URL",
+ "description": "Get a specific status page by its custom URL",
"parameters": [
{
"name": "url",
"in": "path",
"required": true,
- "schema": { "type": "string" }
+ "schema": {
+ "type": "string"
+ },
+ "description": "Custom URL of the status page"
}
],
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": { "$ref": "#/components/schemas/SuccessResponse" }
- }
- }
- },
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
"404": {
- "description": "Not Found",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/NotFound"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": { "$ref": "#/components/schemas/ErrorResponse" }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
}
},
- "post": {
+ "delete": {
"tags": ["status-page"],
- "description": "Create a status page",
- "requestBody": {
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/CreateStatusPageBody"
- }
- }
+ "summary": "Delete status page",
+ "description": "Delete a status page by its custom URL",
+ "parameters": [
+ {
+ "name": "url",
+ "in": "path",
+ "required": true,
+ "schema": {
+ "type": "string"
+ },
+ "description": "Custom URL of the status page (supports wildcards)"
}
- },
+ ],
"responses": {
"200": {
- "description": "OK",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/SuccessResponse"
- }
- }
- }
+ "$ref": "#/components/responses/Success"
},
- "422": {
- "description": "Unprocessable Content",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "401": {
+ "$ref": "#/components/responses/Unauthorized"
},
- "400": {
- "description": "Duplicate URL",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "404": {
+ "$ref": "#/components/responses/NotFound"
},
"500": {
- "description": "Internal Server Error",
- "content": {
- "application/json": {
- "schema": {
- "$ref": "#/components/schemas/ErrorResponse"
- }
- }
- }
+ "$ref": "#/components/responses/InternalServerError"
}
},
"security": [
@@ -2355,72 +2625,597 @@
"bearerAuth": {
"type": "http",
"scheme": "bearer",
- "bearerFormat": "JWT"
+ "bearerFormat": "JWT",
+ "description": "JWT token obtained from login endpoint"
+ }
+ },
+ "responses": {
+ "Success": {
+ "description": "Operation successful",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/SuccessResponse"
+ }
+ }
+ }
+ },
+ "NotFound": {
+ "description": "Resource not found",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ },
+ "ValidationError": {
+ "description": "Validation failed",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ },
+ "Unauthorized": {
+ "description": "Authentication required or token invalid",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ },
+ "Forbidden": {
+ "description": "Insufficient permissions",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
+ },
+ "InternalServerError": {
+ "description": "Internal server error",
+ "content": {
+ "application/json": {
+ "schema": {
+ "$ref": "#/components/schemas/ErrorResponse"
+ }
+ }
+ }
}
},
"schemas": {
- "ErrorResponse": {
- "type": "object",
- "properties": {
- "success": {
- "type": "boolean",
- "default": false
- },
- "msg": {
- "type": "string"
- }
- }
- },
"SuccessResponse": {
"type": "object",
+ "required": ["success", "msg"],
"properties": {
"success": {
"type": "boolean",
- "default": true
+ "example": true
},
"msg": {
- "type": "string"
+ "type": "string",
+ "example": "Operation completed successfully"
},
"data": {
- "type": "object"
+ "type": "object",
+ "description": "Response payload (varies by endpoint)"
}
}
},
- "UserUpdateRequest": {
+ "ErrorResponse": {
"type": "object",
- "required": ["firstName", "lastName", "email", "password", "role", "teamId"],
+ "required": ["success", "msg"],
+ "properties": {
+ "success": {
+ "type": "boolean",
+ "example": false
+ },
+ "msg": {
+ "type": "string",
+ "example": "An error occurred"
+ },
+ "details": {
+ "type": "object",
+ "description": "Additional error details"
+ }
+ }
+ },
+ "RegisterRequest": {
+ "type": "object",
+ "required": ["firstName", "lastName", "email", "password"],
"properties": {
"firstName": {
- "type": "string"
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 50,
+ "example": "John"
},
"lastName": {
- "type": "string"
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 50,
+ "example": "Doe"
+ },
+ "email": {
+ "type": "string",
+ "format": "email",
+ "example": "john@example.com"
},
"password": {
"type": "string",
- "format": "password"
- },
- "newPassword": {
- "type": "string",
- "format": "password"
+ "minLength": 8,
+ "format": "password",
+ "example": "SecurePass123!"
},
"profileImage": {
- "type": "file",
- "format": "file"
+ "type": "string",
+ "format": "binary",
+ "description": "Optional profile image file"
},
"role": {
"type": "array",
- "enum": [["user"], ["admin"], ["superadmin"], ["Demo"]],
- "default": ["superadmin"]
- },
- "deleteProfileImage": {
- "type": "boolean"
+ "items": {
+ "type": "string",
+ "enum": ["user", "admin", "superadmin", "Demo"]
+ },
+ "default": ["user"]
}
}
},
- "CreateMonitorBody": {
+ "LoginRequest": {
+ "type": "object",
+ "required": ["email", "password"],
+ "properties": {
+ "email": {
+ "type": "string",
+ "format": "email",
+ "example": "john@example.com"
+ },
+ "password": {
+ "type": "string",
+ "format": "password",
+ "example": "SecurePass123!"
+ }
+ }
+ },
+ "AuthResponse": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/SuccessResponse"
+ },
+ {
+ "type": "object",
+ "properties": {
+ "data": {
+ "type": "object",
+ "properties": {
+ "token": {
+ "type": "string",
+ "description": "JWT access token"
+ },
+ "user": {
+ "$ref": "#/components/schemas/User"
+ }
+ }
+ }
+ }
+ }
+ ]
+ },
+ "UserUpdateRequest": {
+ "type": "object",
+ "properties": {
+ "firstName": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 50
+ },
+ "lastName": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 50
+ },
+ "password": {
+ "type": "string",
+ "format": "password",
+ "description": "Current password for verification"
+ },
+ "newPassword": {
+ "type": "string",
+ "minLength": 8,
+ "format": "password",
+ "description": "New password (if changing)"
+ },
+ "profileImage": {
+ "type": "string",
+ "format": "binary",
+ "description": "New profile image file"
+ },
+ "deleteProfileImage": {
+ "type": "boolean",
+ "description": "Flag to delete current profile image"
+ }
+ }
+ },
+ "User": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": "64f123a456b789012c345def"
+ },
+ "firstName": {
+ "type": "string",
+ "example": "John"
+ },
+ "lastName": {
+ "type": "string",
+ "example": "Doe"
+ },
+ "email": {
+ "type": "string",
+ "format": "email",
+ "example": "john@example.com"
+ },
+ "role": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "enum": ["user", "admin", "superadmin", "Demo"]
+ }
+ },
+ "profileImage": {
+ "type": "string",
+ "description": "URL or path to profile image"
+ },
+ "isActive": {
+ "type": "boolean"
+ },
+ "teamId": {
+ "type": "string"
+ },
+ "createdAt": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updatedAt": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "CreateMonitorRequest": {
+ "type": "object",
+ "required": ["name", "description", "type", "url", "interval"],
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "example": "My Website Monitor"
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 500,
+ "example": "Monitors the main website homepage"
+ },
+ "type": {
+ "type": "string",
+ "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port"],
+ "example": "http"
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://example.com"
+ },
+ "interval": {
+ "type": "integer",
+ "minimum": 30,
+ "maximum": 3600,
+ "example": 300,
+ "description": "Check interval in seconds"
+ },
+ "isActive": {
+ "type": "boolean",
+ "default": true
+ },
+ "notifications": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ },
+ "description": "Array of notification IDs to associate with this monitor"
+ },
+ "httpOptions": {
+ "type": "object",
+ "properties": {
+ "method": {
+ "type": "string",
+ "enum": ["GET", "POST", "PUT", "DELETE", "HEAD"],
+ "default": "GET"
+ },
+ "headers": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "body": {
+ "type": "string",
+ "description": "Request body for POST/PUT requests"
+ },
+ "timeout": {
+ "type": "integer",
+ "minimum": 1000,
+ "maximum": 30000,
+ "default": 5000,
+ "description": "Request timeout in milliseconds"
+ }
+ }
+ },
+ "assertions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": ["status-code", "response-time", "body-contains", "header-contains"]
+ },
+ "comparison": {
+ "type": "string",
+ "enum": ["equals", "not-equals", "greater-than", "less-than", "contains", "not-contains"]
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "UpdateMonitorRequest": {
+ "type": "object",
+ "properties": {
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100
+ },
+ "description": {
+ "type": "string",
+ "maxLength": 500
+ },
+ "interval": {
+ "type": "integer",
+ "minimum": 30,
+ "maximum": 3600
+ },
+ "isActive": {
+ "type": "boolean"
+ },
+ "notifications": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "httpOptions": {
+ "type": "object",
+ "properties": {
+ "method": {
+ "type": "string",
+ "enum": ["GET", "POST", "PUT", "DELETE", "HEAD"]
+ },
+ "headers": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ },
+ "body": {
+ "type": "string"
+ },
+ "timeout": {
+ "type": "integer",
+ "minimum": 1000,
+ "maximum": 30000
+ }
+ }
+ },
+ "assertions": {
+ "type": "array",
+ "items": {
+ "type": "object",
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": ["status-code", "response-time", "body-contains", "header-contains"]
+ },
+ "comparison": {
+ "type": "string",
+ "enum": ["equals", "not-equals", "greater-than", "less-than", "contains", "not-contains"]
+ },
+ "value": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ }
+ },
+ "Monitor": {
+ "type": "object",
+ "properties": {
+ "_id": {
+ "type": "string",
+ "example": "64f123a456b789012c345def"
+ },
+ "userId": {
+ "type": "string"
+ },
+ "teamId": {
+ "type": "string"
+ },
+ "name": {
+ "type": "string",
+ "example": "My Website Monitor"
+ },
+ "description": {
+ "type": "string",
+ "example": "Monitors the main website homepage"
+ },
+ "type": {
+ "type": "string",
+ "enum": ["http", "ping", "pagespeed", "hardware", "docker", "port"]
+ },
+ "url": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://example.com"
+ },
+ "interval": {
+ "type": "integer",
+ "example": 300
+ },
+ "isActive": {
+ "type": "boolean",
+ "example": true
+ },
+ "status": {
+ "type": "boolean",
+ "description": "Current monitor status (up/down)"
+ },
+ "lastChecked": {
+ "type": "string",
+ "format": "date-time",
+ "description": "Timestamp of last check"
+ },
+ "notifications": {
+ "type": "array",
+ "items": {
+ "type": "string"
+ }
+ },
+ "httpOptions": {
+ "type": "object",
+ "properties": {
+ "method": {
+ "type": "string"
+ },
+ "headers": {
+ "type": "object"
+ },
+ "body": {
+ "type": "string"
+ },
+ "timeout": {
+ "type": "integer"
+ }
+ }
+ },
+ "assertions": {
+ "type": "array",
+ "items": {
+ "type": "object"
+ }
+ },
+ "createdAt": {
+ "type": "string",
+ "format": "date-time"
+ },
+ "updatedAt": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "CreateNotificationRequest": {
+ "type": "object",
+ "required": ["type", "name"],
+ "properties": {
+ "type": {
+ "type": "string",
+ "enum": ["email", "webhook", "slack", "discord", "telegram", "zapier"],
+ "example": "email"
+ },
+ "name": {
+ "type": "string",
+ "minLength": 1,
+ "maxLength": 100,
+ "example": "Admin Email Notifications"
+ },
+ "config": {
+ "type": "object",
+ "description": "Configuration specific to notification type",
+ "oneOf": [
+ {
+ "title": "Email Configuration",
+ "properties": {
+ "to": {
+ "type": "array",
+ "items": {
+ "type": "string",
+ "format": "email"
+ }
+ }
+ }
+ },
+ {
+ "title": "Webhook Configuration",
+ "properties": {
+ "url": {
+ "type": "string",
+ "format": "uri"
+ },
+ "headers": {
+ "type": "object",
+ "additionalProperties": {
+ "type": "string"
+ }
+ }
+ }
+ },
+ {
+ "title": "Slack Configuration",
+ "properties": {
+ "webhookUrl": {
+ "type": "string",
+ "format": "uri"
+ },
+ "channel": {
+ "type": "string"
+ }
+ }
+ },
+ {
+ "title": "Discord Configuration",
+ "properties": {
+ "webhookUrl": {
+ "type": "string",
+ "format": "uri"
+ }
+ }
+ }
+ ]
+ },
+ "isActive": {
+ "type": "boolean",
+ "default": true
+ }
+ }
+ },
+ "Notification": {
"type": "object",
- "required": ["userId", "teamId", "name", "description", "type", "url"],
"properties": {
"_id": {
"type": "string"
@@ -2431,122 +3226,153 @@
"teamId": {
"type": "string"
},
+ "type": {
+ "type": "string",
+ "enum": ["email", "webhook", "slack", "discord", "telegram", "zapier"]
+ },
"name": {
"type": "string"
},
- "description": {
- "type": "string"
- },
- "type": {
- "type": "string",
- "enum": ["http", "ping", "pagespeed"]
- },
- "url": {
- "type": "string"
+ "config": {
+ "type": "object",
+ "description": "Type-specific configuration"
},
"isActive": {
"type": "boolean"
},
- "interval": {
- "type": "integer"
+ "createdAt": {
+ "type": "string",
+ "format": "date-time"
},
- "notifications": {
- "type": "array",
- "items": {
- "type": "string"
+ "updatedAt": {
+ "type": "string",
+ "format": "date-time"
+ }
+ }
+ },
+ "AppSettings": {
+ "type": "object",
+ "properties": {
+ "appName": {
+ "type": "string",
+ "example": "Checkmate"
+ },
+ "appUrl": {
+ "type": "string",
+ "format": "uri",
+ "example": "https://checkmate.example.com"
+ },
+ "logLevel": {
+ "type": "string",
+ "enum": ["error", "warn", "info", "debug"],
+ "default": "info"
+ },
+ "emailConfig": {
+ "type": "object",
+ "properties": {
+ "host": {
+ "type": "string"
+ },
+ "port": {
+ "type": "integer"
+ },
+ "secure": {
+ "type": "boolean"
+ },
+ "user": {
+ "type": "string"
+ },
+ "pass": {
+ "type": "string",
+ "format": "password"
+ },
+ "from": {
+ "type": "string",
+ "format": "email"
+ }
}
+ },
+ "webhookRetries": {
+ "type": "integer",
+ "minimum": 0,
+ "maximum": 10,
+ "default": 3
+ },
+ "checksRetention": {
+ "type": "integer",
+ "minimum": 1,
+ "maximum": 365,
+ "default": 90,
+ "description": "Days to retain check results"
}
}
},
- "UpdateMonitorBody": {
+ "SystemDiagnostics": {
"type": "object",
"properties": {
- "name": {
- "type": "string"
+ "uptime": {
+ "type": "number",
+ "description": "System uptime in seconds"
},
- "description": {
- "type": "string"
- },
- "interval": {
- "type": "integer"
- },
- "notifications": {
- "type": "array",
- "items": {
- "type": "string"
+ "memory": {
+ "type": "object",
+ "properties": {
+ "total": {
+ "type": "number",
+ "description": "Total memory in bytes"
+ },
+ "used": {
+ "type": "number",
+ "description": "Used memory in bytes"
+ },
+ "free": {
+ "type": "number",
+ "description": "Free memory in bytes"
+ }
}
- }
- }
- },
- "CreateCheckBody": {
- "type": "object",
- "required": ["monitorId", "status", "responseTime", "statusCode", "message"],
- "properties": {
- "monitorId": {
- "type": "string"
},
- "status": {
- "type": "boolean"
+ "cpu": {
+ "type": "object",
+ "properties": {
+ "usage": {
+ "type": "number",
+ "description": "CPU usage percentage"
+ },
+ "cores": {
+ "type": "integer",
+ "description": "Number of CPU cores"
+ }
+ }
},
- "responseTime": {
- "type": "integer"
+ "database": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string",
+ "enum": ["connected", "disconnected", "error"]
+ },
+ "responseTime": {
+ "type": "number",
+ "description": "Database response time in milliseconds"
+ }
+ }
},
- "statusCode": {
- "type": "integer"
- },
- "message": {
- "type": "string"
- }
- }
- },
- "UpdateCheckTTLBody": {
- "type": "object",
- "required": ["ttl"],
- "properties": {
- "ttl": {
- "type": "integer"
- }
- }
- },
- "CreateMaintenanceWindowBody": {
- "type": "object",
- "required": ["userId", "active", "oneTime", "start", "end"],
- "properties": {
- "userId": {
- "type": "string"
- },
- "active": {
- "type": "boolean"
- },
- "oneTime": {
- "type": "boolean"
- },
- "start": {
- "type": "string",
- "format": "date-time"
- },
- "end": {
- "type": "string",
- "format": "date-time"
- },
- "expiry": {
- "type": "string",
- "format": "date-time"
- }
- }
- },
- "CreateStatusPageBody": {
- "type": "object",
- "required": ["companyName", "url", "timezone", "color", "theme", "monitors"],
- "properties": {
- "companyName": { "type": "string" },
- "url": { "type": "string" },
- "timezone": { "type": "string" },
- "color": { "type": "string" },
- "theme": { "type": "string" },
- "monitors": {
- "type": "array",
- "items": { "type": "string" }
+ "queue": {
+ "type": "object",
+ "properties": {
+ "active": {
+ "type": "integer"
+ },
+ "waiting": {
+ "type": "integer"
+ },
+ "completed": {
+ "type": "integer"
+ },
+ "failed": {
+ "type": "integer"
+ }
+ }
}
}
}
diff --git a/server/package-lock.json b/server/package-lock.json
index a41a50518..3084a5361 100644
--- a/server/package-lock.json
+++ b/server/package-lock.json
@@ -14,16 +14,20 @@
"bcryptjs": "3.0.2",
"bullmq": "5.41.2",
"compression": "1.8.1",
+ "cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dockerode": "4.0.6",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-rate-limit": "8.0.1",
+ "gamedig": "^5.3.1",
"handlebars": "^4.7.8",
"helmet": "^8.0.0",
"ioredis": "^5.4.2",
+ "isomorphic-dompurify": "^2.26.0",
"jmespath": "^0.16.0",
"joi": "^17.13.1",
+ "jsdom": "^26.1.0",
"jsonwebtoken": "9.0.2",
"mailersend": "^2.2.0",
"mjml": "^5.0.0-alpha.4",
@@ -52,6 +56,19 @@
"sinon": "19.0.2"
}
},
+ "node_modules/@asamuzakjp/css-color": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz",
+ "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==",
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/css-calc": "^2.1.3",
+ "@csstools/css-color-parser": "^3.0.9",
+ "@csstools/css-parser-algorithms": "^3.0.4",
+ "@csstools/css-tokenizer": "^3.0.3",
+ "lru-cache": "^10.4.3"
+ }
+ },
"node_modules/@babel/code-frame": {
"version": "7.27.1",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz",
@@ -109,6 +126,116 @@
"node": ">=0.1.90"
}
},
+ "node_modules/@csstools/color-helpers": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.2.tgz",
+ "integrity": "sha512-JqWH1vsgdGcw2RR6VliXXdA0/59LttzlU8UlRT/iUUsEeWfYq8I+K0yhihEUTTHLRm1EXvpsCx3083EU15ecsA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT-0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@csstools/css-calc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz",
+ "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-color-parser": {
+ "version": "3.0.10",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.10.tgz",
+ "integrity": "sha512-TiJ5Ajr6WRd1r8HSiwJvZBiJOqtH86aHpUjq5aEKWHiII2Qfjqd/HCWKPOW8EP4vcspXbHnXrwIDlu5savQipg==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@csstools/color-helpers": "^5.0.2",
+ "@csstools/css-calc": "^2.1.4"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-parser-algorithms": "^3.0.5",
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-parser-algorithms": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz",
+ "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@csstools/css-tokenizer": "^3.0.4"
+ }
+ },
+ "node_modules/@csstools/css-tokenizer": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz",
+ "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/csstools"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/csstools"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@dabh/diagnostics": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.3.tgz",
@@ -1031,6 +1158,18 @@
"integrity": "sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==",
"license": "BSD-3-Clause"
},
+ "node_modules/@sindresorhus/is": {
+ "version": "5.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-5.6.0.tgz",
+ "integrity": "sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
"node_modules/@sinonjs/commons": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz",
@@ -1080,15 +1219,16 @@
"dev": true,
"license": "(Unlicense OR Apache-2.0)"
},
- "node_modules/@trysound/sax": {
- "version": "0.2.0",
- "resolved": "https://registry.npmjs.org/@trysound/sax/-/sax-0.2.0.tgz",
- "integrity": "sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==",
- "license": "ISC",
- "optional": true,
- "peer": true,
+ "node_modules/@szmarczak/http-timer": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-5.0.1.tgz",
+ "integrity": "sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==",
+ "license": "MIT",
+ "dependencies": {
+ "defer-to-connect": "^2.0.1"
+ },
"engines": {
- "node": ">=10.13.0"
+ "node": ">=14.16"
}
},
"node_modules/@types/estree": {
@@ -1098,6 +1238,12 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz",
+ "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==",
+ "license": "MIT"
+ },
"node_modules/@types/istanbul-lib-coverage": {
"version": "2.0.6",
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
@@ -1127,6 +1273,13 @@
"integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==",
"license": "MIT"
},
+ "node_modules/@types/trusted-types": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
+ "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==",
+ "license": "MIT",
+ "optional": true
+ },
"node_modules/@types/webidl-conversions": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz",
@@ -1249,6 +1402,12 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
+ "node_modules/any-promise": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
+ "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
+ "license": "MIT"
+ },
"node_modules/anymatch": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
@@ -1328,6 +1487,39 @@
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"license": "MIT"
},
+ "node_modules/barse": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/barse/-/barse-0.4.3.tgz",
+ "integrity": "sha512-UEpvriJqAn8zuVinYICuKoPttZy3XxXEoqX/V2uYAL4zzJRuNzCK3+20nAu3YUIa2U7G53kf90wfBIp9/A+Odw==",
+ "license": "MIT",
+ "dependencies": {
+ "readable-stream": "~1.0.2"
+ }
+ },
+ "node_modules/barse/node_modules/isarray": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
+ "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==",
+ "license": "MIT"
+ },
+ "node_modules/barse/node_modules/readable-stream": {
+ "version": "1.0.34",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz",
+ "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==",
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.1",
+ "isarray": "0.0.1",
+ "string_decoder": "~0.10.x"
+ }
+ },
+ "node_modules/barse/node_modules/string_decoder": {
+ "version": "0.10.31",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
+ "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==",
+ "license": "MIT"
+ },
"node_modules/base64-js": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
@@ -1619,6 +1811,33 @@
}
}
},
+ "node_modules/cacheable-lookup": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz",
+ "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "10.2.14",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-10.2.14.tgz",
+ "integrity": "sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-cache-semantics": "^4.0.2",
+ "get-stream": "^6.0.1",
+ "http-cache-semantics": "^4.1.1",
+ "keyv": "^4.5.3",
+ "mimic-response": "^4.0.0",
+ "normalize-url": "^8.0.0",
+ "responselike": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ }
+ },
"node_modules/call-bind-apply-helpers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
@@ -2144,6 +2363,28 @@
"node": ">= 0.6"
}
},
+ "node_modules/cookie-parser": {
+ "version": "1.4.7",
+ "resolved": "https://registry.npmjs.org/cookie-parser/-/cookie-parser-1.4.7.tgz",
+ "integrity": "sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==",
+ "license": "MIT",
+ "dependencies": {
+ "cookie": "0.7.2",
+ "cookie-signature": "1.0.6"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/cookie-parser/node_modules/cookie": {
+ "version": "0.7.2",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
+ "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
"node_modules/cookie-signature": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
@@ -2263,21 +2504,6 @@
"url": "https://github.com/sponsors/fb55"
}
},
- "node_modules/css-tree": {
- "version": "2.3.1",
- "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
- "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "mdn-data": "2.0.30",
- "source-map-js": "^1.0.1"
- },
- "engines": {
- "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
- }
- },
"node_modules/css-what": {
"version": "6.2.2",
"resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz",
@@ -2411,6 +2637,19 @@
"integrity": "sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==",
"license": "CC0-1.0"
},
+ "node_modules/cssstyle": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz",
+ "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@asamuzakjp/css-color": "^3.2.0",
+ "rrweb-cssom": "^0.8.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/data-uri-to-buffer": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz",
@@ -2420,6 +2659,19 @@
"node": ">= 12"
}
},
+ "node_modules/data-urls": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz",
+ "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==",
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/date.js": {
"version": "0.3.3",
"resolved": "https://registry.npmjs.org/date.js/-/date.js-0.3.3.tgz",
@@ -2474,6 +2726,39 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/decimal.js": {
+ "version": "10.6.0",
+ "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz",
+ "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==",
+ "license": "MIT"
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/deep-eql": {
"version": "5.0.2",
"resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz",
@@ -2491,6 +2776,15 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
@@ -2640,6 +2934,15 @@
"url": "https://github.com/fb55/domhandler?sponsor=1"
}
},
+ "node_modules/dompurify": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.6.tgz",
+ "integrity": "sha512-/2GogDQlohXPZe6D6NOgQvXLPSYBqIWMnZ8zzOhn09REE4eyAzb+Hed3jhoM9OkuaJ8P6ZGTTVWQKAi8ieIzfQ==",
+ "license": "(MPL-2.0 OR Apache-2.0)",
+ "optionalDependencies": {
+ "@types/trusted-types": "^2.0.7"
+ }
+ },
"node_modules/domutils": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz",
@@ -2707,6 +3010,14 @@
"integrity": "sha512-k4McmnB2091YIsdCgkS0fMVMPOJgxl93ltFzaryXqwip1AaxeDqKCGLxkXODDA5Ab/D+tV5EL5+aTx76RvLRxw==",
"license": "ISC"
},
+ "node_modules/emitter-component": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/emitter-component/-/emitter-component-1.1.2.tgz",
+ "integrity": "sha512-QdXO3nXOzZB4pAjM0n6ZE+R9/+kPpECA/XSELIcc54NeYVnBqIk+4DFiBgK+8QbV3mdvTG6nedl7dTYgO+5wDw==",
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
@@ -3114,6 +3425,13 @@
"node": ">= 0.6"
}
},
+ "node_modules/event-to-promise": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/event-to-promise/-/event-to-promise-0.7.0.tgz",
+ "integrity": "sha512-VOBBfyaADfe378ZzG0tgkzmsvzUyeU5arehrFzNRt5yaASUDshgctTwSrPI17ocAwR3+YftsxRClHF+GBKFByQ==",
+ "deprecated": "Use promise-toolbox/fromEvent instead",
+ "license": "MIT"
+ },
"node_modules/express": {
"version": "4.21.2",
"resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
@@ -3220,6 +3538,24 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/fast-xml-parser": {
+ "version": "5.2.3",
+ "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.2.3.tgz",
+ "integrity": "sha512-OdCYfRqfpuLUFonTNjvd30rCBZUneHpSQkCqfaeWQ9qrKcl6XlWeDBNVwGb+INAIxRshuN2jF+BE0L6gbBO2mw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "strnum": "^2.1.0"
+ },
+ "bin": {
+ "fxparser": "src/cli/cli.js"
+ }
+ },
"node_modules/fecha": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz",
@@ -3413,6 +3749,15 @@
"node": ">= 6"
}
},
+ "node_modules/form-data-encoder": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-2.1.4.tgz",
+ "integrity": "sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 14.17"
+ }
+ },
"node_modules/formdata-polyfill": {
"version": "4.0.10",
"resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz",
@@ -3472,6 +3817,41 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/gamedig": {
+ "version": "5.3.1",
+ "resolved": "https://registry.npmjs.org/gamedig/-/gamedig-5.3.1.tgz",
+ "integrity": "sha512-RM/eCR8bAKEX+5dA9sxg4D6oQjS5t0MPsIxGquerBLSIu0f1hAVaDGC58Lp1srYwF6A1C2wYY2p9PzOrPTKf4Q==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-xml-parser": "5.2.3",
+ "gbxremote": "0.2.1",
+ "got": "13.0.0",
+ "iconv-lite": "0.6.3",
+ "long": "5.3.2",
+ "minimist": "1.2.8",
+ "seek-bzip": "2.0.0",
+ "telnet-client": "2.2.5",
+ "varint": "6.0.0"
+ },
+ "bin": {
+ "gamedig": "bin/gamedig.js"
+ },
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/gamedig/node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/gaxios": {
"version": "6.7.1",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz",
@@ -3488,6 +3868,21 @@
"node": ">=14"
}
},
+ "node_modules/gbxremote": {
+ "version": "0.2.1",
+ "resolved": "https://registry.npmjs.org/gbxremote/-/gbxremote-0.2.1.tgz",
+ "integrity": "sha512-SMehu6Y6ndq2Qgp9VxAb8Np3f+UUD+RWoW2SAMaxzGS96rWXyr4T1GGkecO0HHtxeH1m7pEh4FJWB8a/6aM2XQ==",
+ "dependencies": {
+ "any-promise": "^1.1.0",
+ "barse": "~0.4.2",
+ "event-to-promise": "^0.7.0",
+ "string-to-stream": "^1.0.1",
+ "xmlrpc": "^1.3.1"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
"node_modules/get-caller-file": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
@@ -3534,6 +3929,18 @@
"node": ">= 0.4"
}
},
+ "node_modules/get-stream": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+ "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/glob": {
"version": "10.4.5",
"resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
@@ -3616,6 +4023,31 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/got": {
+ "version": "13.0.0",
+ "resolved": "https://registry.npmjs.org/got/-/got-13.0.0.tgz",
+ "integrity": "sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==",
+ "license": "MIT",
+ "dependencies": {
+ "@sindresorhus/is": "^5.2.0",
+ "@szmarczak/http-timer": "^5.0.1",
+ "cacheable-lookup": "^7.0.0",
+ "cacheable-request": "^10.2.8",
+ "decompress-response": "^6.0.0",
+ "form-data-encoder": "^2.1.2",
+ "get-stream": "^6.0.1",
+ "http2-wrapper": "^2.1.10",
+ "lowercase-keys": "^3.0.0",
+ "p-cancelable": "^3.0.0",
+ "responselike": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
"node_modules/handlebars": {
"version": "4.7.8",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz",
@@ -3705,6 +4137,18 @@
"node": ">=18.0.0"
}
},
+ "node_modules/html-encoding-sniffer": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz",
+ "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "whatwg-encoding": "^3.1.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/html-escaper": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
@@ -3777,6 +4221,12 @@
"entities": "^4.4.0"
}
},
+ "node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+ "license": "BSD-2-Clause"
+ },
"node_modules/http-errors": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
@@ -3793,6 +4243,32 @@
"node": ">= 0.8"
}
},
+ "node_modules/http-proxy-agent": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz",
+ "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==",
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "^7.1.0",
+ "debug": "^4.3.4"
+ },
+ "engines": {
+ "node": ">= 14"
+ }
+ },
+ "node_modules/http2-wrapper": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz",
+ "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==",
+ "license": "MIT",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
"node_modules/https-proxy-agent": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
@@ -4011,6 +4487,12 @@
"node": ">=8"
}
},
+ "node_modules/is-potential-custom-element-name": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz",
+ "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==",
+ "license": "MIT"
+ },
"node_modules/is-stream": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
@@ -4048,6 +4530,19 @@
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
"license": "ISC"
},
+ "node_modules/isomorphic-dompurify": {
+ "version": "2.26.0",
+ "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-2.26.0.tgz",
+ "integrity": "sha512-nZmoK4wKdzPs5USq4JHBiimjdKSVAOm2T1KyDoadtMPNXYHxiENd19ou4iU/V4juFM6LVgYQnpxCYmxqNP4Obw==",
+ "license": "MIT",
+ "dependencies": {
+ "dompurify": "^3.2.6",
+ "jsdom": "^26.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/isomorphic-unfetch": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-4.0.2.tgz",
@@ -4170,11 +4665,49 @@
"js-yaml": "bin/js-yaml.js"
}
},
+ "node_modules/jsdom": {
+ "version": "26.1.0",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.1.0.tgz",
+ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==",
+ "license": "MIT",
+ "dependencies": {
+ "cssstyle": "^4.2.1",
+ "data-urls": "^5.0.0",
+ "decimal.js": "^10.5.0",
+ "html-encoding-sniffer": "^4.0.0",
+ "http-proxy-agent": "^7.0.2",
+ "https-proxy-agent": "^7.0.6",
+ "is-potential-custom-element-name": "^1.0.1",
+ "nwsapi": "^2.2.16",
+ "parse5": "^7.2.1",
+ "rrweb-cssom": "^0.8.0",
+ "saxes": "^6.0.0",
+ "symbol-tree": "^3.2.4",
+ "tough-cookie": "^5.1.1",
+ "w3c-xmlserializer": "^5.0.0",
+ "webidl-conversions": "^7.0.0",
+ "whatwg-encoding": "^3.1.1",
+ "whatwg-mimetype": "^4.0.0",
+ "whatwg-url": "^14.1.1",
+ "ws": "^8.18.0",
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "canvas": "^3.0.0"
+ },
+ "peerDependenciesMeta": {
+ "canvas": {
+ "optional": true
+ }
+ }
+ },
"node_modules/json-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
"integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true,
"license": "MIT"
},
"node_modules/json-parse-even-better-errors": {
@@ -4336,7 +4869,6 @@
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
"integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"json-buffer": "3.0.1"
@@ -4536,6 +5068,18 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/lowercase-keys": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz",
+ "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/lru-cache": {
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
@@ -4587,14 +5131,6 @@
"node": ">= 0.4"
}
},
- "node_modules/mdn-data": {
- "version": "2.0.30",
- "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
- "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
- "license": "CC0-1.0",
- "optional": true,
- "peer": true
- },
"node_modules/media-typer": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
@@ -4676,6 +5212,18 @@
"node": ">= 0.6"
}
},
+ "node_modules/mimic-response": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz",
+ "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==",
+ "license": "MIT",
+ "engines": {
+ "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/minimatch": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -5498,6 +6046,12 @@
"integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==",
"license": "MIT"
},
+ "node_modules/net": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/net/-/net-1.0.2.tgz",
+ "integrity": "sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==",
+ "license": "MIT"
+ },
"node_modules/nise": {
"version": "6.1.1",
"resolved": "https://registry.npmjs.org/nise/-/nise-6.1.1.tgz",
@@ -5681,6 +6235,18 @@
"node": ">=0.10.0"
}
},
+ "node_modules/normalize-url": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.0.2.tgz",
+ "integrity": "sha512-Ee/R3SyN4BuynXcnTaekmaVdbDAEiNrHqjQIA37mHU8G9pf7aaAD4ZX3XjBLo6rsdcxA/gtkcNYZLt30ACgynw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/nth-check": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz",
@@ -5699,6 +6265,12 @@
"integrity": "sha512-pv/ue2Odr7IfYOO0byC1KgBI10wo5YDauLhxY6/saNzAdAs0r1SotGCPzzCLNPL0xtrAwWRialLu23AAu9xO1g==",
"license": "MIT"
},
+ "node_modules/nwsapi": {
+ "version": "2.2.21",
+ "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.21.tgz",
+ "integrity": "sha512-o6nIY3qwiSXl7/LuOU0Dmuctd34Yay0yeuZRLFmDPrrdHpXKFndPj3hM+YEPVHYC5fx2otBx4Ilc/gyYSAUaIA==",
+ "license": "MIT"
+ },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@@ -5777,6 +6349,15 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/p-cancelable": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-3.0.0.tgz",
+ "integrity": "sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.20"
+ }
+ },
"node_modules/p-limit": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -6733,6 +7314,18 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/rambda": {
"version": "7.5.0",
"resolved": "https://registry.npmjs.org/rambda/-/rambda-7.5.0.tgz",
@@ -6830,6 +7423,12 @@
"node": ">=0.10.0"
}
},
+ "node_modules/resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
+ "license": "MIT"
+ },
"node_modules/resolve-from": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -6839,6 +7438,27 @@
"node": ">=4"
}
},
+ "node_modules/responselike": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-3.0.0.tgz",
+ "integrity": "sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==",
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=14.16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/rrweb-cssom": {
+ "version": "0.8.0",
+ "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz",
+ "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==",
+ "license": "MIT"
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -6880,6 +7500,40 @@
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"license": "ISC"
},
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/seek-bzip": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-2.0.0.tgz",
+ "integrity": "sha512-SMguiTnYrhpLdk3PwfzHeotrcwi8bNV4iemL9tx9poR/yeaMYwB9VzR1w7b57DuWpuqR8n6oZboi0hj3AxZxQg==",
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^6.0.0"
+ },
+ "bin": {
+ "seek-bunzip": "bin/seek-bunzip",
+ "seek-table": "bin/seek-bzip-table"
+ }
+ },
+ "node_modules/seek-bzip/node_modules/commander": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz",
+ "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/semver": {
"version": "7.7.2",
"resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
@@ -7267,6 +7921,15 @@
"node": ">= 0.8"
}
},
+ "node_modules/stream": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/stream/-/stream-0.0.2.tgz",
+ "integrity": "sha512-gCq3NDI2P35B2n6t76YJuOp7d6cN/C7Rt0577l91wllh0sY9ZBuw9KaSGqH/b0hzn3CWWJbpbW0W0WvQ1H/Q7g==",
+ "license": "MIT",
+ "dependencies": {
+ "emitter-component": "^1.1.1"
+ }
+ },
"node_modules/streamsearch": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
@@ -7284,6 +7947,46 @@
"safe-buffer": "~5.2.0"
}
},
+ "node_modules/string-to-stream": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string-to-stream/-/string-to-stream-1.1.1.tgz",
+ "integrity": "sha512-QySF2+3Rwq0SdO3s7BAp4x+c3qsClpPQ6abAmb0DGViiSBAkT5kL6JT2iyzEVP+T1SmzHrQD1TwlP9QAHCc+Sw==",
+ "license": "MIT",
+ "dependencies": {
+ "inherits": "^2.0.1",
+ "readable-stream": "^2.1.0"
+ }
+ },
+ "node_modules/string-to-stream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "license": "MIT",
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/string-to-stream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "license": "MIT"
+ },
+ "node_modules/string-to-stream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "license": "MIT",
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
@@ -7393,6 +8096,18 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
+ "node_modules/strnum": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.1.tgz",
+ "integrity": "sha512-7ZvoFTiCnGxBtDqJ//Cu6fWtZtc7Y3x+QOirG15wztbdngGSkht27o2pyGWrVy0b4WAy3jbKmnoK6g5VlVNUUw==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/NaturalIntelligence"
+ }
+ ],
+ "license": "MIT"
+ },
"node_modules/stylehacks": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/stylehacks/-/stylehacks-7.0.6.tgz",
@@ -7516,44 +8231,6 @@
"node": ">=8"
}
},
- "node_modules/svgo": {
- "version": "3.3.2",
- "resolved": "https://registry.npmjs.org/svgo/-/svgo-3.3.2.tgz",
- "integrity": "sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@trysound/sax": "0.2.0",
- "commander": "^7.2.0",
- "css-select": "^5.1.0",
- "css-tree": "^2.3.1",
- "css-what": "^6.1.0",
- "csso": "^5.0.5",
- "picocolors": "^1.0.0"
- },
- "bin": {
- "svgo": "bin/svgo"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/svgo"
- }
- },
- "node_modules/svgo/node_modules/commander": {
- "version": "7.2.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz",
- "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==",
- "license": "MIT",
- "optional": true,
- "peer": true,
- "engines": {
- "node": ">= 10"
- }
- },
"node_modules/swagger-ui-dist": {
"version": "5.27.0",
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-5.27.0.tgz",
@@ -7578,6 +8255,12 @@
"express": ">=4.0.0 || >=5.0.0-beta"
}
},
+ "node_modules/symbol-tree": {
+ "version": "3.2.4",
+ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",
+ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==",
+ "license": "MIT"
+ },
"node_modules/tar-fs": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.3.tgz",
@@ -7606,6 +8289,20 @@
"node": ">=6"
}
},
+ "node_modules/telnet-client": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/telnet-client/-/telnet-client-2.2.5.tgz",
+ "integrity": "sha512-X5xEkmKHgpCpngnH7QnkFX87UyBErauHsjzlCGVp87MbhnmHoaAeacuALGfqovHh3MXAfrpPs+g30PgyBpy8Jw==",
+ "license": "MIT",
+ "dependencies": {
+ "net": "^1.0.2",
+ "stream": "^0.0.2"
+ },
+ "funding": {
+ "type": "paypal",
+ "url": "https://paypal.me/kozjak"
+ }
+ },
"node_modules/test-exclude": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz",
@@ -7653,6 +8350,24 @@
"integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==",
"license": "MIT"
},
+ "node_modules/tldts": {
+ "version": "6.1.86",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
+ "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==",
+ "license": "MIT",
+ "dependencies": {
+ "tldts-core": "^6.1.86"
+ },
+ "bin": {
+ "tldts": "bin/cli.js"
+ }
+ },
+ "node_modules/tldts-core": {
+ "version": "6.1.86",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz",
+ "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==",
+ "license": "MIT"
+ },
"node_modules/to-regex-range": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
@@ -7684,6 +8399,18 @@
"nodetouch": "bin/nodetouch.js"
}
},
+ "node_modules/tough-cookie": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz",
+ "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "tldts": "^6.1.32"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/tr46": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz",
@@ -7917,6 +8644,12 @@
"node": ">=10"
}
},
+ "node_modules/varint": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
+ "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
+ "license": "MIT"
+ },
"node_modules/vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -7926,6 +8659,18 @@
"node": ">= 0.8"
}
},
+ "node_modules/w3c-xmlserializer": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz",
+ "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==",
+ "license": "MIT",
+ "dependencies": {
+ "xml-name-validator": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/web-resource-inliner": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/web-resource-inliner/-/web-resource-inliner-7.0.0.tgz",
@@ -8283,6 +9028,71 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
+ "node_modules/ws": {
+ "version": "8.18.3",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz",
+ "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ },
+ "peerDependencies": {
+ "bufferutil": "^4.0.1",
+ "utf-8-validate": ">=5.0.2"
+ },
+ "peerDependenciesMeta": {
+ "bufferutil": {
+ "optional": true
+ },
+ "utf-8-validate": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/xml-name-validator": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz",
+ "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/xmlbuilder": {
+ "version": "8.2.2",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-8.2.2.tgz",
+ "integrity": "sha512-eKRAFz04jghooy8muekqzo8uCSVNeyRedbuJrp0fovbLIi7wlsYtdUn3vBAAPq2Y3/0xMz2WMEUQ8yhVVO9Stw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/xmlchars": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
+ "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==",
+ "license": "MIT"
+ },
+ "node_modules/xmlrpc": {
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/xmlrpc/-/xmlrpc-1.3.2.tgz",
+ "integrity": "sha512-jQf5gbrP6wvzN71fgkcPPkF4bF/Wyovd7Xdff8d6/ihxYmgETQYSuTc+Hl+tsh/jmgPLro/Aro48LMFlIyEKKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "sax": "1.2.x",
+ "xmlbuilder": "8.2.x"
+ },
+ "engines": {
+ "node": ">=0.8",
+ "npm": ">=1.0.0"
+ }
+ },
+ "node_modules/xmlrpc/node_modules/sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "license": "ISC"
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/server/package.json b/server/package.json
index 4768fb45f..929fab58a 100755
--- a/server/package.json
+++ b/server/package.json
@@ -21,16 +21,20 @@
"bcryptjs": "3.0.2",
"bullmq": "5.41.2",
"compression": "1.8.1",
+ "cookie-parser": "^1.4.7",
"cors": "^2.8.5",
"dockerode": "4.0.6",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-rate-limit": "8.0.1",
+ "gamedig": "^5.3.1",
"handlebars": "^4.7.8",
"helmet": "^8.0.0",
"ioredis": "^5.4.2",
+ "isomorphic-dompurify": "^2.26.0",
"jmespath": "^0.16.0",
"joi": "^17.13.1",
+ "jsdom": "^26.1.0",
"jsonwebtoken": "9.0.2",
"mailersend": "^2.2.0",
"mjml": "^5.0.0-alpha.4",
diff --git a/server/src/app.js b/server/src/app.js
index 6ba1d0d05..9cabda6b6 100644
--- a/server/src/app.js
+++ b/server/src/app.js
@@ -4,11 +4,13 @@ import { responseHandler } from "./middleware/responseHandler.js";
import cors from "cors";
import helmet from "helmet";
import compression from "compression";
+import cookieParser from "cookie-parser";
import languageMiddleware from "./middleware/languageMiddleware.js";
import swaggerUi from "swagger-ui-express";
import { handleErrors } from "./middleware/handleErrors.js";
import { setupRoutes } from "./config/routes.js";
import { generalApiLimiter } from "./middleware/rateLimiter.js";
+import { sanitizeBody, sanitizeQuery } from "./middleware/sanitization.js";
export const createApp = ({ services, controllers, envSettings, frontendPath, openApiSpec }) => {
const allowedOrigin = envSettings.clientHost;
@@ -30,6 +32,11 @@ export const createApp = ({ services, controllers, envSettings, frontendPath, op
})
);
app.use(express.json());
+ app.use(cookieParser());
+
+ app.use(sanitizeBody());
+ app.use(sanitizeQuery());
+
app.use(
helmet({
hsts: false,
@@ -37,6 +44,9 @@ export const createApp = ({ services, controllers, envSettings, frontendPath, op
useDefaults: true,
directives: {
upgradeInsecureRequests: null,
+ "script-src": ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
+ "object-src": ["'none'"],
+ "base-uri": ["'self'"],
},
},
})
diff --git a/server/src/config/services.js b/server/src/config/services.js
index 4fb9424cc..73220128f 100644
--- a/server/src/config/services.js
+++ b/server/src/config/services.js
@@ -31,6 +31,7 @@ const { compile } = pkg;
import mjml2html from "mjml";
import jwt from "jsonwebtoken";
import crypto from "crypto";
+import { games } from "gamedig";
import { fileURLToPath } from "url";
import { ObjectId } from "mongodb";
@@ -201,6 +202,7 @@ export const initializeServices = async ({ logger, envSettings, settingsService
papaparse,
logger,
errorService,
+ games,
});
const services = {
diff --git a/server/src/controllers/monitorController.js b/server/src/controllers/monitorController.js
index a4f391087..0f40a4a01 100755
--- a/server/src/controllers/monitorController.js
+++ b/server/src/controllers/monitorController.js
@@ -441,6 +441,17 @@ class MonitorController extends BaseController {
SERVICE_NAME,
"exportMonitorsToCSV"
);
+
+ getAllGames = this.asyncHandler(
+ async (req, res) => {
+ return res.success({
+ msg: "OK",
+ data: this.monitorService.getAllGames(),
+ });
+ },
+ SERVICE_NAME,
+ "getAllGames"
+ );
}
export default MonitorController;
diff --git a/server/src/db/models/Monitor.js b/server/src/db/models/Monitor.js
index 266cebeef..1ee467999 100755
--- a/server/src/db/models/Monitor.js
+++ b/server/src/db/models/Monitor.js
@@ -33,7 +33,7 @@ const MonitorSchema = mongoose.Schema(
type: {
type: String,
required: true,
- enum: ["http", "ping", "pagespeed", "hardware", "docker", "port"],
+ enum: ["http", "ping", "pagespeed", "hardware", "docker", "port", "game"],
},
ignoreTlsErrors: {
type: Boolean,
@@ -115,6 +115,9 @@ const MonitorSchema = mongoose.Schema(
return this.alertThreshold;
},
},
+ gameId: {
+ type: String,
+ },
},
{
timestamps: true,
diff --git a/server/src/db/mongo/modules/checkModule.js b/server/src/db/mongo/modules/checkModule.js
index f806b5bf5..a606a593b 100755
--- a/server/src/db/mongo/modules/checkModule.js
+++ b/server/src/db/mongo/modules/checkModule.js
@@ -86,6 +86,7 @@ class CheckModule {
port: this.Check,
pagespeed: this.PageSpeedCheck,
hardware: this.HardwareCheck,
+ game: this.Check,
};
const Model = checkModels[type];
diff --git a/server/src/db/mongo/modules/monitorModule.js b/server/src/db/mongo/modules/monitorModule.js
index 76f055f94..c4a896030 100755
--- a/server/src/db/mongo/modules/monitorModule.js
+++ b/server/src/db/mongo/modules/monitorModule.js
@@ -310,7 +310,7 @@ class MonitorModule {
checks: this.processChecksForDisplay(this.NormalizeData, checksForDateRange, numToDisplay, normalize),
};
- if (monitor.type === "http" || monitor.type === "ping" || monitor.type === "docker" || monitor.type === "port") {
+ if (monitor.type === "http" || monitor.type === "ping" || monitor.type === "docker" || monitor.type === "port" || monitor.type === "game") {
// HTTP/PING Specific stats
monitorStats.periodAvgResponseTime = this.getAverageResponseTime(checksForDateRange);
monitorStats.periodUptime = this.getUptimePercentage(checksForDateRange);
diff --git a/server/src/db/mongo/modules/monitorModuleQueries.js b/server/src/db/mongo/modules/monitorModuleQueries.js
index f7283c8e9..a5a56ff9b 100755
--- a/server/src/db/mongo/modules/monitorModuleQueries.js
+++ b/server/src/db/mongo/modules/monitorModuleQueries.js
@@ -910,7 +910,7 @@ const buildGetMonitorsByTeamIdPipeline = (req) => {
$switch: {
branches: [
{
- case: { $in: ["$type", ["http", "ping", "docker", "port"]] },
+ case: { $in: ["$type", ["http", "ping", "docker", "port", "game"]] },
then: "$standardchecks",
},
{
diff --git a/server/src/middleware/sanitization.js b/server/src/middleware/sanitization.js
new file mode 100644
index 000000000..a89325de1
--- /dev/null
+++ b/server/src/middleware/sanitization.js
@@ -0,0 +1,85 @@
+import { JSDOM } from "jsdom";
+import DOMPurify from "isomorphic-dompurify";
+
+// Initialize DOMPurify with jsdom
+const window = new JSDOM("").window;
+const purify = DOMPurify(window);
+
+/**
+ * Sanitizes user input to prevent XSS attacks
+ * @param {string} input - The input string to sanitize
+ * @param {Object} options - Sanitization options
+ * @returns {string} The sanitized string
+ */
+export const sanitizeInput = (input, options = {}) => {
+ if (typeof input !== "string") {
+ return input;
+ }
+
+ // Default configuration - remove all HTML tags and attributes
+ const defaultConfig = {
+ ALLOWED_TAGS: [],
+ ALLOWED_ATTR: [],
+ KEEP_CONTENT: true,
+ ...options,
+ };
+
+ return purify.sanitize(input, defaultConfig);
+};
+
+/**
+ * Sanitizes an object recursively
+ * @param {Object} obj - The object to sanitize
+ * @param {Object} options - Sanitization options
+ * @returns {Object} The sanitized object
+ */
+export const sanitizeObject = (obj, options = {}) => {
+ if (typeof obj !== "object" || obj === null) {
+ return obj;
+ }
+
+ if (Array.isArray(obj)) {
+ return obj.map((item) => sanitizeObject(item, options));
+ }
+
+ const sanitized = {};
+ for (const [key, value] of Object.entries(obj)) {
+ if (typeof value === "string") {
+ sanitized[key] = sanitizeInput(value, options);
+ } else if (typeof value === "object" && value !== null) {
+ sanitized[key] = sanitizeObject(value, options);
+ } else {
+ sanitized[key] = value;
+ }
+ }
+
+ return sanitized;
+};
+
+/**
+ * Express middleware for sanitizing request body
+ * @param {Object} options - Sanitization options
+ * @returns {Function} Express middleware function
+ */
+export const sanitizeBody = (options = {}) => {
+ return (req, res, next) => {
+ if (req.body && typeof req.body === "object") {
+ req.body = sanitizeObject(req.body, options);
+ }
+ next();
+ };
+};
+
+/**
+ * Express middleware for sanitizing query parameters
+ * @param {Object} options - Sanitization options
+ * @returns {Function} Express middleware function
+ */
+export const sanitizeQuery = (options = {}) => {
+ return (req, res, next) => {
+ if (req.query && typeof req.query === "object") {
+ req.query = sanitizeObject(req.query, options);
+ }
+ next();
+ };
+};
diff --git a/server/src/routes/monitorRoute.js b/server/src/routes/monitorRoute.js
index 0124c209d..043d361d1 100755
--- a/server/src/routes/monitorRoute.js
+++ b/server/src/routes/monitorRoute.js
@@ -44,6 +44,7 @@ class MonitorRoutes {
this.router.get("/export", isAllowed(["admin", "superadmin"]), this.monitorController.exportMonitorsToCSV);
this.router.post("/bulk", isAllowed(["admin", "superadmin"]), upload.single("csvFile"), this.monitorController.createBulkMonitors);
this.router.post("/test-email", isAllowed(["admin", "superadmin"]), this.monitorController.sendTestEmail);
+ this.router.get("/games", this.monitorController.getAllGames);
// Individual monitor CRUD routes
this.router.get("/:monitorId", this.monitorController.getMonitorById);
diff --git a/server/src/service/business/monitorService.js b/server/src/service/business/monitorService.js
index 32f7d1ebe..637870b71 100644
--- a/server/src/service/business/monitorService.js
+++ b/server/src/service/business/monitorService.js
@@ -4,7 +4,7 @@ const SERVICE_NAME = "MonitorService";
class MonitorService {
static SERVICE_NAME = SERVICE_NAME;
- constructor({ db, settingsService, jobQueue, stringService, emailService, papaparse, logger, errorService }) {
+ constructor({ db, settingsService, jobQueue, stringService, emailService, papaparse, logger, errorService, games }) {
this.db = db;
this.settingsService = settingsService;
this.jobQueue = jobQueue;
@@ -13,6 +13,7 @@ class MonitorService {
this.papaparse = papaparse;
this.logger = logger;
this.errorService = errorService;
+ this.games = games;
}
get serviceName() {
@@ -183,7 +184,8 @@ class MonitorService {
};
addDemoMonitors = async ({ userId, teamId }) => {
- const demoMonitors = await this.db.monitorModuleaddDemoMonitors(userId, teamId);
+ const demoMonitors = await this.db.monitorModule.addDemoMonitors(userId, teamId);
+
await Promise.all(demoMonitors.map((monitor) => this.jobQueue.addJob(monitor._id, monitor)));
return demoMonitors;
};
@@ -261,6 +263,10 @@ class MonitorService {
const csv = this.papaparse.unparse(csvData);
return csv;
};
+
+ getAllGames = () => {
+ return this.games;
+ };
}
export default MonitorService;
diff --git a/server/src/service/infrastructure/bufferService.js b/server/src/service/infrastructure/bufferService.js
index 46da6bef3..dc86597b2 100755
--- a/server/src/service/infrastructure/bufferService.js
+++ b/server/src/service/infrastructure/bufferService.js
@@ -6,6 +6,7 @@ const TYPE_MAP = {
docker: "checks",
pagespeed: "pagespeedChecks",
hardware: "hardwareChecks",
+ game: "checks",
};
class BufferService {
diff --git a/server/src/service/infrastructure/networkService.js b/server/src/service/infrastructure/networkService.js
index 6a2e3c8a7..8f904bf56 100755
--- a/server/src/service/infrastructure/networkService.js
+++ b/server/src/service/infrastructure/networkService.js
@@ -1,5 +1,6 @@
import jmespath from "jmespath";
import https from "https";
+import { GameDig } from "gamedig";
const SERVICE_NAME = "NetworkService";
const UPROCK_ENDPOINT = "https://api.uprock.com/checkmate/push";
@@ -23,6 +24,7 @@ class NetworkService {
this.TYPE_HARDWARE = "hardware";
this.TYPE_DOCKER = "docker";
this.TYPE_PORT = "port";
+ this.TYPE_GAME = "game";
this.SERVICE_NAME = SERVICE_NAME;
this.NETWORK_ERROR = 5000;
this.PING_ERROR = 5001;
@@ -470,6 +472,62 @@ class NetworkService {
}
}
+ /**
+ * Requests the status of a game monitor.
+ *
+ * @param {Object} monitor - The monitor object to request the status for.
+ * @returns {Promise