diff --git a/public/locales/en/common.json b/public/locales/en/common.json index 4273fff86..80ba5357e 100644 --- a/public/locales/en/common.json +++ b/public/locales/en/common.json @@ -70,6 +70,12 @@ "bitrate": "Bitrate", "no_active": "No Active Streams" }, + "flood": { + "download": "Download", + "upload": "Upload", + "leech": "Leech", + "seed": "Seed" + }, "changedetectionio": { "totalObserved": "Total Observed", "diffsDetected": "Diffs Detected" diff --git a/src/widgets/components.js b/src/widgets/components.js index 606d82913..b0752b62f 100644 --- a/src/widgets/components.js +++ b/src/widgets/components.js @@ -11,6 +11,7 @@ const components = { diskstation: dynamic(() => import("./diskstation/component")), docker: dynamic(() => import("./docker/component")), emby: dynamic(() => import("./emby/component")), + flood: dynamic(() => import("./flood/component")), gluetun: dynamic(() => import("./gluetun/component")), gotify: dynamic(() => import("./gotify/component")), hdhomerun: dynamic(() => import("./hdhomerun/component")), diff --git a/src/widgets/flood/component.jsx b/src/widgets/flood/component.jsx new file mode 100644 index 000000000..0615154f7 --- /dev/null +++ b/src/widgets/flood/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"; + +export default function Component({ service }) { + const { t } = useTranslation(); + + const { widget } = service; + + const { data: torrentData, error: torrentError } = useWidgetAPI(widget, "torrents"); + + if (torrentError) { + return ; + } + + if (!torrentData) { + return ( + + + + + + + ); + } + + let rateDl = 0; + let rateUl = 0; + let completed = 0; + let leech = 0; + + for (var torrent in torrentData.torrents) { + rateDl += torrentData.torrents[torrent].downRate; + rateUl += torrentData.torrents[torrent].upRate; + if(torrentData.torrents[torrent].status.includes('complete')){ + completed += 1; + } + if(torrentData.torrents[torrent].status.includes('downloading')){ + leech += 1; + } + } + + return ( + + + + + + + ); +} diff --git a/src/widgets/flood/proxy.js b/src/widgets/flood/proxy.js new file mode 100644 index 000000000..adcebddcf --- /dev/null +++ b/src/widgets/flood/proxy.js @@ -0,0 +1,61 @@ +import { formatApiCall } from "utils/proxy/api-helpers"; +import { httpProxy } from "utils/proxy/http"; +import getServiceWidget from "utils/config/service-helpers"; +import createLogger from "utils/logger"; + +const logger = createLogger("floodProxyHandler"); + +async function login(widget) { + logger.debug("flood is rejecting the request, logging in."); + const loginUrl = new URL(`${widget.url}/api/auth/authenticate`).toString(); + const loginHeaders = { + "Accept-Encoding": "application/json" + }; + if (widget.username && widget.password) { + loginHeaders.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}` + } + + const loginParams = { method: "POST", loginHeaders }; + + // eslint-disable-next-line no-unused-vars + const [status, contentType, data] = await httpProxy(loginUrl, loginParams); + return [status, data]; +} + +export default async function floodProxyHandler(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("{url}/api/{endpoint}", { endpoint, ...widget })); + const params = { method: "GET", headers: {} }; + + let [status, contentType, data] = await httpProxy(url, params); + if (status === 401) { + [status, data] = await login(widget); + + if (status !== 200) { + logger.error("HTTP %d logging in to flood. Data: %s", status, data); + return res.status(status).end(data); + } + + [status, contentType, data] = await httpProxy(url, params); + } + + if (status !== 200) { + logger.error("HTTP %d getting data from flood. Data: %s", status, data); + } + + if (contentType) res.setHeader("Content-Type", contentType); + return res.status(status).send(data); +} diff --git a/src/widgets/flood/widget.js b/src/widgets/flood/widget.js new file mode 100644 index 000000000..027ff344b --- /dev/null +++ b/src/widgets/flood/widget.js @@ -0,0 +1,7 @@ +import floodProxyHandler from "./proxy"; + +const widget = { + proxyHandler: floodProxyHandler, +}; + +export default widget; diff --git a/src/widgets/widgets.js b/src/widgets/widgets.js index 24dd6e5f9..b3eaa8855 100644 --- a/src/widgets/widgets.js +++ b/src/widgets/widgets.js @@ -7,6 +7,7 @@ import coinmarketcap from "./coinmarketcap/widget"; import deluge from "./deluge/widget"; import diskstation from "./diskstation/widget"; import emby from "./emby/widget"; +import flood from "./flood/widget"; import gluetun from "./gluetun/widget"; import gotify from "./gotify/widget"; import hdhomerun from "./hdhomerun/widget"; @@ -54,6 +55,7 @@ const widgets = { deluge, diskstation, emby, + flood, gluetun, gotify, hdhomerun,