diff --git a/package.json b/package.json
index 0238c6cdc..bbe3cbf0c 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,7 @@
"js-yaml": "^4.1.0",
"json-rpc-2.0": "^1.4.1",
"memory-cache": "^0.2.0",
+ "minecraft-ping-js": "^1.0.2",
"next": "^12.3.1",
"next-i18next": "^12.0.1",
"node-os-utils": "^1.3.7",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e26fee84d..a3ce3fcd8 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -22,6 +22,7 @@ specifiers:
js-yaml: ^4.1.0
json-rpc-2.0: ^1.4.1
memory-cache: ^0.2.0
+ minecraft-ping-js: ^1.0.2
next: ^12.3.1
next-i18next: ^12.0.1
node-os-utils: ^1.3.7
@@ -53,6 +54,7 @@ dependencies:
js-yaml: 4.1.0
json-rpc-2.0: 1.4.1
memory-cache: 0.2.0
+ minecraft-ping-js: 1.0.2
next: 12.3.1_biqbaboplfbrettd7655fr4n2y
next-i18next: 12.0.1_azq6kxkn3od7qdylwkyksrwopy
node-os-utils: 1.3.7
@@ -2152,6 +2154,13 @@ packages:
engines: {node: '>=6'}
dev: false
+ /minecraft-ping-js/1.0.2:
+ resolution: {integrity: sha512-h9QYG2n+fBKgp520tXBwR354XRzR/w5wXe8CJCmxKm6jbLpAoLODM8Nj5+ssuIVQF8rtxkAnjwv7PH+7ehFzQQ==}
+ dependencies:
+ node-int64: 0.4.0
+ varint: 6.0.0
+ dev: false
+
/mini-svg-data-uri/1.4.4:
resolution: {integrity: sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==}
hasBin: true
@@ -2288,6 +2297,10 @@ packages:
- babel-plugin-macros
dev: false
+ /node-int64/0.4.0:
+ resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==}
+ dev: false
+
/node-os-utils/1.3.7:
resolution: {integrity: sha512-fvnX9tZbR7WfCG5BAy3yO/nCLyjVWD6MghEq0z5FDfN+ZXpLWNITBdbifxQkQ25ebr16G0N7eRWJisOcMEHG3Q==}
dev: false
@@ -3296,6 +3309,10 @@ packages:
hasBin: true
dev: false
+ /varint/6.0.0:
+ resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==}
+ dev: false
+
/verror/1.10.0:
resolution: {integrity: sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==}
engines: {'0': node >=0.6.0}
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index ba88d14cc..c8cb23f59 100755
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -262,6 +262,13 @@
"queued": "Queued",
"series": "Series"
},
+ "minecraft": {
+ "players": "Players",
+ "version": "Version",
+ "status": "Status",
+ "up": "Online",
+ "down": "Offline"
+ },
"miniflux": {
"read": "Read",
"unread": "Unread"
diff --git a/src/widgets/components.js b/src/widgets/components.js
index 0252f9455..bb96b9014 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -31,6 +31,7 @@ const components = {
lidarr: dynamic(() => import("./lidarr/component")),
mastodon: dynamic(() => import("./mastodon/component")),
medusa: dynamic(() => import("./medusa/component")),
+ minecraft: dynamic(() => import("./minecraft/component")),
miniflux: dynamic(() => import("./miniflux/component")),
mikrotik: dynamic(() => import("./mikrotik/component")),
moonraker: dynamic(() => import("./moonraker/component")),
diff --git a/src/widgets/minecraft/component.jsx b/src/widgets/minecraft/component.jsx
new file mode 100644
index 000000000..013d7606a
--- /dev/null
+++ b/src/widgets/minecraft/component.jsx
@@ -0,0 +1,39 @@
+import { useTranslation } from "next-i18next";
+
+import Container from "components/services/widget/container";
+import Block from "components/services/widget/block";
+import useWidgetAPI from "utils/proxy/use-widget-api";
+
+export default function Component({ service }) {
+ const { widget } = service;
+ const { data: serverData, error: serverError } = useWidgetAPI(widget, "status");
+ const { t } = useTranslation();
+
+ if(serverError){
+ return ;
+ }
+ if (!serverData) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ const statusIndicator = serverData.online ?
+ {t("minecraft.up")}:
+ {t("minecraft.down")};
+ const players = serverData.players ? `${serverData.players.online} / ${serverData.players.max}` : "-";
+ const version = serverData.version || "-";
+
+ return (
+
+
+
+
+
+ );
+}
+
\ No newline at end of file
diff --git a/src/widgets/minecraft/proxy.js b/src/widgets/minecraft/proxy.js
new file mode 100644
index 000000000..19d2206ff
--- /dev/null
+++ b/src/widgets/minecraft/proxy.js
@@ -0,0 +1,28 @@
+import { pingWithPromise } from "minecraft-ping-js";
+
+import createLogger from "utils/logger";
+import getServiceWidget from "utils/config/service-helpers";
+
+const proxyName = "minecraftProxyHandler";
+const logger = createLogger(proxyName);
+
+export default async function minecraftProxyHandler(req, res) {
+ const { group, service } = req.query;
+ const serviceWidget = await getServiceWidget(group, service);
+ const url = new URL(serviceWidget.url);
+ try {
+ const pingResponse = await pingWithPromise(url.hostname, url.port || 25565);
+ res.status(200).send({
+ version: pingResponse.version.name,
+ online: true,
+ players: pingResponse.players
+ });
+ } catch (e) {
+ logger.error(e);
+ res.status(200).send({
+ version: undefined,
+ online: false,
+ players: undefined
+ });
+ }
+}
diff --git a/src/widgets/minecraft/widget.js b/src/widgets/minecraft/widget.js
new file mode 100644
index 000000000..aef9bb571
--- /dev/null
+++ b/src/widgets/minecraft/widget.js
@@ -0,0 +1,7 @@
+import minecraftProxyHandler from "./proxy";
+
+const widget = {
+ proxyHandler: minecraftProxyHandler
+}
+
+export default widget;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index c3f6f5863..4e537cd6b 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -25,6 +25,7 @@ import kopia from "./kopia/widget";
import lidarr from "./lidarr/widget";
import mastodon from "./mastodon/widget";
import medusa from "./medusa/widget";
+import minecraft from "./minecraft/widget";
import miniflux from "./miniflux/widget";
import mikrotik from "./mikrotik/widget";
import moonraker from "./moonraker/widget";
@@ -100,6 +101,7 @@ const widgets = {
lidarr,
mastodon,
medusa,
+ minecraft,
miniflux,
mikrotik,
moonraker,