From 885b2624a8764fea0136ae35098089e42eb3f373 Mon Sep 17 00:00:00 2001 From: Dawud <7688823+technowhizz@users.noreply.github.com> Date: Sat, 23 Mar 2024 08:34:07 +0000 Subject: [PATCH] Enhancement: support Jackett widget with admin password (#3097) (#3165) --------- Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/widgets/services/jackett.md | 4 +- src/utils/proxy/http.js | 2 +- src/widgets/jackett/proxy.js | 68 ++++++++++++++++++++++++++++++++ src/widgets/jackett/widget.js | 5 ++- 4 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 src/widgets/jackett/proxy.js diff --git a/docs/widgets/services/jackett.md b/docs/widgets/services/jackett.md index 22e089a4f..e102743be 100644 --- a/docs/widgets/services/jackett.md +++ b/docs/widgets/services/jackett.md @@ -5,7 +5,7 @@ description: Jackett Widget Configuration Learn more about [Jackett](https://github.com/Jackett/Jackett). -Jackett must not have any authentication for the widget to work. +If Jackett has an admin password set, you must set the `password` field for the widget to work. Allowed fields: `["configured", "errored"]`. @@ -13,5 +13,5 @@ Allowed fields: `["configured", "errored"]`. widget: type: jackett url: http://jackett.host.or.ip - key: jackettapikey + password: jackettadminpassword # optional ``` diff --git a/src/utils/proxy/http.js b/src/utils/proxy/http.js index ff34ce0d6..8a9ce380c 100644 --- a/src/utils/proxy/http.js +++ b/src/utils/proxy/http.js @@ -103,7 +103,7 @@ export async function httpProxy(url, params = {}) { try { const [status, contentType, data, responseHeaders] = await request; - return [status, contentType, data, responseHeaders]; + return [status, contentType, data, responseHeaders, params]; } catch (err) { logger.error( "Error calling %s//%s%s%s...", diff --git a/src/widgets/jackett/proxy.js b/src/widgets/jackett/proxy.js new file mode 100644 index 000000000..5292695fc --- /dev/null +++ b/src/widgets/jackett/proxy.js @@ -0,0 +1,68 @@ +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 logger = createLogger("jackettProxyHandler"); + +async function fetchJackettCookie(widget, loginURL) { + const url = new URL(formatApiCall(loginURL, widget)); + const loginData = `password=${encodeURIComponent(widget.password)}`; + const [status, , , , params] = await httpProxy(url, { + method: "POST", + headers: { + "Content-Type": "application/x-www-form-urlencoded", + }, + body: loginData, + }); + + if (!(status === 200) || !params?.headers?.Cookie) { + logger.error("Failed to fetch Jackett cookie, status: %d", status); + return null; + } + return params.headers.Cookie; +} + +export default async function jackettProxyHandler(req, res) { + const { group, service, endpoint } = req.query; + + if (!group || !service) { + logger.error("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 || !widgets[widget.type].api) { + logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); + return res.status(400).json({ error: "Invalid widget configuration" }); + } + + if (widget.password) { + const jackettCookie = await fetchJackettCookie(widget, widgets[widget.type].loginURL); + if (!jackettCookie) { + return res.status(500).json({ error: "Failed to authenticate with Jackett" }); + } + // Add the cookie to the widget for use in subsequent requests + widget.headers = { ...widget.headers, Cookie: jackettCookie }; + } + + const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget })); + + try { + const [status, , data] = await httpProxy(url, { + method: "GET", + headers: widget.headers, + }); + + if (status !== 200) { + logger.error("Error calling Jackett API: %d. Data: %s", status, data); + return res.status(status).json({ error: "Failed to call Jackett API", data }); + } + + return res.status(status).send(data); + } catch (error) { + logger.error("Exception calling Jackett API: %s", error.message); + return res.status(500).json({ error: "Server error", message: error.message }); + } +} diff --git a/src/widgets/jackett/widget.js b/src/widgets/jackett/widget.js index 9d2a9b5cf..0af816e52 100644 --- a/src/widgets/jackett/widget.js +++ b/src/widgets/jackett/widget.js @@ -1,8 +1,9 @@ -import genericProxyHandler from "utils/proxy/handlers/generic"; +import jackettProxyHandler from "./proxy"; const widget = { api: "{url}/api/v2.0/{endpoint}?apikey={key}&configured=true", - proxyHandler: genericProxyHandler, + proxyHandler: jackettProxyHandler, + loginURL: "{url}/UI/Dashboard", mappings: { indexers: {