소스 검색

Add Proxmox widget

Jason Fischer 2 년 전
부모
커밋
a9fb458f19

+ 6 - 0
public/locales/en/common.json

@@ -185,5 +185,11 @@
         "users": "Users",
         "users": "Users",
         "loginsLast24H": "Logins (24h)",
         "loginsLast24H": "Logins (24h)",
         "failedLoginsLast24H": "Failed Logins (24h)"
         "failedLoginsLast24H": "Failed Logins (24h)"
+    },
+    "proxmox": {
+        "mem": "MEM",
+        "cpu": "CPU",
+        "lxc": "LXC",
+        "vms": "VMs"
     }
     }
 }
 }

+ 2 - 0
src/utils/proxy/handlers/credentialed.js

@@ -29,6 +29,8 @@ export default async function credentialedProxyHandler(req, res) {
         headers["X-gotify-Key"] = `${widget.key}`;
         headers["X-gotify-Key"] = `${widget.key}`;
       } else if (widget.type === "authentik") {
       } else if (widget.type === "authentik") {
         headers.Authorization = `Bearer ${widget.key}`;
         headers.Authorization = `Bearer ${widget.key}`;
+      } else if (widget.type === "proxmox") {
+        headers.Authorization = `PVEAPIToken=${widget.username}=${widget.password}`;
       } else {
       } else {
         headers["X-API-Key"] = `${widget.key}`;
         headers["X-API-Key"] = `${widget.key}`;
       }
       }

+ 1 - 0
src/widgets/components.js

@@ -20,6 +20,7 @@ const components = {
   pihole: dynamic(() => import("./pihole/component")),
   pihole: dynamic(() => import("./pihole/component")),
   portainer: dynamic(() => import("./portainer/component")),
   portainer: dynamic(() => import("./portainer/component")),
   prowlarr: dynamic(() => import("./prowlarr/component")),
   prowlarr: dynamic(() => import("./prowlarr/component")),
+  proxmox: dynamic(() => import("./proxmox/component")),
   qbittorrent: dynamic(() => import("./qbittorrent/component")),
   qbittorrent: dynamic(() => import("./qbittorrent/component")),
   radarr: dynamic(() => import("./radarr/component")),
   radarr: dynamic(() => import("./radarr/component")),
   readarr: dynamic(() => import("./readarr/component")),
   readarr: dynamic(() => import("./readarr/component")),

+ 53 - 0
src/widgets/proxmox/component.jsx

@@ -0,0 +1,53 @@
+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";
+
+function calcRunning(total, current) {
+  return current.status === "running" ? total + 1 : total;
+}
+
+export default function Component({ service }) {
+  const { t } = useTranslation();
+
+  const { widget } = service;
+
+  const { data: clusterData, error: clusterError } = useWidgetAPI(widget, "cluster/resources");
+
+  if (clusterError) {
+    return <Container error={t("widget.api_error")} />;
+  }
+
+  if (!clusterData || !clusterData.data) {
+    return (
+      <Container service={service}>
+        <Block label="proxmox.vms" />
+        <Block label="proxmox.lxc" />
+        <Block label="proxmox.cpu" />
+        <Block label="proxmox.ram" />
+      </Container>
+    );
+  }
+
+  const { data } = clusterData ;
+  const vms = data.filter(item => item.type === "qemu") || [];
+  const lxc = data.filter(item => item.type === "lxc") || [];
+  const nodes = data.filter(item => item.type === "node") || [];
+
+  const runningVMs = vms.reduce(calcRunning, 0);
+  const runningLXC = lxc.reduce(calcRunning, 0);
+
+  // TODO: support more than one node
+  // TODO: better handling of cluster with zero nodes
+  const node = nodes.length > 0 ? nodes[0] : { cpu: 0.0, mem: 0, maxmem: 0 };
+
+  return (
+    <Container service={service}>
+      <Block label="proxmox.vms" value={`${runningVMs} / ${vms.length}`} />
+      <Block label="proxmox.lxc" value={`${runningLXC} / ${lxc.length}`} />
+      <Block label="proxmox.cpu" value={t("common.percent", { value: (node.cpu * 100) })} />
+      <Block label="proxmox.mem" value={t("common.percent", { value: ((node.mem / node.maxmem) * 100) })} />
+    </Container>
+  );
+}

+ 14 - 0
src/widgets/proxmox/widget.js

@@ -0,0 +1,14 @@
+import credentialedProxyHandler from "utils/proxy/handlers/credentialed";
+
+const widget = {
+  api: "{url}/api2/json/{endpoint}",
+  proxyHandler: credentialedProxyHandler,
+
+  mappings: {
+    "cluster/resources": {
+      endpoint: "cluster/resources",
+    },
+  },
+};
+
+export default widget;

+ 2 - 0
src/widgets/widgets.js

@@ -15,6 +15,7 @@ import overseerr from "./overseerr/widget";
 import pihole from "./pihole/widget";
 import pihole from "./pihole/widget";
 import portainer from "./portainer/widget";
 import portainer from "./portainer/widget";
 import prowlarr from "./prowlarr/widget";
 import prowlarr from "./prowlarr/widget";
+import proxmox from "./proxmox/widget";
 import qbittorrent from "./qbittorrent/widget";
 import qbittorrent from "./qbittorrent/widget";
 import radarr from "./radarr/widget";
 import radarr from "./radarr/widget";
 import readarr from "./readarr/widget";
 import readarr from "./readarr/widget";
@@ -46,6 +47,7 @@ const widgets = {
   pihole,
   pihole,
   portainer,
   portainer,
   prowlarr,
   prowlarr,
+  proxmox,
   qbittorrent,
   qbittorrent,
   radarr,
   radarr,
   readarr,
   readarr,