diff --git a/public/locales/en/common.json b/public/locales/en/common.json index c46e6c38..c6d44949 100755 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -576,5 +576,11 @@ "people_home": "People Home", "lights_on": "Lights On", "switches_on": "Switches On" + }, + "nomad": { + "nodes": "Nodes", + "jobs": "Jobs", + "volumes": "Volumes", + "services": "Services" } } diff --git a/public/locales/zh-CN/common.json b/public/locales/zh-CN/common.json index fd77e685..9518f9dc 100644 --- a/public/locales/zh-CN/common.json +++ b/public/locales/zh-CN/common.json @@ -567,5 +567,11 @@ "people_home": "People Home", "lights_on": "Lights On", "switches_on": "Switches On" + }, + "nomad": { + "nodes": "节点", + "jobs": "任务", + "volumes": "挂载", + "services": "服务" } } diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 93cdb995..84808b76 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -54,6 +54,8 @@ export default async function credentialedProxyHandler(req, res, map) { } else { headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`; } + } else if (widget.type === "nomad") { + headers["X-Nomad-Token"] = `${widget.key}`; } else { headers["X-API-Key"] = `${widget.key}`; } @@ -78,7 +80,7 @@ export default async function credentialedProxyHandler(req, res, map) { if (status >= 400) { logger.error("HTTP Error %d calling %s", status, url.toString()); } - + if (status === 200) { if (!validateWidgetData(widget, endpoint, resultData)) { return res.status(500).json({error: {message: "Invalid data", url: sanitizeErrorURL(url), data: resultData}}); diff --git a/src/widgets/components.js b/src/widgets/components.js index ec7be376..9ebef242 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -42,6 +42,7 @@ const components = { navidrome: dynamic(() => import("./navidrome/component")), nextcloud: dynamic(() => import("./nextcloud/component")), nextdns: dynamic(() => import("./nextdns/component")), + nomad: dynamic(() => import("./nomad/component")), npm: dynamic(() => import("./npm/component")), nzbget: dynamic(() => import("./nzbget/component")), octoprint: dynamic(() => import("./octoprint/component")), diff --git a/src/widgets/nomad/component.jsx b/src/widgets/nomad/component.jsx new file mode 100644 index 00000000..7ad2a822 --- /dev/null +++ b/src/widgets/nomad/component.jsx @@ -0,0 +1,62 @@ +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 calcRunningService(total, current) { + return current.Services.length + total; +} + +function calcReadyNode(total, current) { + return current.Status === "ready" ? total + 1 : total; +} + +function calcRunningJob(total, current) { + return current.Status === "running" ? total + 1 : total; +} + +export default function Component({ service }) { + const { t } = useTranslation(); + const { widget } = service; + + const { data: nodeData, error: nodeError } = useWidgetAPI(widget, "nodes"); + const { data: jobData, error: jobError } = useWidgetAPI(widget, "jobs"); + const { data: serviceData, error: serviceError } = useWidgetAPI(widget, "services"); + const { data: volumeData, error: volumeError } = useWidgetAPI(widget, "volumes"); + const { data: csiVolumeData, error: csiVolumeError } = useWidgetAPI(widget, "csi_volumes"); + + if (nodeError || jobError || serviceError || volumeError || csiVolumeError) { + const finalError = nodeError ?? jobError ?? serviceError ?? volumeError ?? csiVolumeError; + return ; + } + + if (!nodeData || !jobData || !serviceData || !volumeData || !csiVolumeData) { + return ( + + + + + + + ); + } + + const nodes = nodeData || []; + const readyNodes = nodes.reduce(calcReadyNode, 0); + + const jobs = jobData || []; + const runningJobs = jobs.reduce(calcRunningJob, 0); + + const volumeTotal = volumeData.length + csiVolumeData.length; + const runningServices = (serviceData || []).reduce(calcRunningService, 0); + + return ( + + + + + + + ); +} diff --git a/src/widgets/nomad/widget.js b/src/widgets/nomad/widget.js new file mode 100644 index 00000000..363c95d4 --- /dev/null +++ b/src/widgets/nomad/widget.js @@ -0,0 +1,26 @@ +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; + +const widget = { + api: "{url}/v1/{endpoint}", + proxyHandler: credentialedProxyHandler, + + mappings: { + nodes: { + endpoint: "nodes", + }, + jobs: { + endpoint: "jobs", + }, + services: { + endpoint: "services", + }, + volumes: { + endpoint: "volumes", + }, + csi_volumes: { + endpoint: "volumes?type=csi", + } + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index a8ce5282..9e2c5f7f 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -37,6 +37,7 @@ import navidrome from "./navidrome/widget"; import nextcloud from "./nextcloud/widget"; import nextdns from "./nextdns/widget"; import npm from "./npm/widget"; +import nomad from "./nomad/widget"; import nzbget from "./nzbget/widget"; import octoprint from "./octoprint/widget"; import omada from "./omada/widget"; @@ -116,6 +117,7 @@ const widgets = { nextcloud, nextdns, npm, + nomad, nzbget, octoprint, omada,