Merge pull request #2736 from burak28/feat/add-game-server-support

Feat/add game server support
This commit is contained in:
Alexander Holliday
2025-08-08 15:11:06 -07:00
committed by GitHub
34 changed files with 755 additions and 104 deletions
@@ -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(
+26 -1
View File
@@ -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,
};
+60 -6
View File
@@ -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}
/>
<Radio
name="type"
title={t("gameServerMonitoring")}
desc={t("gameServerMonitoringDescription")}
size="small"
value="game"
checked={monitor.type === "game"}
onChange={onChange}
/>
{errors["type"] ? (
<Box className="error-container">
<Typography
@@ -547,8 +590,19 @@ const UptimeCreate = ({ isClone = false }) => {
onChange={onChange}
error={errors["port"] ? true : false}
helperText={errors["port"]}
hidden={monitor.type !== "port"}
hidden={monitor.type !== "port" && monitor.type !== "game"}
/>
{monitor.type === "game" && (
<Select
name="gameId"
label={t("chooseGame")}
value={monitor.gameId || ""}
placeholder={t("chooseGame")}
onChange={onChange}
items={GAMELIST}
error={errors["gameId"] ? true : false}
/>
)}
<TextInput
name="name"
type="text"
@@ -32,6 +32,7 @@ const getTypeOptions = () => [
{ 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
+1 -1
View File
@@ -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();
+14
View File
@@ -74,6 +74,20 @@ class NetworkService {
}
}
/**
* Fetch the games associated with a monitor
*
* @async
* @returns {Promise<AxiosResponse>} The response from the axios GET request.
*/
async getMonitorGames() {
return this.axiosInstance.get(`/monitors/games`, {
headers: {
"Content-Type": "application/json",
},
});
}
/**
*
* ************************************
+11 -3
View File
@@ -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({
+3 -1
View File
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
"chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
"port": ""
"port": "",
"game": ""
},
"common": {
"appName": "",
+3 -1
View File
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
"chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
"port": ""
"port": "",
"game": ""
},
"common": {
"appName": "Checkmate",
+3 -1
View File
@@ -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",
+10 -1
View File
@@ -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 <short_id> 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": {
+3 -1
View File
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
"chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
"port": ""
"port": "",
"game": ""
},
"common": {
"appName": "",
+3 -1
View File
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
"chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
"port": ""
"port": "",
"game": ""
},
"common": {
"appName": "",
+3 -1
View File
@@ -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 <short_id> 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",
+3 -1
View File
@@ -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 <short_id>を実行してフルコンテナIDを取得できます。",
"port": "サーバーのURLまたはIP、ポート番号、ダッシュボードに表示される明確な表示名を入力。"
"port": "サーバーのURLまたはIP、ポート番号、ダッシュボードに表示される明確な表示名を入力。",
"game": ""
},
"common": {
"appName": "Checkmate",
+3 -1
View File
@@ -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 <short_id> 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",
+3 -1
View File
@@ -260,6 +260,7 @@
"notifyEmails": "Также уведомлять по электронной почте на несколько адресов (скоро)",
"seperateEmails": "Вы можете разделить несколько адресов электронной почты запятой.",
"checkFrequency": "Проверить частоту",
"chooseGame": "",
"matchMethod": "Метод сопоставления",
"expectedValue": "Ожидаемое значение",
"deleteDialogTitle": "Вы действительно хотите удалить этот монитор?",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
"port": ""
"port": "",
"game": ""
},
"common": {
"appName": "",
+3 -1
View File
@@ -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",
+3 -1
View File
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
"chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -435,7 +436,8 @@
"http": "",
"ping": "",
"docker": "",
"port": ""
"port": "",
"game": ""
},
"common": {
"appName": "",
+3 -1
View File
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
"chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -435,7 +436,8 @@
"http": "",
"ping": "",
"docker": "",
"port": ""
"port": "",
"game": ""
},
"common": {
"appName": "",
+3 -1
View File
@@ -260,6 +260,7 @@
"notifyEmails": "",
"seperateEmails": "",
"checkFrequency": "",
"chooseGame": "",
"matchMethod": "",
"expectedValue": "",
"deleteDialogTitle": "",
@@ -437,7 +438,8 @@
"http": "",
"ping": "",
"docker": "",
"port": ""
"port": "",
"game": ""
},
"common": {
"appName": "",
+494 -71
View File
@@ -19,6 +19,7 @@
"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",
@@ -1031,6 +1032,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 +1093,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 +1112,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",
@@ -1249,6 +1269,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 +1354,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 +1678,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",
@@ -2263,21 +2349,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",
@@ -2474,6 +2545,33 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"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 +2589,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",
@@ -2707,6 +2814,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 +3229,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 +3342,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 +3553,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 +3621,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 +3672,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 +3733,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 +3827,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",
@@ -3777,6 +4013,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 +4035,19 @@
"node": ">= 0.8"
}
},
"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",
@@ -4174,7 +4429,6 @@
"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 +4590,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 +4789,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 +4852,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 +4933,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 +5767,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 +5956,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",
@@ -5777,6 +6064,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 +7029,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 +7138,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 +7153,21 @@
"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/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -6880,6 +7209,28 @@
"integrity": "sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==",
"license": "ISC"
},
"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 +7618,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 +7644,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 +7793,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 +7928,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",
@@ -7606,6 +7980,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",
@@ -7917,6 +8305,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",
@@ -8283,6 +8677,35 @@
"integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
"license": "ISC"
},
"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/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",
+1
View File
@@ -26,6 +26,7 @@
"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",
+2
View File
@@ -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 = {
@@ -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;
+4 -1
View File
@@ -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,
@@ -86,6 +86,7 @@ class CheckModule {
port: this.Check,
pagespeed: this.PageSpeedCheck,
hardware: this.HardwareCheck,
game: this.Check,
};
const Model = checkModels[type];
+1 -1
View File
@@ -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);
@@ -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",
},
{
+1
View File
@@ -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);
@@ -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;
@@ -6,6 +6,7 @@ const TYPE_MAP = {
docker: "checks",
pagespeed: "pagespeedChecks",
hardware: "hardwareChecks",
game: "checks",
};
class BufferService {
@@ -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<Object>} The response from the game status request.
* @throws {Error} Throws an error if the request fails or if the monitor is not configured correctly.
* @property {string} monitorId - The ID of the monitor.
* @property {string} type - The type of the monitor (should be "game").
* @property {number} responseTime - The time taken for the request.
* @property {Object|null} payload - The game state response or null if the request failed.
* @property {boolean} status - Indicates if the request was successful (true) or not (false).
* @property {number} code - The status code of the request (200 for success, NETWORK_ERROR for failure).
* @property {string} message - A message indicating the result of the request.
*/
async requestGame(monitor) {
try {
const { url, port, gameId } = monitor;
const gameResponse = {
code: 200,
status: true,
message: "Success",
monitorId: monitor._id,
type: "game",
};
const state = await GameDig.query({
type: gameId,
host: url,
port: port,
}).catch((error) => {
this.logger.warn({
message: error.message,
service: this.SERVICE_NAME,
method: "requestGame",
details: { url, port, gameId },
});
});
if (!state) {
gameResponse.status = false;
gameResponse.code = this.NETWORK_ERROR;
gameResponse.message = "No response";
return gameResponse;
}
gameResponse.responseTime = state.ping;
gameResponse.payload = state;
return gameResponse;
} catch (error) {
error.service = this.SERVICE_NAME;
error.method = "requestPing";
throw error;
}
}
/**
* Gets the status of a job based on its type and returns the appropriate response.
*
@@ -494,6 +552,8 @@ class NetworkService {
return await this.requestDocker(monitor);
case this.TYPE_PORT:
return await this.requestPort(monitor);
case this.TYPE_GAME:
return await this.requestGame(monitor);
default:
return this.handleUnsupportedType(type);
}
+5 -3
View File
@@ -124,8 +124,8 @@ const getMonitorsByTeamIdQueryValidation = joi.object({
type: joi
.alternatives()
.try(
joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port"),
joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port"))
joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game"),
joi.array().items(joi.string().valid("http", "ping", "pagespeed", "docker", "hardware", "port", "game"))
),
page: joi.number(),
rowsPerPage: joi.number(),
@@ -171,6 +171,7 @@ const createMonitorBodyValidation = joi.object({
jsonPath: joi.string().allow(""),
expectedValue: joi.string().allow(""),
matchMethod: joi.string(),
gameId: joi.string().allow(""),
});
const createMonitorsBodyValidation = joi.array().items(
@@ -197,6 +198,7 @@ const editMonitorBodyValidation = joi.object({
usage_disk: joi.number(),
usage_temperature: joi.number(),
}),
gameId: joi.string(),
});
const pauseMonitorParamValidation = joi.object({
@@ -294,7 +296,7 @@ const getChecksParamValidation = joi.object({
});
const getChecksQueryValidation = joi.object({
type: joi.string().valid("http", "ping", "pagespeed", "hardware", "docker", "port"),
type: joi.string().valid("http", "ping", "pagespeed", "hardware", "docker", "port", "game"),
sortOrder: joi.string().valid("asc", "desc"),
limit: joi.number(),
dateRange: joi.string().valid("recent", "hour", "day", "week", "month", "all"),