diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 62ef2102b..dd00ff86e 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -293,6 +293,11 @@ "child_bridges": "Child Bridges", "child_bridges_status": "{{ok}}/{{total}}" }, + "watchtower":{ + "containers_scanned": "Scanned", + "containers_updated": "Updated", + "containers_failed": "Failed" + }, "autobrr": { "approvedPushes": "Approved", "rejectedPushes": "Rejected", diff --git a/src/widgets/components.js b/src/widgets/components.js index f46808a50..c2b501890 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -37,6 +37,7 @@ const components = { traefik: dynamic(() => import("./traefik/component")), transmission: dynamic(() => import("./transmission/component")), unifi: dynamic(() => import("./unifi/component")), + watchtower: dynamic(() => import("./watchtower/component")), }; export default components; diff --git a/src/widgets/watchtower/component.jsx b/src/widgets/watchtower/component.jsx new file mode 100644 index 000000000..68c5531f6 --- /dev/null +++ b/src/widgets/watchtower/component.jsx @@ -0,0 +1,36 @@ +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"; + + +export default function Component({ service }) { + const { t } = useTranslation(); + + const { widget } = service; + + const { data: watchData, error: watchError } = useWidgetAPI(widget, "watchtower"); + + if (watchError || !watchData) { + return ; + } + + if (!watchData) { + return ( + + + + + + ); + } + + return ( + + + + + + ); +} diff --git a/src/widgets/watchtower/proxy.js b/src/widgets/watchtower/proxy.js new file mode 100644 index 000000000..2d54928c6 --- /dev/null +++ b/src/widgets/watchtower/proxy.js @@ -0,0 +1,48 @@ +import { httpProxy } from "utils/proxy/http"; +import { formatApiCall } from "utils/proxy/api-helpers"; +import getServiceWidget from "utils/config/service-helpers"; +import createLogger from "utils/logger"; +import widgets from "widgets/widgets"; + +const proxyName = "watchtowerProxyHandler"; +const logger = createLogger(proxyName); + +export default async function watchtowerProxyHandler(req, res) { + const { group, service, endpoint } = req.query; + + if (!group || !service) { + logger.debug("Invalid or missing service '%s' or group '%s'", service, group); + return res.status(400).json({ error: "Invalid proxy service type" }); + } + + const widget = await getServiceWidget(group, service); + + if (!widget) { + logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); + return res.status(400).json({ error: "Invalid proxy service type" }); + } + + const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget })); + + const [status, contentType, data] = await httpProxy(url, { + method: "GET", + headers: { + "Authorization": `Bearer ${widget.key}`, + } + }); + + if (status !== 200 || !data) { + logger.error("Error getting data from WatchTower: %d. Data: %s", status, data); + } + + const cleanData = data.toString().split("\n").filter(s => s.startsWith("watchtower")) + const jsonRes = {} + + cleanData.map(e => e.split(" ")).forEach(strArray => { + const [key, value] = strArray + jsonRes[key] = value + }) + + if (contentType) res.setHeader("Content-Type", contentType); + return res.status(status).send(jsonRes); +} diff --git a/src/widgets/watchtower/widget.js b/src/widgets/watchtower/widget.js new file mode 100644 index 000000000..6e8fdf661 --- /dev/null +++ b/src/widgets/watchtower/widget.js @@ -0,0 +1,14 @@ +import watchtowerProxyHandler from "./proxy"; + +const widget = { + api: "{url}/{endpoint}", + proxyHandler: watchtowerProxyHandler, + + mappings: { + "watchtower": { + endpoint: "v1/metrics", + }, + }, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 577243a7a..74f426b36 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -32,6 +32,7 @@ import tautulli from "./tautulli/widget"; import traefik from "./traefik/widget"; import transmission from "./transmission/widget"; import unifi from "./unifi/widget"; +import watchtower from './watchtower/widget' const widgets = { adguard, @@ -70,6 +71,7 @@ const widgets = { transmission, unifi, unifi_console: unifi, + watchtower, }; export default widgets;