Enhancement: resources network widget (#4327)

pull/4331/head
shamoon 2 months ago
parent a06964dd17
commit 897309a47c
No known key found for this signature in database

@ -175,6 +175,7 @@ data:
expanded: true expanded: true
cpu: true cpu: true
memory: true memory: true
network: default
- search: - search:
provider: duckduckgo provider: duckduckgo
target: _blank target: _blank
@ -370,7 +371,7 @@ prevent unnecessary re-renders on page loads and window / tab focusing. The
procedure for enabling sticky sessions depends on your Ingress controller. Below procedure for enabling sticky sessions depends on your Ingress controller. Below
is an example using Traefik as the Ingress controller. is an example using Traefik as the Ingress controller.
``` ```yaml
apiVersion: traefik.io/v1alpha1 apiVersion: traefik.io/v1alpha1
kind: IngressRoute kind: IngressRoute
metadata: metadata:

@ -24,9 +24,10 @@ _Note: unfortunately, the package used for getting CPU temp ([systeminformation]
tempmin: 0 # optional, minimum cpu temp tempmin: 0 # optional, minimum cpu temp
tempmax: 100 # optional, maximum cpu temp tempmax: 100 # optional, maximum cpu temp
uptime: true uptime: true
units: imperial # only used by cpu temp units: imperial # only used by cpu temp, options: 'imperial' or 'metric'
refresh: 3000 # optional, in ms refresh: 3000 # optional, in ms
diskUnits: bytes # optional, bytes (default) or bbytes. Only applies to disk diskUnits: bytes # optional, bytes (default) or bbytes. Only applies to disk
network: true # optional, uses 'default' if true or specify a network interface name
``` ```
You can also pass a `label` option, which allows you to group resources under named sections, You can also pass a `label` option, which allows you to group resources under named sections,

@ -0,0 +1,47 @@
import useSWR from "swr";
import { FaNetworkWired } from "react-icons/fa";
import { useTranslation } from "next-i18next";
import Resource from "../widget/resource";
import Error from "../widget/error";
export default function Network({ options, refresh = 1500 }) {
const { t } = useTranslation();
// eslint-disable-next-line no-param-reassign
if (options.network === true) options.network = "default";
const { data, error } = useSWR(`/api/widgets/resources?type=network&interfaceName=${options.network}`, {
refreshInterval: refresh,
});
if (error || data?.error) {
return <Error />;
}
if (!data || !data.network) {
return (
<Resource
icon={FaNetworkWired}
value="- ↑"
label="- ↓"
expandedValue="- ↑"
expandedLabel="- ↓"
percentage="0"
wide
/>
);
}
return (
<Resource
icon={FaNetworkWired}
value={`${t("common.byterate", { value: data.network.tx_sec })} ↑`}
label={`${t("common.byterate", { value: data.network.rx_sec })} ↓`}
expandedValue={`${t("common.bytes", { value: data.network.tx_bytes })} ↑`}
expandedLabel={`${t("common.bytes", { value: data.network.rx_bytes })} ↓`}
expanded={options.expanded}
wide
percentage={(100 * data.network.rx_sec) / (data.network.rx_sec + data.network.tx_sec)}
/>
);
}

@ -6,6 +6,7 @@ import Cpu from "./cpu";
import Memory from "./memory"; import Memory from "./memory";
import CpuTemp from "./cputemp"; import CpuTemp from "./cputemp";
import Uptime from "./uptime"; import Uptime from "./uptime";
import Network from "./network";
export default function Resources({ options }) { export default function Resources({ options }) {
const { expanded, units, diskUnits, tempmin, tempmax } = options; const { expanded, units, diskUnits, tempmin, tempmax } = options;
@ -23,6 +24,7 @@ export default function Resources({ options }) {
<Disk key={disk} options={{ disk }} expanded={expanded} diskUnits={diskUnits} refresh={refresh} /> <Disk key={disk} options={{ disk }} expanded={expanded} diskUnits={diskUnits} refresh={refresh} />
)) ))
: options.disk && <Disk options={options} expanded={expanded} diskUnits={diskUnits} refresh={refresh} />} : options.disk && <Disk options={options} expanded={expanded} diskUnits={diskUnits} refresh={refresh} />}
{options.network && <Network options={options} refresh={refresh} />}
{options.cputemp && ( {options.cputemp && (
<CpuTemp expanded={expanded} units={units} refresh={refresh} tempmin={tempmin} tempmax={tempmax} /> <CpuTemp expanded={expanded} units={units} refresh={refresh} tempmin={tempmin} tempmax={tempmax} />
)} )}

@ -10,6 +10,7 @@ export default function Resource({
percentage, percentage,
expanded = false, expanded = false,
additionalClassNames = "", additionalClassNames = "",
wide = false,
}) { }) {
const Icon = icon; const Icon = icon;
@ -18,7 +19,11 @@ export default function Resource({
className={`flex-none flex flex-row items-center mr-3 py-1.5 information-widget-resource ${additionalClassNames}`} className={`flex-none flex flex-row items-center mr-3 py-1.5 information-widget-resource ${additionalClassNames}`}
> >
<Icon className="text-theme-800 dark:text-theme-200 w-5 h-5 resource-icon" /> <Icon className="text-theme-800 dark:text-theme-200 w-5 h-5 resource-icon" />
<div className={`flex flex-col ml-3 text-left min-w-[85px] ${expanded ? " expanded" : ""}`}> <div
className={`flex flex-col ml-3 text-left ${expanded ? " expanded" : ""} ${
wide ? " min-w-[120px]" : "min-w-[85px]"
}`}
>
<div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between"> <div className="text-theme-800 dark:text-theme-200 text-xs flex flex-row justify-between">
<div className="pl-0.5">{value}</div> <div className="pl-0.5">{value}</div>
<div className="pr-1">{label}</div> <div className="pr-1">{label}</div>

@ -7,7 +7,7 @@ const logger = createLogger("resources");
const si = require("systeminformation"); const si = require("systeminformation");
export default async function handler(req, res) { export default async function handler(req, res) {
const { type, target } = req.query; const { type, target, interfaceName = "default" } = req.query;
if (type === "cpu") { if (type === "cpu") {
const load = await si.currentLoad(); const load = await si.currentLoad();
@ -57,6 +57,32 @@ export default async function handler(req, res) {
}); });
} }
if (type === "network") {
let networkData = await si.networkStats();
let interfaceDefault;
logger.debug("networkData:", JSON.stringify(networkData));
if (interfaceName && interfaceName !== "default") {
networkData = networkData.filter((network) => network.iface === interfaceName).at(0);
if (!networkData) {
return res.status(404).json({
error: "Interface not found",
});
}
} else {
interfaceDefault = await si.networkInterfaceDefault();
networkData = networkData.filter((network) => network.iface === interfaceDefault).at(0);
if (!networkData) {
return res.status(404).json({
error: "Default interface not found",
});
}
}
return res.status(200).json({
network: networkData,
interface: interfaceName !== "default" ? interfaceName : interfaceDefault,
});
}
return res.status(400).json({ return res.status(400).json({
error: "invalid type", error: "invalid type",
}); });

Loading…
Cancel
Save