From 321efd08cc967f6e06f15487526cdee21b7c7fbc Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Mon, 10 Oct 2022 11:18:29 -0700
Subject: [PATCH 1/5] Glances info widget
---
public/locales/en/common.json | 5 +
src/components/widgets/glances/glances.jsx | 111 +++++++++++++++++++++
src/components/widgets/widget.jsx | 1 +
src/pages/api/widgets/glances.js | 23 +++++
4 files changed, 140 insertions(+)
create mode 100644 src/components/widgets/glances/glances.jsx
create mode 100644 src/pages/api/widgets/glances.js
diff --git a/public/locales/en/common.json b/public/locales/en/common.json
index 1261c2930..d53d480c7 100644
--- a/public/locales/en/common.json
+++ b/public/locales/en/common.json
@@ -207,5 +207,10 @@
"cpu": "CPU",
"lxc": "LXC",
"vms": "VMs"
+ },
+ "glances": {
+ "cpu": "CPU",
+ "mem": "MEM",
+ "wait": "Please wait"
}
}
diff --git a/src/components/widgets/glances/glances.jsx b/src/components/widgets/glances/glances.jsx
new file mode 100644
index 000000000..75bdeb711
--- /dev/null
+++ b/src/components/widgets/glances/glances.jsx
@@ -0,0 +1,111 @@
+import useSWR from "swr";
+
+import { BiError } from "react-icons/bi";
+import { FaMemory } from "react-icons/fa";
+import { FiCpu } from "react-icons/fi";
+import { useTranslation } from "next-i18next";
+
+import UsageBar from "../resources/usage-bar";
+
+export default function Widget({ options }) {
+ const { t, i18n } = useTranslation();
+
+ const { data, error } = useSWR(
+ `/api/widgets/glances?${new URLSearchParams({ lang: i18n.language, ...options }).toString()}`, {
+ refreshInterval: 1500,
+ }
+ );
+
+ if (error || data?.error) {
+ return (
+
+
+
+
+
+ {t("widget.api_error")}
+
+
+
+
+ );
+ }
+
+ if (!data) {
+ return (
+
+
+
+
+
+
+
+ {t("glances.wait")}
+
+
+
+
+
+
+
+
+
+
+ {t("glances.wait")}
+
+
+
+
+
+
+ {options.label && (
+
{options.label}
+ )}
+
+ );
+ }
+
+ return (
+
+
+
+
+
+
+
+ {t("common.number", {
+ value: data.cpu,
+ style: "unit",
+ unit: "percent",
+ maximumFractionDigits: 0,
+ })}
+
+
{t("glances.cpu")}
+
+
+
+
+
+
+
+
+
+ {t("common.number", {
+ value: data.mem,
+ style: "unit",
+ unit: "percent",
+ maximumFractionDigits: 0,
+ })}
+
+
{t("glances.mem")}
+
+
+
+
+
+ {options.label && (
+
{options.label}
+ )}
+
+ );
+}
diff --git a/src/components/widgets/widget.jsx b/src/components/widgets/widget.jsx
index ac5353eb9..bd31ed934 100644
--- a/src/components/widgets/widget.jsx
+++ b/src/components/widgets/widget.jsx
@@ -11,6 +11,7 @@ const widgetMappings = {
datetime: dynamic(() => import("components/widgets/datetime/datetime")),
logo: dynamic(() => import("components/widgets/logo/logo"), { ssr: false }),
unifi_console: dynamic(() => import("components/widgets/unifi_console/unifi_console")),
+ glances: dynamic(() => import("components/widgets/glances/glances")),
};
export default function Widget({ widget }) {
diff --git a/src/pages/api/widgets/glances.js b/src/pages/api/widgets/glances.js
new file mode 100644
index 000000000..26edbb81f
--- /dev/null
+++ b/src/pages/api/widgets/glances.js
@@ -0,0 +1,23 @@
+import { httpProxy } from "utils/proxy/http";
+
+export default async function handler(req, res) {
+ const { url } = req.query;
+
+ if (!url) {
+ return res.status(400).json({ error: "Missing Glances URL" });
+ }
+
+ const apiUrl = `${url}/api/3/quicklook`;
+ const params = { method: "GET", headers: {
+ "Accept-Encoding": "application/json"
+ } };
+
+ const [status, contentType, data, responseHeaders] = await httpProxy(apiUrl, params);
+
+ if (status !== 200) {
+ logger.error("HTTP %d getting data from glances API %s. Data: %s", status, apiUrl, data);
+ }
+
+ if (contentType) res.setHeader("Content-Type", contentType);
+ return res.status(status).send(data);
+}
From 080bc44a6f3d9421b748d63d03ffd07cd088e358 Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Mon, 10 Oct 2022 11:21:11 -0700
Subject: [PATCH 2/5] Lint glances info widget
---
src/components/widgets/glances/glances.jsx | 1 -
src/pages/api/widgets/glances.js | 5 ++++-
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/src/components/widgets/glances/glances.jsx b/src/components/widgets/glances/glances.jsx
index 75bdeb711..a48cef50c 100644
--- a/src/components/widgets/glances/glances.jsx
+++ b/src/components/widgets/glances/glances.jsx
@@ -1,5 +1,4 @@
import useSWR from "swr";
-
import { BiError } from "react-icons/bi";
import { FaMemory } from "react-icons/fa";
import { FiCpu } from "react-icons/fi";
diff --git a/src/pages/api/widgets/glances.js b/src/pages/api/widgets/glances.js
index 26edbb81f..b02028600 100644
--- a/src/pages/api/widgets/glances.js
+++ b/src/pages/api/widgets/glances.js
@@ -1,4 +1,7 @@
import { httpProxy } from "utils/proxy/http";
+import createLogger from "utils/logger";
+
+const logger = createLogger("glances");
export default async function handler(req, res) {
const { url } = req.query;
@@ -12,7 +15,7 @@ export default async function handler(req, res) {
"Accept-Encoding": "application/json"
} };
- const [status, contentType, data, responseHeaders] = await httpProxy(apiUrl, params);
+ const [status, contentType, data] = await httpProxy(apiUrl, params);
if (status !== 200) {
logger.error("HTTP %d getting data from glances API %s. Data: %s", status, apiUrl, data);
From 802fe0f721b641f09effab272ebd287979e89f65 Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Tue, 11 Oct 2022 08:13:19 -0700
Subject: [PATCH 3/5] Glances widget use settings for URL
---
src/pages/api/widgets/glances.js | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/src/pages/api/widgets/glances.js b/src/pages/api/widgets/glances.js
index b02028600..d8cc04b7a 100644
--- a/src/pages/api/widgets/glances.js
+++ b/src/pages/api/widgets/glances.js
@@ -1,12 +1,19 @@
import { httpProxy } from "utils/proxy/http";
import createLogger from "utils/logger";
+import { getSettings } from "utils/config/config";
const logger = createLogger("glances");
export default async function handler(req, res) {
- const { url } = req.query;
+ const settings = getSettings()?.glances;
+ if (!settings) {
+ logger.error("There is no glances section in settings.yaml");
+ return res.status(400).json({ error: "There is no glances section in settings.yaml" });
+ }
+ const url = settings?.url;
if (!url) {
+ logger.error("Missing Glances URL");
return res.status(400).json({ error: "Missing Glances URL" });
}
From 99b70f96e438e37c629e95cb1b37f3ce2d06b665 Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Tue, 11 Oct 2022 08:40:15 -0700
Subject: [PATCH 4/5] Allow username + password for glances
---
src/pages/api/widgets/glances.js | 14 +++++++++++---
1 file changed, 11 insertions(+), 3 deletions(-)
diff --git a/src/pages/api/widgets/glances.js b/src/pages/api/widgets/glances.js
index d8cc04b7a..bf00862b2 100644
--- a/src/pages/api/widgets/glances.js
+++ b/src/pages/api/widgets/glances.js
@@ -18,14 +18,22 @@ export default async function handler(req, res) {
}
const apiUrl = `${url}/api/3/quicklook`;
- const params = { method: "GET", headers: {
+ const headers = {
"Accept-Encoding": "application/json"
- } };
+ };
+ if (settings.username && settings.password) {
+ headers.Authorization = `Basic ${Buffer.from(`${settings.username}:${settings.password}`).toString("base64")}`
+ }
+ const params = { method: "GET", headers };
const [status, contentType, data] = await httpProxy(apiUrl, params);
+ if (status === 401) {
+ logger.error("Authorization failure getting data from glances API. Data: %s", data);
+ }
+
if (status !== 200) {
- logger.error("HTTP %d getting data from glances API %s. Data: %s", status, apiUrl, data);
+ logger.error("HTTP %d getting data from glances API. Data: %s", status, data);
}
if (contentType) res.setHeader("Content-Type", contentType);
From 8e2ff61f1cf809c98af06dcfa8913f29819a8728 Mon Sep 17 00:00:00 2001
From: Michael Shamoon <4887959+shamoon@users.noreply.github.com>
Date: Tue, 11 Oct 2022 09:34:59 -0700
Subject: [PATCH 5/5] Allow multiple glances widgets with optional `id`
property
---
src/pages/api/widgets/glances.js | 29 ++++++++++++++++++++---------
1 file changed, 20 insertions(+), 9 deletions(-)
diff --git a/src/pages/api/widgets/glances.js b/src/pages/api/widgets/glances.js
index bf00862b2..86992dd10 100644
--- a/src/pages/api/widgets/glances.js
+++ b/src/pages/api/widgets/glances.js
@@ -5,24 +5,35 @@ import { getSettings } from "utils/config/config";
const logger = createLogger("glances");
export default async function handler(req, res) {
- const settings = getSettings()?.glances;
- if (!settings) {
- logger.error("There is no glances section in settings.yaml");
- return res.status(400).json({ error: "There is no glances section in settings.yaml" });
+ const { id } = req.query;
+
+ let errorMessage;
+
+ let instanceID = "glances";
+ if (id) { // multiple instances
+ instanceID = id;
+ }
+ const settings = getSettings();
+ const instanceSettings = settings[instanceID];
+ if (!instanceSettings) {
+ errorMessage = id ? `There is no glances section with id '${id}' in settings.yaml` : "There is no glances section in settings.yaml";
+ logger.error(errorMessage);
+ return res.status(400).json({ error: errorMessage });
}
- const url = settings?.url;
+ const url = instanceSettings?.url;
if (!url) {
- logger.error("Missing Glances URL");
- return res.status(400).json({ error: "Missing Glances URL" });
+ errorMessage = "Missing Glances URL";
+ logger.error(errorMessage);
+ return res.status(400).json({ error: errorMessage });
}
const apiUrl = `${url}/api/3/quicklook`;
const headers = {
"Accept-Encoding": "application/json"
};
- if (settings.username && settings.password) {
- headers.Authorization = `Basic ${Buffer.from(`${settings.username}:${settings.password}`).toString("base64")}`
+ if (instanceSettings.username && instanceSettings.password) {
+ headers.Authorization = `Basic ${Buffer.from(`${instanceSettings.username}:${instanceSettings.password}`).toString("base64")}`
}
const params = { method: "GET", headers };