From cdd7b2d44b48159e8f48aaa560e48e2af2a137f2 Mon Sep 17 00:00:00 2001 From: Georges-Antoine Assi Date: Mon, 22 May 2023 13:50:58 -0400 Subject: [PATCH] Enhanced glances widget (#1534) * Enhanced glances widget (resource match) * Make widget clickable + cleanup helperrs * Prevent unused glances API calls --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- public/locales/en/common.json | 6 +- src/components/widgets/glances/glances.jsx | 90 +++++++++++++++++++--- src/pages/api/widgets/glances.js | 28 +++++-- src/utils/config/widget-helpers.js | 24 +++--- 4 files changed, 115 insertions(+), 33 deletions(-) diff --git a/public/locales/en/common.json b/public/locales/en/common.json index b725132a9..7f1a86de5 100755 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -357,10 +357,14 @@ }, "glances": { "cpu": "CPU", - "mem": "MEM", + "load": "Load", "wait": "Please wait", "temp": "TEMP", + "warn": "Warn", "uptime": "UP", + "total": "Total", + "free": "Free", + "used": "Used", "days": "d", "hours": "h" }, diff --git a/src/components/widgets/glances/glances.jsx b/src/components/widgets/glances/glances.jsx index fc0d21ed7..85dd44c0c 100644 --- a/src/components/widgets/glances/glances.jsx +++ b/src/components/widgets/glances/glances.jsx @@ -1,11 +1,14 @@ import useSWR from "swr"; +import { useContext } from "react"; import { BiError } from "react-icons/bi"; import { FaMemory, FaRegClock, FaThermometerHalf } from "react-icons/fa"; -import { FiCpu } from "react-icons/fi"; +import { FiCpu, FiHardDrive } from "react-icons/fi"; import { useTranslation } from "next-i18next"; import UsageBar from "../resources/usage-bar"; +import { SettingsContext } from "utils/contexts/settings"; + const cpuSensorLabels = ["cpu_thermal", "Core", "Tctl"]; function convertToFahrenheit(t) { @@ -14,6 +17,7 @@ function convertToFahrenheit(t) { export default function Widget({ options }) { const { t, i18n } = useTranslation(); + const { settings } = useContext(SettingsContext); const { data, error } = useSWR( `/api/widgets/glances?${new URLSearchParams({ lang: i18n.language, ...options }).toString()}`, { @@ -88,8 +92,16 @@ export default function Widget({ options }) { } const tempPercent = Math.round((mainTemp / maxTemp) * 100); + let disks = []; + + if (options.disk) { + disks = Array.isArray(options.disk) + ? options.disk.map((disk) => data.fs.find((d) => d.mnt_point === disk)).filter((d) => d) + : [data.fs.find((d) => d.mnt_point === options.disk)].filter((d) => d); + } + return ( -
+
@@ -97,7 +109,7 @@ export default function Widget({ options }) {
{t("common.number", { - value: data.quicklook.cpu, + value: data.cpu.total, style: "unit", unit: "percent", maximumFractionDigits: 0, @@ -105,7 +117,20 @@ export default function Widget({ options }) {
{t("glances.cpu")}
- + {options.expanded && ( + +
+ {t("common.number", { + value: data.load.min15, + style: "unit", + unit: "percent", + maximumFractionDigits: 0, + })} +
+
{t("glances.load")}
+
+ )} +
@@ -113,18 +138,46 @@ export default function Widget({ options }) {
- {t("common.number", { - value: data.quicklook.mem, - style: "unit", - unit: "percent", - maximumFractionDigits: 0, + {t("common.bytes", { + value: data.mem.free, + maximumFractionDigits: 1, + binary: true, })}
-
{t("glances.mem")}
+
{t("glances.free")}
- + {options.expanded && ( + +
+ {t("common.bytes", { + value: data.mem.total, + maximumFractionDigits: 1, + binary: true, + })} +
+
{t("glances.total")}
+
+ )} +
+ {disks.map((disk) => ( +
+ +
+ +
{t("common.bytes", { value: disk.free })}
+
{t("glances.free")}
+
+ {options.expanded && ( + +
{t("common.bytes", { value: disk.size })}
+
{t("glances.total")}
+
+ )} + +
+
))} {options.cputemp && mainTemp > 0 && (
@@ -140,6 +193,19 @@ export default function Widget({ options }) {
{t("glances.temp")}
+ {options.expanded && ( + +
+ {t("common.number", { + value: maxTemp, + maximumFractionDigits: 1, + style: "unit", + unit + })} +
+
{t("glances.warn")}
+
+ )}
)} @@ -160,6 +226,6 @@ export default function Widget({ options }) { {options.label && (
{options.label}
)} - +
); } diff --git a/src/pages/api/widgets/glances.js b/src/pages/api/widgets/glances.js index 46be14a05..7605144d8 100644 --- a/src/pages/api/widgets/glances.js +++ b/src/pages/api/widgets/glances.js @@ -40,20 +40,32 @@ async function retrieveFromGlancesAPI(privateWidgetOptions, endpoint) { } export default async function handler(req, res) { - const { index } = req.query; + const { index, cputemp: includeCpuTemp, uptime: includeUptime, disk: includeDisks } = req.query; const privateWidgetOptions = await getPrivateWidgetOptions("glances", index); try { - const quicklookData = await retrieveFromGlancesAPI(privateWidgetOptions, "quicklook"); - + const cpuData = await retrieveFromGlancesAPI(privateWidgetOptions, "cpu"); + const loadData = await retrieveFromGlancesAPI(privateWidgetOptions, "load"); + const memoryData = await retrieveFromGlancesAPI(privateWidgetOptions, "mem"); const data = { - quicklook: quicklookData + cpu: cpuData, + load: loadData, + mem: memoryData, + } + + // Disabled by default, dont call unless needed + if (includeUptime) { + data.uptime = await retrieveFromGlancesAPI(privateWidgetOptions, "uptime"); + } + + if (includeCpuTemp) { + data.sensors = await retrieveFromGlancesAPI(privateWidgetOptions, "sensors"); + } + + if (includeDisks) { + data.fs = await retrieveFromGlancesAPI(privateWidgetOptions, "fs"); } - - data.uptime = await retrieveFromGlancesAPI(privateWidgetOptions, "uptime"); - - data.sensors = await retrieveFromGlancesAPI(privateWidgetOptions, "sensors"); return res.status(200).send(data); } catch (e) { diff --git a/src/utils/config/widget-helpers.js b/src/utils/config/widget-helpers.js index c03bd9065..6f61b7e28 100644 --- a/src/utils/config/widget-helpers.js +++ b/src/utils/config/widget-helpers.js @@ -5,8 +5,6 @@ import yaml from "js-yaml"; import checkAndCopyConfig, { substituteEnvironmentVars } from "utils/config/config"; -const exemptWidgets = ["search"]; - export async function widgetsFromConfig() { checkAndCopyConfig("widgets.yaml"); @@ -32,15 +30,17 @@ export async function cleanWidgetGroups(widgets) { return widgets.map((widget, index) => { const sanitizedOptions = widget.options; const optionKeys = Object.keys(sanitizedOptions); - if (!exemptWidgets.includes(widget.type)) { - ["url", "username", "password", "key"].forEach((pO) => { - if (optionKeys.includes(pO)) { - // allow URL in search - if (widget.type !== "search" && pO !== "key") { - delete sanitizedOptions[pO]; - } - } - }); + + // delete private options from the sanitized options + ["username", "password", "key"].forEach((pO) => { + if (optionKeys.includes(pO)) { + delete sanitizedOptions[pO]; + } + }); + + // delete url from the sanitized options if the widget is not a search or glances widgeth + if (widget.type !== "search" && widget.type !== "glances" && optionKeys.includes("url")) { + delete sanitizedOptions.url; } return { @@ -78,4 +78,4 @@ export async function getPrivateWidgetOptions(type, widgetIndex) { }); return (type !== undefined && widgetIndex !== undefined) ? privateOptions.find(o => o.type === type && o.options.index === parseInt(widgetIndex, 10))?.options : privateOptions; -} \ No newline at end of file +}