mirror of
https://github.com/karl0ss/homepage.git
synced 2025-05-02 13:33:40 +01:00
Enhancement: support komga API keys, breaking API changes (#4874)
This commit is contained in:
parent
548b5f8081
commit
d26ec27942
@ -9,6 +9,11 @@ Uses the same username and password used to login from the web.
|
|||||||
|
|
||||||
Allowed fields: `["libraries", "series", "books"]`.
|
Allowed fields: `["libraries", "series", "books"]`.
|
||||||
|
|
||||||
|
| Komga API Version | Homepage Widget Version |
|
||||||
|
| ----------------- | ----------------------- |
|
||||||
|
| < v2 | 1 (default) |
|
||||||
|
| >= v2 | 2 |
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
widget:
|
widget:
|
||||||
type: komga
|
type: komga
|
||||||
|
@ -304,7 +304,7 @@ export function cleanServiceGroups(groups) {
|
|||||||
// frigate
|
// frigate
|
||||||
enableRecentEvents,
|
enableRecentEvents,
|
||||||
|
|
||||||
// beszel, glances, immich, mealie, pihole, pfsense, speedtest
|
// beszel, glances, immich, komga, mealie, pihole, pfsense, speedtest
|
||||||
version,
|
version,
|
||||||
|
|
||||||
// glances
|
// glances
|
||||||
@ -482,7 +482,7 @@ export function cleanServiceGroups(groups) {
|
|||||||
if (snapshotHost) widget.snapshotHost = snapshotHost;
|
if (snapshotHost) widget.snapshotHost = snapshotHost;
|
||||||
if (snapshotPath) widget.snapshotPath = snapshotPath;
|
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 (version) widget.version = parseInt(version, 10);
|
||||||
}
|
}
|
||||||
if (type === "glances") {
|
if (type === "glances") {
|
||||||
|
@ -8,7 +8,7 @@ export function setCookieHeader(url, params) {
|
|||||||
const existingCookie = cookieJar.getCookieStringSync(url.toString());
|
const existingCookie = cookieJar.getCookieStringSync(url.toString());
|
||||||
if (existingCookie) {
|
if (existingCookie) {
|
||||||
params.headers = params.headers ?? {};
|
params.headers = params.headers ?? {};
|
||||||
params.headers.Cookie = existingCookie;
|
params.headers[params.cookieHeader ?? "Cookie"] = existingCookie;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,16 +8,13 @@ export default function Component({ service }) {
|
|||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
const { widget } = service;
|
const { widget } = service;
|
||||||
|
|
||||||
const { data: libraryData, error: libraryError } = useWidgetAPI(widget, "libraries");
|
const { data: komgaData, error: komgaError } = useWidgetAPI(widget);
|
||||||
const { data: seriesData, error: seriesError } = useWidgetAPI(widget, "series");
|
|
||||||
const { data: bookData, error: bookError } = useWidgetAPI(widget, "books");
|
|
||||||
|
|
||||||
if (libraryError || seriesError || bookError) {
|
if (komgaError) {
|
||||||
const finalError = libraryError ?? seriesError ?? bookError;
|
return <Container service={service} error={komgaError} />;
|
||||||
return <Container service={service} error={finalError} />;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!libraryData || !seriesData || !bookData) {
|
if (!komgaData) {
|
||||||
return (
|
return (
|
||||||
<Container service={service}>
|
<Container service={service}>
|
||||||
<Block label="komga.libraries" />
|
<Block label="komga.libraries" />
|
||||||
@ -27,9 +24,11 @@ export default function Component({ service }) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const { libraries: libraryData, series: seriesData, books: bookData } = komgaData;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container service={service}>
|
<Container service={service}>
|
||||||
<Block label="komga.libraries" value={t("common.number", { value: libraryData.total })} />
|
<Block label="komga.libraries" value={t("common.number", { value: libraryData.length })} />
|
||||||
<Block label="komga.series" value={t("common.number", { value: seriesData.totalElements })} />
|
<Block label="komga.series" value={t("common.number", { value: seriesData.totalElements })} />
|
||||||
<Block label="komga.books" value={t("common.number", { value: bookData.totalElements })} />
|
<Block label="komga.books" value={t("common.number", { value: bookData.totalElements })} />
|
||||||
</Container>
|
</Container>
|
||||||
|
86
src/widgets/komga/proxy.js
Normal file
86
src/widgets/komga/proxy.js
Normal file
@ -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" });
|
||||||
|
}
|
@ -1,25 +1,31 @@
|
|||||||
import genericProxyHandler from "utils/proxy/handlers/generic";
|
import komgaProxyHandler from "./proxy";
|
||||||
import { jsonArrayFilter } from "utils/proxy/api-helpers";
|
|
||||||
|
|
||||||
const widget = {
|
const widget = {
|
||||||
api: "{url}/api/v1/{endpoint}",
|
api: "{url}/api/v1/{endpoint}",
|
||||||
proxyHandler: genericProxyHandler,
|
proxyHandler: komgaProxyHandler,
|
||||||
|
|
||||||
mappings: {
|
mappings: {
|
||||||
libraries: {
|
libraries: {
|
||||||
endpoint: "libraries",
|
endpoint: "libraries",
|
||||||
map: (data) => ({
|
|
||||||
total: jsonArrayFilter(data, (item) => !item.unavailable).length,
|
|
||||||
}),
|
|
||||||
},
|
},
|
||||||
series: {
|
series: {
|
||||||
endpoint: "series",
|
endpoint: "series",
|
||||||
validate: ["totalElements"],
|
validate: ["totalElements"],
|
||||||
},
|
},
|
||||||
|
seriesv2: {
|
||||||
|
endpoint: "series/list",
|
||||||
|
method: "POST",
|
||||||
|
validate: ["totalElements"],
|
||||||
|
},
|
||||||
books: {
|
books: {
|
||||||
endpoint: "books",
|
endpoint: "books",
|
||||||
validate: ["totalElements"],
|
validate: ["totalElements"],
|
||||||
},
|
},
|
||||||
|
booksv2: {
|
||||||
|
endpoint: "books/list",
|
||||||
|
method: "POST",
|
||||||
|
validate: ["totalElements"],
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user