diff --git a/docs/widgets/services/komga.md b/docs/widgets/services/komga.md index c9edaeb2..fc76127f 100644 --- a/docs/widgets/services/komga.md +++ b/docs/widgets/services/komga.md @@ -9,6 +9,11 @@ Uses the same username and password used to login from the web. Allowed fields: `["libraries", "series", "books"]`. +| Komga API Version | Homepage Widget Version | +| ----------------- | ----------------------- | +| < v2 | 1 (default) | +| >= v2 | 2 | + ```yaml widget: type: komga diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index bd304df9..38e7a2bf 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -304,7 +304,7 @@ export function cleanServiceGroups(groups) { // frigate enableRecentEvents, - // beszel, glances, immich, mealie, pihole, pfsense, speedtest + // beszel, glances, immich, komga, mealie, pihole, pfsense, speedtest version, // glances @@ -482,7 +482,7 @@ export function cleanServiceGroups(groups) { if (snapshotHost) widget.snapshotHost = snapshotHost; if (snapshotPath) widget.snapshotPath = snapshotPath; } - if (["beszel", "glances", "immich", "mealie", "pfsense", "pihole", "speedtest"].includes(type)) { + if (["beszel", "glances", "immich", "komga", "mealie", "pfsense", "pihole", "speedtest"].includes(type)) { if (version) widget.version = parseInt(version, 10); } if (type === "glances") { diff --git a/src/utils/proxy/cookie-jar.js b/src/utils/proxy/cookie-jar.js index 6519231c..baea21d5 100644 --- a/src/utils/proxy/cookie-jar.js +++ b/src/utils/proxy/cookie-jar.js @@ -8,7 +8,7 @@ export function setCookieHeader(url, params) { const existingCookie = cookieJar.getCookieStringSync(url.toString()); if (existingCookie) { params.headers = params.headers ?? {}; - params.headers.Cookie = existingCookie; + params.headers[params.cookieHeader ?? "Cookie"] = existingCookie; } } diff --git a/src/widgets/komga/component.jsx b/src/widgets/komga/component.jsx index 14af8df4..4272ddfc 100644 --- a/src/widgets/komga/component.jsx +++ b/src/widgets/komga/component.jsx @@ -8,16 +8,13 @@ export default function Component({ service }) { const { t } = useTranslation(); const { widget } = service; - const { data: libraryData, error: libraryError } = useWidgetAPI(widget, "libraries"); - const { data: seriesData, error: seriesError } = useWidgetAPI(widget, "series"); - const { data: bookData, error: bookError } = useWidgetAPI(widget, "books"); + const { data: komgaData, error: komgaError } = useWidgetAPI(widget); - if (libraryError || seriesError || bookError) { - const finalError = libraryError ?? seriesError ?? bookError; - return ; + if (komgaError) { + return ; } - if (!libraryData || !seriesData || !bookData) { + if (!komgaData) { return ( @@ -27,9 +24,11 @@ export default function Component({ service }) { ); } + const { libraries: libraryData, series: seriesData, books: bookData } = komgaData; + return ( - + diff --git a/src/widgets/komga/proxy.js b/src/widgets/komga/proxy.js new file mode 100644 index 00000000..a827f408 --- /dev/null +++ b/src/widgets/komga/proxy.js @@ -0,0 +1,86 @@ +import getServiceWidget from "utils/config/service-helpers"; +import { formatApiCall } from "utils/proxy/api-helpers"; +import { httpProxy } from "utils/proxy/http"; +import widgets from "widgets/widgets"; +import createLogger from "utils/logger"; + +const proxyName = "komgaProxyHandler"; +const logger = createLogger(proxyName); + +export default async function komgaProxyHandler(req, res) { + const { group, service, index } = req.query; + + if (group && service) { + const widget = await getServiceWidget(group, service, index); + + if (!widgets?.[widget.type]?.api) { + return res.status(403).json({ error: "Service does not support API calls" }); + } + + if (widget) { + try { + const data = {}; + const headers = { + accept: "application/json", + "Content-Type": "application/json", + }; + if (widget.username && widget.password) { + headers.Authorization = `Basic ${Buffer.from(`${widget.username}:${widget.password}`).toString("base64")}`; + } else if (widget.key) { + headers["X-API-Key"] = widget.key; + } + const librariesURL = formatApiCall(widgets?.[widget.type].api, { ...widget, endpoint: "libraries" }); + const [librariesStatus, , librariesData] = await httpProxy(librariesURL, { + method: "GET", + headers, + cookieHeader: "X-Auth-Token", + }); + + if (librariesStatus !== 200) { + return res.status(librariesStatus).send(data); + } + + data.libraries = JSON.parse(Buffer.from(librariesData).toString()).filter((library) => !library.unavailable); + + const seriesEndpointName = widget.version === 2 ? "seriesv2" : "series"; + const seriesEndpoint = widgets[widget.type].mappings[seriesEndpointName].endpoint; + const seriesURL = formatApiCall(widgets?.[widget.type].api, { ...widget, endpoint: seriesEndpoint }); + const [seriesStatus, , seriesData] = await httpProxy(seriesURL, { + method: widgets[widget.type].mappings[seriesEndpointName].method || "GET", + headers, + body: "{}", + cookieHeader: "X-Auth-Token", + }); + + if (seriesStatus !== 200) { + return res.status(seriesStatus).send(data); + } + + data.series = JSON.parse(Buffer.from(seriesData).toString()); + + const booksEndpointName = widget.version === 2 ? "booksv2" : "books"; + const booksEndpoint = widgets[widget.type].mappings[booksEndpointName].endpoint; + const booksURL = formatApiCall(widgets?.[widget.type].api, { ...widget, endpoint: booksEndpoint }); + const [booksStatus, , booksData] = await httpProxy(booksURL, { + method: widgets[widget.type].mappings[booksEndpointName].method || "GET", + headers, + body: "{}", + cookieHeader: "X-Auth-Token", + }); + + if (booksStatus !== 200) { + return res.status(booksStatus).send(data); + } + + data.books = JSON.parse(Buffer.from(booksData).toString()); + + return res.send(data); + } catch (e) { + logger.error("Error communicating with Komga API: %s", e); + return res.status(500).json({ error: "Error communicating with Komga API" }); + } + } + } + + return res.status(400).json({ error: "Invalid proxy service type" }); +} diff --git a/src/widgets/komga/widget.js b/src/widgets/komga/widget.js index ee01e391..e89bd746 100644 --- a/src/widgets/komga/widget.js +++ b/src/widgets/komga/widget.js @@ -1,25 +1,31 @@ -import genericProxyHandler from "utils/proxy/handlers/generic"; -import { jsonArrayFilter } from "utils/proxy/api-helpers"; +import komgaProxyHandler from "./proxy"; const widget = { api: "{url}/api/v1/{endpoint}", - proxyHandler: genericProxyHandler, + proxyHandler: komgaProxyHandler, mappings: { libraries: { endpoint: "libraries", - map: (data) => ({ - total: jsonArrayFilter(data, (item) => !item.unavailable).length, - }), }, series: { endpoint: "series", validate: ["totalElements"], }, + seriesv2: { + endpoint: "series/list", + method: "POST", + validate: ["totalElements"], + }, books: { endpoint: "books", validate: ["totalElements"], }, + booksv2: { + endpoint: "books/list", + method: "POST", + validate: ["totalElements"], + }, }, };