From 44f8e9d4da4d53e143735e22cc9f02047deca399 Mon Sep 17 00:00:00 2001
From: Mitchell <37925797+ping-localhost@users.noreply.github.com>
Date: Thu, 29 Aug 2024 20:45:05 +0200
Subject: [PATCH] Feature: Zabbix service widget (#3905)
Co-Authored-By: shamoon <4887959+shamoon@users.noreply.github.com>
---
docs/widgets/authoring/proxies.md | 27 ++++++++++++++++-
docs/widgets/services/index.md | 1 +
docs/widgets/services/zabbix.md | 19 ++++++++++++
mkdocs.yml | 1 +
public/locales/en/common.json | 6 ++++
src/utils/proxy/handlers/jsonrpc.js | 16 ++++++----
src/widgets/components.js | 1 +
src/widgets/widgets.js | 2 ++
src/widgets/zabbix/component.jsx | 46 +++++++++++++++++++++++++++++
src/widgets/zabbix/widget.js | 23 +++++++++++++++
10 files changed, 136 insertions(+), 6 deletions(-)
create mode 100644 docs/widgets/services/zabbix.md
create mode 100644 src/widgets/zabbix/component.jsx
create mode 100644 src/widgets/zabbix/widget.js
diff --git a/docs/widgets/authoring/proxies.md b/docs/widgets/authoring/proxies.md
index 220af4ad0..15cdb6703 100644
--- a/docs/widgets/authoring/proxies.md
+++ b/docs/widgets/authoring/proxies.md
@@ -79,7 +79,21 @@ By default the key is passed as an `X-API-Key` header. If you need to pass the k
### `jsonrpcProxyHandler`
-A proxy handler that makes authenticated JSON-RPC requests to the specified API endpoint. Where the endpoint is the method to call.
+A proxy handler that makes authenticated JSON-RPC requests to the specified API endpoint, either using username + password or an API token.
+The endpoint is the method to call and queryParams are used as the parameters.
+
+=== "component.js"
+
+ ```js
+ import Container from "components/services/widget/container";
+ import useWidgetAPI from "utils/proxy/use-widget-api";
+
+ export default function Component({ service }) {
+ const { widget } = service;
+
+ const { data, error } = useWidgetAPI(widget, 'trigger', { "triggerids": "14062", "output": "extend", "selectFunctions": "extend" });
+ }
+ ```
=== "widget.js"
@@ -93,6 +107,7 @@ A proxy handler that makes authenticated JSON-RPC requests to the specified API
mappings: {
total: { endpoint: "total" },
average: { endpoint: "average" },
+ trigger: { endpoint: "trigger.get" },
},
};
```
@@ -110,6 +125,16 @@ A proxy handler that makes authenticated JSON-RPC requests to the specified API
password: your-password
```
+ ```yaml
+ - Your Widget:
+ icon: yourwidget.svg
+ href: https://example.com/
+ widget:
+ type: yourwidget
+ url: http://127.0.0.1:1337
+ key: your-api-token
+ ```
+
### `synologyProxyHandler`
A proxy handler that makes authenticated requests to the specified Synology API endpoint. This is used exclusively for Synology DSM services.
diff --git a/docs/widgets/services/index.md b/docs/widgets/services/index.md
index 61d11df23..35d0fdcf9 100644
--- a/docs/widgets/services/index.md
+++ b/docs/widgets/services/index.md
@@ -128,3 +128,4 @@ You can also find a list of all available service widgets in the sidebar navigat
- [WGEasy](wgeasy.md)
- [WhatsUpDocker](whatsupdocker.md)
- [xTeVe](xteve.md)
+- [Zabbix](zabbix.md)
diff --git a/docs/widgets/services/zabbix.md b/docs/widgets/services/zabbix.md
new file mode 100644
index 000000000..e7f18c416
--- /dev/null
+++ b/docs/widgets/services/zabbix.md
@@ -0,0 +1,19 @@
+---
+title: Zabbix
+description: Zabbix Widget Configuration
+---
+
+Learn more about [Zabbix](https://github.com/zabbix/zabbix).
+
+See the [Zabbix documentation](https://www.zabbix.com/documentation/current/en/manual/web_interface/frontend_sections/users/api_tokens) for details on generating API tokens.
+
+The widget supports (at least) Zibbax server version 7.0.
+
+Allowed fields: `["warning", "average", "high", "disaster"]`.
+
+```yaml
+widget:
+ type: zabbix
+ url: http://zabbix.host.or.ip/zabbix
+ key: your-api-key
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index a8096ad23..7c2d88e1a 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -152,6 +152,7 @@ nav:
- widgets/services/wgeasy.md
- widgets/services/whatsupdocker.md
- widgets/services/xteve.md
+ - widgets/services/zabbix.md
- "Information Widgets":
- widgets/info/index.md
- widgets/info/datetime.md
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 786d98f0b..e54a0a776 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -931,5 +931,11 @@
"links": "Links",
"collections": "Collections",
"tags": "Tags"
+ },
+ "zabbix": {
+ "warning": "Warning",
+ "average": "Average",
+ "high": "High",
+ "disaster": "Disaster"
}
}
diff --git a/src/utils/proxy/handlers/jsonrpc.js b/src/utils/proxy/handlers/jsonrpc.js
index b1b080fd0..41bbf866d 100644
--- a/src/utils/proxy/handlers/jsonrpc.js
+++ b/src/utils/proxy/handlers/jsonrpc.js
@@ -8,14 +8,18 @@ import widgets from "widgets/widgets";
const logger = createLogger("jsonrpcProxyHandler");
-export async function sendJsonRpcRequest(url, method, params, username, password) {
+export async function sendJsonRpcRequest(url, method, params, widget) {
const headers = {
"content-type": "application/json",
accept: "application/json",
};
- if (username && password) {
- headers.authorization = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`;
+ if (widget.username && widget.password) {
+ headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`;
+ }
+
+ if (widget.key) {
+ headers.Authorization = `Bearer ${widget.key}`;
}
const client = new JSONRPCClient(async (rpcRequest) => {
@@ -67,6 +71,9 @@ export default async function jsonrpcProxyHandler(req, res) {
const widget = await getServiceWidget(group, service);
const api = widgets?.[widget.type]?.api;
+ const [, mapping] = Object.entries(widgets?.[widget.type]?.mappings).find(([, value]) => value.endpoint === method);
+ const params = mapping?.params ?? null;
+
if (!api) {
return res.status(403).json({ error: "Service does not support API calls" });
}
@@ -74,8 +81,7 @@ export default async function jsonrpcProxyHandler(req, res) {
if (widget) {
const url = formatApiCall(api, { ...widget });
- // eslint-disable-next-line no-unused-vars
- const [status, contentType, data] = await sendJsonRpcRequest(url, method, null, widget.username, widget.password);
+ const [status, , data] = await sendJsonRpcRequest(url, method, params, widget);
return res.status(status).end(data);
}
}
diff --git a/src/widgets/components.js b/src/widgets/components.js
index 3d800d0ed..9f0218938 100644
--- a/src/widgets/components.js
+++ b/src/widgets/components.js
@@ -127,6 +127,7 @@ const components = {
wgeasy: dynamic(() => import("./wgeasy/component")),
whatsupdocker: dynamic(() => import("./whatsupdocker/component")),
xteve: dynamic(() => import("./xteve/component")),
+ zabbix: dynamic(() => import("./zabbix/component")),
};
export default components;
diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js
index ed468e889..909fea844 100644
--- a/src/widgets/widgets.js
+++ b/src/widgets/widgets.js
@@ -119,6 +119,7 @@ import whatsupdocker from "./whatsupdocker/widget";
import xteve from "./xteve/widget";
import urbackup from "./urbackup/widget";
import romm from "./romm/widget";
+import zabbix from "./zabbix/widget";
const widgets = {
adguard,
@@ -245,6 +246,7 @@ const widgets = {
wgeasy,
whatsupdocker,
xteve,
+ zabbix,
};
export default widgets;
diff --git a/src/widgets/zabbix/component.jsx b/src/widgets/zabbix/component.jsx
new file mode 100644
index 000000000..620edb618
--- /dev/null
+++ b/src/widgets/zabbix/component.jsx
@@ -0,0 +1,46 @@
+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";
+
+const PriorityWarning = "2";
+const PriorityAverage = "3";
+const PriorityHigh = "4";
+const PriorityDisaster = "5";
+
+export default function Component({ service }) {
+ const { t } = useTranslation();
+ const { widget } = service;
+
+ const { data: zabbixData, error: zabbixError } = useWidgetAPI(widget, "trigger");
+
+ if (zabbixError) {
+ return ;
+ }
+
+ if (!zabbixData) {
+ return (
+
+
+
+
+
+
+ );
+ }
+
+ const warning = zabbixData.filter((item) => item.priority === PriorityWarning).length;
+ const average = zabbixData.filter((item) => item.priority === PriorityAverage).length;
+ const high = zabbixData.filter((item) => item.priority === PriorityHigh).length;
+ const disaster = zabbixData.filter((item) => item.priority === PriorityDisaster).length;
+
+ return (
+
+
+
+
+
+
+ );
+}
diff --git a/src/widgets/zabbix/widget.js b/src/widgets/zabbix/widget.js
new file mode 100644
index 000000000..537253595
--- /dev/null
+++ b/src/widgets/zabbix/widget.js
@@ -0,0 +1,23 @@
+import jsonrpcProxyHandler from "utils/proxy/handlers/jsonrpc";
+
+const widget = {
+ api: "{url}/api_jsonrpc.php",
+ proxyHandler: jsonrpcProxyHandler,
+
+ mappings: {
+ trigger: {
+ endpoint: "trigger.get",
+ params: {
+ output: ["triggerid", "description", "priority"],
+ filter: {
+ value: 1,
+ },
+ sortfield: "priority",
+ sortorder: "DESC",
+ monitored: "true",
+ },
+ },
+ },
+};
+
+export default widget;