From c36c6a9012a49a76ed29a067ad0dfe12daa39fa4 Mon Sep 17 00:00:00 2001 From: Romloader <37718731+Romloader@users.noreply.github.com> Date: Tue, 25 Nov 2025 20:34:54 +0100 Subject: [PATCH] Enhancement: support authentication for Frigate widget (#6006) Co-authored-by: shamoon <4887959+shamoon@users.noreply.github.com> --- docs/widgets/services/frigate.md | 2 + src/widgets/frigate/proxy.js | 95 ++++++++++++++++++++++++++++++++ src/widgets/frigate/widget.js | 33 ++--------- 3 files changed, 101 insertions(+), 29 deletions(-) create mode 100644 src/widgets/frigate/proxy.js diff --git a/docs/widgets/services/frigate.md b/docs/widgets/services/frigate.md index 411c3cc79..4011ee53a 100644 --- a/docs/widgets/services/frigate.md +++ b/docs/widgets/services/frigate.md @@ -14,4 +14,6 @@ widget: type: frigate url: http://frigate.host.or.ip:port enableRecentEvents: true # Optional, defaults to false + username: username # optional + password: password # optional ``` diff --git a/src/widgets/frigate/proxy.js b/src/widgets/frigate/proxy.js new file mode 100644 index 000000000..b73f5533f --- /dev/null +++ b/src/widgets/frigate/proxy.js @@ -0,0 +1,95 @@ +import getServiceWidget from "utils/config/service-helpers"; +import createLogger from "utils/logger"; +import { asJson, formatApiCall, sanitizeErrorURL } from "utils/proxy/api-helpers"; +import { addCookieToJar } from "utils/proxy/cookie-jar"; +import { httpProxy } from "utils/proxy/http"; +import widgets from "widgets/widgets"; + +const proxyName = "frigateProxyHandler"; +const logger = createLogger(proxyName); + +export default async function frigateProxyHandler(req, res, map) { + const { group, service, endpoint, 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) { + const url = formatApiCall(widgets[widget.type].api, { endpoint, ...widget }); + + const params = { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }; + + let [status, , data] = await httpProxy(url, params); + + if (status === 401 && widget.username && widget.password) { + const loginUrl = `${widget.url}/api/login`; + logger.debug("Attempting login to Frigate at %s", loginUrl); + const [loginStatus, , , loginResponseHeaders] = await httpProxy(loginUrl, { + method: "POST", + body: JSON.stringify({ user: widget.username, password: widget.password }), + headers: { + "Content-Type": "application/json", + }, + }); + + if (loginStatus !== 200) { + logger.error("HTTP Error %d calling %s", loginStatus, sanitizeErrorURL(loginUrl)); + return res.status(status).json({ + error: { + message: `HTTP Error ${status} while trying to login to Frigate`, + url: sanitizeErrorURL(url), + }, + }); + } + + addCookieToJar(url, loginResponseHeaders); + // Retry original request with cookie set + [status, , data] = await httpProxy(url, params); + } + + if (status >= 400) { + logger.error("HTTP Error %d calling %s", status, sanitizeErrorURL(url)); + return res.status(status).json({ + error: { + message: `HTTP Error ${status} from Frigate`, + url: sanitizeErrorURL(url), + }, + }); + } + + data = asJson(data); + + if (endpoint == "stats") { + res.status(status).send({ + num_cameras: data?.cameras !== undefined ? Object.keys(data?.cameras).length : 0, + uptime: data?.service?.uptime, + version: data?.service.version, + }); + } else if (endpoint == "events") { + return res.status(status).send( + data.slice(0, 5).map((event) => ({ + id: event.id, + camera: event.camera, + label: event.label, + start_time: new Date(event.start_time * 1000), + thumbnail: event.thumbnail, + score: event.data.score, + type: event.data.type, + })), + ); + } + } + } + + logger.debug("Invalid or missing proxy service type '%s' in group '%s'", service, group); + return res.status(400).json({ error: "Invalid proxy service type" }); +} diff --git a/src/widgets/frigate/widget.js b/src/widgets/frigate/widget.js index 62327c4e9..f30292233 100644 --- a/src/widgets/frigate/widget.js +++ b/src/widgets/frigate/widget.js @@ -1,37 +1,12 @@ -import { asJson } from "utils/proxy/api-helpers"; -import genericProxyHandler from "utils/proxy/handlers/generic"; +import frigateProxyHandler from "./proxy"; const widget = { api: "{url}/api/{endpoint}", - proxyHandler: genericProxyHandler, + proxyHandler: frigateProxyHandler, mappings: { - stats: { - endpoint: "stats", - map: (data) => { - const jsonData = asJson(data); - return { - num_cameras: jsonData?.cameras !== undefined ? Object.keys(jsonData?.cameras).length : 0, - uptime: jsonData?.service?.uptime, - version: jsonData?.service.version, - }; - }, - }, - events: { - endpoint: "events", - map: (data) => - asJson(data) - .slice(0, 5) - .map((event) => ({ - id: event.id, - camera: event.camera, - label: event.label, - start_time: new Date(event.start_time * 1000), - thumbnail: event.thumbnail, - score: event.data.score, - type: event.data.type, - })), - }, + stats: { endpoint: "stats" }, + events: { endpoint: "events" }, }, };