diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index 756c4e57f..added6e30 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -55,6 +55,7 @@ export default async function credentialedProxyHandler(req, res, map) { "authentik", "cloudflared", "ghostfolio", + "gitea", "headscale", "hoarder", "karakeep", @@ -143,7 +144,7 @@ export default async function credentialedProxyHandler(req, res, map) { headers["X-API-Key"] = `${widget.key}`; } - const [status, contentType, data] = await httpProxy(url, { + const [status, contentType, data, responseHeaders] = await httpProxy(url, { method: req.method, withCredentials: true, credentials: "include", @@ -178,7 +179,7 @@ export default async function credentialedProxyHandler(req, res, map) { .status(500) .json({ error: { message: "Invalid data", url: sanitizeErrorURL(url), data: resultData } }); } - if (map) resultData = map(resultData); + if (map) resultData = map(resultData, responseHeaders); } if (contentType) res.setHeader("Content-Type", contentType); diff --git a/src/utils/proxy/handlers/credentialed.test.js b/src/utils/proxy/handlers/credentialed.test.js index 206f83a44..c3e37070e 100644 --- a/src/utils/proxy/handlers/credentialed.test.js +++ b/src/utils/proxy/handlers/credentialed.test.js @@ -45,6 +45,7 @@ vi.mock("widgets/widgets", () => ({ autobrr: { api: "{url}/{endpoint}" }, jellystat: { api: "{url}/{endpoint}" }, trilium: { api: "{url}/{endpoint}" }, + gitea: { api: "{url}/api/v1/{endpoint}" }, gitlab: { api: "{url}/{endpoint}" }, azuredevops: { api: "{url}/{endpoint}" }, glances: { api: "{url}/{endpoint}" }, @@ -135,6 +136,33 @@ describe("utils/proxy/handlers/credentialed", () => { expect(res.body).toEqual({ ok: true }); }); + it("uses Bearer auth for gitea widgets and keeps the key out of the URL", async () => { + getServiceWidget.mockResolvedValue({ type: "gitea", url: "http://example", key: "token" }); + httpProxy.mockResolvedValue([200, "application/json", []]); + + const req = { method: "GET", query: { group: "g", service: "s", endpoint: "notifications?limit=1", index: 0 } }; + const res = createMockRes(); + + await credentialedProxyHandler(req, res); + + const [url, params] = httpProxy.mock.calls[0]; + expect(params.headers.Authorization).toBe("Bearer token"); + expect(url.toString()).toBe("http://example/api/v1/notifications?limit=1"); + }); + + it("passes response headers to the mapping function", async () => { + getServiceWidget.mockResolvedValue({ type: "gitea", url: "http://example", key: "token" }); + httpProxy.mockResolvedValue([200, "application/json", [], { "x-total-count": "7" }]); + + const req = { method: "GET", query: { group: "g", service: "s", endpoint: "notifications?limit=1", index: 0 } }; + const res = createMockRes(); + + await credentialedProxyHandler(req, res, (data, headers) => ({ count: Number(headers["x-total-count"]) })); + + expect(res.statusCode).toBe(200); + expect(res.body).toEqual({ count: 7 }); + }); + it("uses NC-Token auth for nextcloud widgets when key is provided", async () => { getServiceWidget.mockResolvedValue({ type: "nextcloud", url: "http://example", key: "nc-token" }); httpProxy.mockResolvedValue([200, "application/json", { ok: true }]); diff --git a/src/widgets/gitea/widget.js b/src/widgets/gitea/widget.js index 2d7261a80..71e4ca22e 100644 --- a/src/widgets/gitea/widget.js +++ b/src/widgets/gitea/widget.js @@ -1,4 +1,4 @@ -import genericProxyHandler from "utils/proxy/handlers/generic"; +import credentialedProxyHandler from "utils/proxy/handlers/credentialed"; // Get total from header to avoid pagination limit export function totalCount(data, headers) { @@ -7,8 +7,8 @@ export function totalCount(data, headers) { } const widget = { - api: "{url}/api/v1/{endpoint}?access_token={key}", - proxyHandler: genericProxyHandler, + api: "{url}/api/v1/{endpoint}", + proxyHandler: credentialedProxyHandler, mappings: { notifications: {