diff --git a/docs/configs/docker.md b/docs/configs/docker.md index 51f6b5238..7cea1fdce 100644 --- a/docs/configs/docker.md +++ b/docs/configs/docker.md @@ -153,6 +153,18 @@ labels: - homepage.widget.fields=["field1","field2"] # optional ``` +Multiple widgets can be specified by incrementing the index, e.g. + +```yaml +labels: ... + - homepage.widget[0].type=emby + - homepage.widget[0].url=http://emby.home + - homepage.widget[0].key=yourembyapikeyhere + - homepage.widget[1].type=uptimekuma + - homepage.widget[1].url=http://uptimekuma.home + - homepage.widget[1].slug=youreventslughere +``` + You can add specify fields for e.g. the [CustomAPI](../widgets/services/customapi.md) widget by using array-style dot notation: ```yaml diff --git a/docs/configs/service-widgets.md b/docs/configs/service-widgets.md index 9c54964ec..df696f618 100644 --- a/docs/configs/service-widgets.md +++ b/docs/configs/service-widgets.md @@ -5,7 +5,7 @@ description: Service Widget Configuration Unless otherwise noted, URLs should not end with a `/` or other API path. Each widget will handle the path on its own. -Each service can have one widget attached to it (often matching the service type, but that's not forced). +Each service can have widgets attached to it (often matching the service type, but that's not forced). In addition to the href of the service, you can also specify the target location in which to open that link. See [Link Target](settings.md#link-target) for more details. @@ -22,6 +22,24 @@ Using Emby as an example, this is how you would attach the Emby service widget. key: apikeyapikeyapikeyapikeyapikey ``` +## Multiple Widgets + +Each service can have multiple widgets attached to it, for example: + +```yaml +- Emby: + icon: emby.png + href: http://emby.host.or.ip/ + description: Movies & TV Shows + widgets: + - type: emby + url: http://emby.host.or.ip + key: apikeyapikeyapikeyapikeyapikey + - type: uptimekuma + url: http://uptimekuma.host.or.ip:port + slug: statuspageslug +``` + ## Field Visibility Each widget can optionally provide a list of which fields should be visible via the `fields` widget property. If no fields are specified, then all fields will be displayed. The `fields` property must be a valid YAML array of strings. As an example, here is the entry for Sonarr showing only a couple of fields. diff --git a/docs/widgets/index.md b/docs/widgets/index.md index 8b81ee402..4bd45af71 100644 --- a/docs/widgets/index.md +++ b/docs/widgets/index.md @@ -19,10 +19,13 @@ Service widgets are used to display the status of a service, often a web service description: Watch movies and TV shows. server: localhost container: plex - widget: - type: tautulli - url: http://172.16.1.1:8181 - key: aabbccddeeffgghhiijjkkllmmnnoo + widgets: + - type: tautulli + url: http://172.16.1.1:8181 + key: aabbccddeeffgghhiijjkkllmmnnoo + - type: uptimekuma + url: http://172.16.1.2:8080 + slug: aaaaaaabbbbb ``` ## Info Widgets diff --git a/src/components/services/item.jsx b/src/components/services/item.jsx index a38dfaa3a..54560d6f2 100644 --- a/src/components/services/item.jsx +++ b/src/components/services/item.jsx @@ -154,7 +154,9 @@ export default function Item({ service, group, useEqualHeights }) { )} - {service.widget && } + {service.widgets.map((widget) => ( + + ))} ); diff --git a/src/components/services/widget.jsx b/src/components/services/widget.jsx index 292b2b1c5..61a21a663 100644 --- a/src/components/services/widget.jsx +++ b/src/components/services/widget.jsx @@ -3,22 +3,24 @@ import { useTranslation } from "next-i18next"; import ErrorBoundary from "components/errorboundry"; import components from "widgets/components"; -export default function Widget({ service }) { +export default function Widget({ widget, service }) { const { t } = useTranslation("common"); - const ServiceWidget = components[service.widget.type]; + const ServiceWidget = components[widget.type]; + const fullService = Object.apply({}, service); + fullService.widget = widget; if (ServiceWidget) { return ( - + ); } return (
-
{t("widget.missing_type", { type: service.widget.type })}
+
{t("widget.missing_type", { type: widget.type })}
); } diff --git a/src/pages/api/services/proxy.js b/src/pages/api/services/proxy.js index 90280c3dd..3f8adc883 100644 --- a/src/pages/api/services/proxy.js +++ b/src/pages/api/services/proxy.js @@ -9,8 +9,8 @@ const logger = createLogger("servicesProxy"); export default async function handler(req, res) { try { - const { service, group } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { service, group, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); let type = serviceWidget?.type; // exceptions @@ -41,7 +41,7 @@ export default async function handler(req, res) { const endpoint = mapping?.endpoint; const endpointProxy = mapping?.proxyHandler || serviceProxyHandler; - if (mapping.method && mapping.method !== req.method) { + if (mapping?.method && mapping.method !== req.method) { logger.debug("Unsupported method: %s", req.method); return res.status(403).json({ error: "Unsupported method" }); } diff --git a/src/utils/config/service-helpers.js b/src/utils/config/service-helpers.js index ea82c7352..e6ef6173e 100644 --- a/src/utils/config/service-helpers.js +++ b/src/utils/config/service-helpers.js @@ -354,8 +354,12 @@ export function cleanServiceGroups(groups) { if (typeof cleanedService.weight !== "number") { cleanedService.weight = 0; } - + if (!cleanedService.widgets) cleanedService.widgets = []; if (cleanedService.widget) { + cleanedService.widgets.push(cleanedService.widget); + delete cleanedService.widget; + } + cleanedService.widgets = cleanedService.widgets.map((widgetData, index) => { // whitelisted set of keys to pass to the frontend // alphabetical, grouped by widget(s) const { @@ -495,7 +499,7 @@ export function cleanServiceGroups(groups) { // spoolman spoolIds, - } = cleanedService.widget; + } = widgetData; let fieldsList = fields; if (typeof fields === "string") { @@ -507,160 +511,160 @@ export function cleanServiceGroups(groups) { } } - cleanedService.widget = { + const widget = { type, fields: fieldsList || null, hide_errors: hideErrors || false, service_name: service.name, service_group: serviceGroup.name, + index, }; if (type === "azuredevops") { - if (userEmail) cleanedService.widget.userEmail = userEmail; - if (repositoryId) cleanedService.widget.repositoryId = repositoryId; + if (userEmail) widget.userEmail = userEmail; + if (repositoryId) widget.repositoryId = repositoryId; } if (type === "beszel") { - if (systemId) cleanedService.widget.systemId = systemId; + if (systemId) widget.systemId = systemId; } if (type === "coinmarketcap") { - if (currency) cleanedService.widget.currency = currency; - if (symbols) cleanedService.widget.symbols = symbols; - if (slugs) cleanedService.widget.slugs = slugs; - if (defaultinterval) cleanedService.widget.defaultinterval = defaultinterval; + if (currency) widget.currency = currency; + if (symbols) widget.symbols = symbols; + if (slugs) widget.slugs = slugs; + if (defaultinterval) widget.defaultinterval = defaultinterval; } if (type === "docker") { - if (server) cleanedService.widget.server = server; - if (container) cleanedService.widget.container = container; + if (server) widget.server = server; + if (container) widget.container = container; } if (type === "unifi") { - if (site) cleanedService.widget.site = site; + if (site) widget.site = site; } if (type === "proxmox") { - if (node) cleanedService.widget.node = node; + if (node) widget.node = node; } if (type === "kubernetes") { - if (namespace) cleanedService.widget.namespace = namespace; - if (app) cleanedService.widget.app = app; - if (podSelector) cleanedService.widget.podSelector = podSelector; + if (namespace) widget.namespace = namespace; + if (app) widget.app = app; + if (podSelector) widget.podSelector = podSelector; } if (type === "iframe") { - if (src) cleanedService.widget.src = src; - if (classes) cleanedService.widget.classes = classes; - if (referrerPolicy) cleanedService.widget.referrerPolicy = referrerPolicy; - if (allowPolicy) cleanedService.widget.allowPolicy = allowPolicy; - if (allowFullscreen) cleanedService.widget.allowFullscreen = allowFullscreen; - if (loadingStrategy) cleanedService.widget.loadingStrategy = loadingStrategy; - if (allowScrolling) cleanedService.widget.allowScrolling = allowScrolling; - if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; + if (src) widget.src = src; + if (classes) widget.classes = classes; + if (referrerPolicy) widget.referrerPolicy = referrerPolicy; + if (allowPolicy) widget.allowPolicy = allowPolicy; + if (allowFullscreen) widget.allowFullscreen = allowFullscreen; + if (loadingStrategy) widget.loadingStrategy = loadingStrategy; + if (allowScrolling) widget.allowScrolling = allowScrolling; + if (refreshInterval) widget.refreshInterval = refreshInterval; } if (["opnsense", "pfsense"].includes(type)) { - if (wan) cleanedService.widget.wan = wan; + if (wan) widget.wan = wan; } if (["emby", "jellyfin"].includes(type)) { - if (enableBlocks !== undefined) cleanedService.widget.enableBlocks = JSON.parse(enableBlocks); - if (enableNowPlaying !== undefined) cleanedService.widget.enableNowPlaying = JSON.parse(enableNowPlaying); + if (enableBlocks !== undefined) widget.enableBlocks = JSON.parse(enableBlocks); + if (enableNowPlaying !== undefined) widget.enableNowPlaying = JSON.parse(enableNowPlaying); } if (["emby", "jellyfin", "tautulli"].includes(type)) { if (expandOneStreamToTwoRows !== undefined) - cleanedService.widget.expandOneStreamToTwoRows = !!JSON.parse(expandOneStreamToTwoRows); - if (showEpisodeNumber !== undefined) - cleanedService.widget.showEpisodeNumber = !!JSON.parse(showEpisodeNumber); - if (enableUser !== undefined) cleanedService.widget.enableUser = !!JSON.parse(enableUser); + widget.expandOneStreamToTwoRows = !!JSON.parse(expandOneStreamToTwoRows); + if (showEpisodeNumber !== undefined) widget.showEpisodeNumber = !!JSON.parse(showEpisodeNumber); + if (enableUser !== undefined) widget.enableUser = !!JSON.parse(enableUser); } if (["sonarr", "radarr"].includes(type)) { - if (enableQueue !== undefined) cleanedService.widget.enableQueue = JSON.parse(enableQueue); + if (enableQueue !== undefined) widget.enableQueue = JSON.parse(enableQueue); } if (type === "truenas") { - if (enablePools !== undefined) cleanedService.widget.enablePools = JSON.parse(enablePools); - if (nasType !== undefined) cleanedService.widget.nasType = nasType; + if (enablePools !== undefined) widget.enablePools = JSON.parse(enablePools); + if (nasType !== undefined) widget.nasType = nasType; } if (["diskstation", "qnap"].includes(type)) { - if (volume) cleanedService.widget.volume = volume; + if (volume) widget.volume = volume; } if (type === "kopia") { - if (snapshotHost) cleanedService.widget.snapshotHost = snapshotHost; - if (snapshotPath) cleanedService.widget.snapshotPath = snapshotPath; + if (snapshotHost) widget.snapshotHost = snapshotHost; + if (snapshotPath) widget.snapshotPath = snapshotPath; } if (["glances", "immich", "mealie", "pfsense", "pihole"].includes(type)) { - if (version) cleanedService.widget.version = parseInt(version, 10); + if (version) widget.version = parseInt(version, 10); } if (type === "glances") { - if (metric) cleanedService.widget.metric = metric; + if (metric) widget.metric = metric; if (chart !== undefined) { - cleanedService.widget.chart = chart; + widget.chart = chart; } else { - cleanedService.widget.chart = true; + widget.chart = true; } - if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; - if (pointsLimit) cleanedService.widget.pointsLimit = pointsLimit; - if (diskUnits) cleanedService.widget.diskUnits = diskUnits; + if (refreshInterval) widget.refreshInterval = refreshInterval; + if (pointsLimit) widget.pointsLimit = pointsLimit; + if (diskUnits) widget.diskUnits = diskUnits; } if (type === "mjpeg") { - if (stream) cleanedService.widget.stream = stream; - if (fit) cleanedService.widget.fit = fit; + if (stream) widget.stream = stream; + if (fit) widget.fit = fit; } if (type === "openmediavault") { - if (method) cleanedService.widget.method = method; + if (method) widget.method = method; } if (type === "openwrt") { - if (interfaceName) cleanedService.widget.interfaceName = interfaceName; + if (interfaceName) widget.interfaceName = interfaceName; } if (type === "customapi") { - if (mappings) cleanedService.widget.mappings = mappings; - if (display) cleanedService.widget.display = display; - if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; + if (mappings) widget.mappings = mappings; + if (display) widget.display = display; + if (refreshInterval) widget.refreshInterval = refreshInterval; } if (type === "calendar") { - if (integrations) cleanedService.widget.integrations = integrations; - if (firstDayInWeek) cleanedService.widget.firstDayInWeek = firstDayInWeek; - if (view) cleanedService.widget.view = view; - if (maxEvents) cleanedService.widget.maxEvents = maxEvents; - if (previousDays) cleanedService.widget.previousDays = previousDays; - if (showTime) cleanedService.widget.showTime = showTime; - if (timezone) cleanedService.widget.timezone = timezone; + if (integrations) widget.integrations = integrations; + if (firstDayInWeek) widget.firstDayInWeek = firstDayInWeek; + if (view) widget.view = view; + if (maxEvents) widget.maxEvents = maxEvents; + if (previousDays) widget.previousDays = previousDays; + if (showTime) widget.showTime = showTime; + if (timezone) widget.timezone = timezone; } if (type === "hdhomerun") { - if (tuner !== undefined) cleanedService.widget.tuner = tuner; + if (tuner !== undefined) widget.tuner = tuner; } if (type === "healthchecks") { - if (uuid !== undefined) cleanedService.widget.uuid = uuid; + if (uuid !== undefined) widget.uuid = uuid; } if (type === "speedtest") { if (bitratePrecision !== undefined) { - cleanedService.widget.bitratePrecision = parseInt(bitratePrecision, 10); + widget.bitratePrecision = parseInt(bitratePrecision, 10); } } if (type === "stocks") { - if (watchlist) cleanedService.widget.watchlist = watchlist; - if (showUSMarketStatus) cleanedService.widget.showUSMarketStatus = showUSMarketStatus; + if (watchlist) widget.watchlist = watchlist; + if (showUSMarketStatus) widget.showUSMarketStatus = showUSMarketStatus; } if (type === "wgeasy") { - if (threshold !== undefined) cleanedService.widget.threshold = parseInt(threshold, 10); + if (threshold !== undefined) widget.threshold = parseInt(threshold, 10); } if (type === "frigate") { - if (enableRecentEvents !== undefined) cleanedService.widget.enableRecentEvents = enableRecentEvents; + if (enableRecentEvents !== undefined) widget.enableRecentEvents = enableRecentEvents; } if (type === "technitium") { - if (range !== undefined) cleanedService.widget.range = range; + if (range !== undefined) widget.range = range; } if (type === "lubelogger") { - if (vehicleID !== undefined) cleanedService.widget.vehicleID = parseInt(vehicleID, 10); + if (vehicleID !== undefined) widget.vehicleID = parseInt(vehicleID, 10); } if (type === "vikunja") { - if (enableTaskList !== undefined) cleanedService.widget.enableTaskList = !!enableTaskList; + if (enableTaskList !== undefined) widget.enableTaskList = !!enableTaskList; } if (type === "prometheusmetric") { - if (metrics) cleanedService.widget.metrics = metrics; - if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; + if (metrics) widget.metrics = metrics; + if (refreshInterval) widget.refreshInterval = refreshInterval; } if (type === "spoolman") { - if (spoolIds !== undefined) cleanedService.widget.spoolIds = spoolIds; + if (spoolIds !== undefined) widget.spoolIds = spoolIds; } - } - + return widget; + }); return cleanedService; }), })); @@ -693,12 +697,11 @@ export async function getServiceItem(group, service) { return false; } -export default async function getServiceWidget(group, service) { +export default async function getServiceWidget(group, service, index) { const serviceItem = await getServiceItem(group, service); if (serviceItem) { - const { widget } = serviceItem; - return widget; + const { widget, widgets } = serviceItem; + return index > -1 && widgets ? widgets[index] : widget; } - return false; } diff --git a/src/utils/proxy/api-helpers.js b/src/utils/proxy/api-helpers.js index 8e0682dbc..a02ea6233 100644 --- a/src/utils/proxy/api-helpers.js +++ b/src/utils/proxy/api-helpers.js @@ -12,6 +12,7 @@ export function getURLSearchParams(widget, endpoint) { const params = new URLSearchParams({ group: widget.service_group, service: widget.service_name, + index: widget.index, }); if (endpoint) { params.append("endpoint", endpoint); diff --git a/src/utils/proxy/handlers/credentialed.js b/src/utils/proxy/handlers/credentialed.js index cbe0422ae..cea95196a 100644 --- a/src/utils/proxy/handlers/credentialed.js +++ b/src/utils/proxy/handlers/credentialed.js @@ -9,10 +9,10 @@ import widgets from "widgets/widgets"; const logger = createLogger("credentialedProxyHandler"); export default async function credentialedProxyHandler(req, res, map) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(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" }); diff --git a/src/utils/proxy/handlers/generic.js b/src/utils/proxy/handlers/generic.js index c6b9236b3..2e788a98c 100644 --- a/src/utils/proxy/handlers/generic.js +++ b/src/utils/proxy/handlers/generic.js @@ -8,10 +8,10 @@ import widgets from "widgets/widgets"; const logger = createLogger("genericProxyHandler"); export default async function genericProxyHandler(req, res, map) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(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" }); diff --git a/src/utils/proxy/handlers/jsonrpc.js b/src/utils/proxy/handlers/jsonrpc.js index 3974dbdc3..f9fb18832 100644 --- a/src/utils/proxy/handlers/jsonrpc.js +++ b/src/utils/proxy/handlers/jsonrpc.js @@ -65,10 +65,10 @@ export async function sendJsonRpcRequest(url, method, params, widget) { } export default async function jsonrpcProxyHandler(req, res) { - const { group, service, endpoint: method } = req.query; + const { group, service, endpoint: method, index } = req.query; if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); const api = widgets?.[widget.type]?.api; const [, mapping] = Object.entries(widgets?.[widget.type]?.mappings).find(([, value]) => value.endpoint === method); diff --git a/src/utils/proxy/handlers/synology.js b/src/utils/proxy/handlers/synology.js index be44e810a..030e53bad 100644 --- a/src/utils/proxy/handlers/synology.js +++ b/src/utils/proxy/handlers/synology.js @@ -131,13 +131,13 @@ function toError(url, synologyError) { } export default async function synologyProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { return res.status(400).json({ error: "Invalid proxy service type" }); } - const serviceWidget = await getServiceWidget(group, service); + const serviceWidget = await getServiceWidget(group, service, index); const widget = widgets?.[serviceWidget.type]; const mapping = widget?.mappings?.[endpoint]; if (!widget.api || !mapping) { diff --git a/src/widgets/audiobookshelf/proxy.js b/src/widgets/audiobookshelf/proxy.js index 9701c1feb..1a89736b1 100644 --- a/src/widgets/audiobookshelf/proxy.js +++ b/src/widgets/audiobookshelf/proxy.js @@ -23,14 +23,14 @@ async function retrieveFromAPI(url, key) { } export default async function audiobookshelfProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/beszel/proxy.js b/src/widgets/beszel/proxy.js index 04083e420..61bc969b4 100644 --- a/src/widgets/beszel/proxy.js +++ b/src/widgets/beszel/proxy.js @@ -34,10 +34,10 @@ async function login(loginUrl, username, password, service) { } export default async function beszelProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(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" }); diff --git a/src/widgets/calendar/proxy.js b/src/widgets/calendar/proxy.js index cf754424f..d36f30c90 100644 --- a/src/widgets/calendar/proxy.js +++ b/src/widgets/calendar/proxy.js @@ -5,10 +5,10 @@ import createLogger from "utils/logger"; const logger = createLogger("calendarProxyHandler"); export default async function calendarProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); const integration = widget.integrations?.find((i) => i.name === endpoint); if (integration) { diff --git a/src/widgets/crowdsec/proxy.js b/src/widgets/crowdsec/proxy.js index e78fbc5ef..85803845b 100644 --- a/src/widgets/crowdsec/proxy.js +++ b/src/widgets/crowdsec/proxy.js @@ -35,14 +35,14 @@ async function login(widget, service) { } export default async function crowdsecProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = 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); + const widget = await getServiceWidget(group, service, index); 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" }); diff --git a/src/widgets/deluge/proxy.js b/src/widgets/deluge/proxy.js index b86873a89..613296978 100644 --- a/src/widgets/deluge/proxy.js +++ b/src/widgets/deluge/proxy.js @@ -40,14 +40,14 @@ function login(url, password) { } export default async function delugeProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/flood/proxy.js b/src/widgets/flood/proxy.js index 3345ad7b2..e0c101731 100644 --- a/src/widgets/flood/proxy.js +++ b/src/widgets/flood/proxy.js @@ -28,14 +28,14 @@ async function login(widget) { } export default async function floodProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/freshrss/proxy.js b/src/widgets/freshrss/proxy.js index c08e5c871..881094bd7 100644 --- a/src/widgets/freshrss/proxy.js +++ b/src/widgets/freshrss/proxy.js @@ -74,14 +74,14 @@ async function apiCall(widget, endpoint, service) { } export default async function freshrssProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/fritzbox/proxy.js b/src/widgets/fritzbox/proxy.js index a0a22d8b5..d1a66d979 100644 --- a/src/widgets/fritzbox/proxy.js +++ b/src/widgets/fritzbox/proxy.js @@ -46,8 +46,8 @@ async function requestEndpoint(apiBaseUrl, service, action) { } export default async function fritzboxProxyHandler(req, res) { - const { group, service } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { group, service, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); if (!serviceWidget) { res.status(500).json({ error: { message: "Service widget not found" } }); diff --git a/src/widgets/gamedig/proxy.js b/src/widgets/gamedig/proxy.js index 05fa615c1..ecf6e4c68 100644 --- a/src/widgets/gamedig/proxy.js +++ b/src/widgets/gamedig/proxy.js @@ -7,8 +7,8 @@ const proxyName = "gamedigProxyHandler"; const logger = createLogger(proxyName); export default async function gamedigProxyHandler(req, res) { - const { group, service } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { group, service, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); const url = new URL(serviceWidget.url); try { diff --git a/src/widgets/homeassistant/proxy.js b/src/widgets/homeassistant/proxy.js index fe488f86b..e1f02ddbb 100644 --- a/src/widgets/homeassistant/proxy.js +++ b/src/widgets/homeassistant/proxy.js @@ -62,14 +62,14 @@ async function getQuery(query, { url, key }) { } export default async function homeassistantProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/homebox/proxy.js b/src/widgets/homebox/proxy.js index 0d6fdf13c..c91ce5528 100644 --- a/src/widgets/homebox/proxy.js +++ b/src/widgets/homebox/proxy.js @@ -68,14 +68,14 @@ async function apiCall(widget, endpoint, service) { } export default async function homeboxProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/homebridge/proxy.js b/src/widgets/homebridge/proxy.js index 17dc8635c..4da9197b0 100644 --- a/src/widgets/homebridge/proxy.js +++ b/src/widgets/homebridge/proxy.js @@ -71,14 +71,14 @@ async function apiCall(widget, endpoint, service) { } export default async function homebridgeProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/jackett/proxy.js b/src/widgets/jackett/proxy.js index 5292695fc..035309b36 100644 --- a/src/widgets/jackett/proxy.js +++ b/src/widgets/jackett/proxy.js @@ -25,14 +25,14 @@ async function fetchJackettCookie(widget, loginURL) { } export default async function jackettProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = 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); + const widget = await getServiceWidget(group, service, index); 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" }); diff --git a/src/widgets/jdownloader/proxy.js b/src/widgets/jdownloader/proxy.js index 88a92d95d..ae8c845ca 100644 --- a/src/widgets/jdownloader/proxy.js +++ b/src/widgets/jdownloader/proxy.js @@ -12,12 +12,12 @@ const proxyName = "jdownloaderProxyHandler"; const logger = createLogger(proxyName); async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return null; } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return null; diff --git a/src/widgets/kavita/proxy.js b/src/widgets/kavita/proxy.js index b8e9813f8..1c41c45f1 100644 --- a/src/widgets/kavita/proxy.js +++ b/src/widgets/kavita/proxy.js @@ -70,14 +70,14 @@ async function apiCall(widget, endpoint, service) { } export default async function KavitaProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/minecraft/proxy.js b/src/widgets/minecraft/proxy.js index f7bac9d45..98d1be88c 100644 --- a/src/widgets/minecraft/proxy.js +++ b/src/widgets/minecraft/proxy.js @@ -7,8 +7,8 @@ const proxyName = "minecraftProxyHandler"; const logger = createLogger(proxyName); export default async function minecraftProxyHandler(req, res) { - const { group, service } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { group, service, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); const url = new URL(serviceWidget.url); try { const pingResponse = await pingWithPromise(url.hostname, url.port || 25565); diff --git a/src/widgets/npm/proxy.js b/src/widgets/npm/proxy.js index 978254f81..6c7ba09ef 100644 --- a/src/widgets/npm/proxy.js +++ b/src/widgets/npm/proxy.js @@ -36,10 +36,10 @@ async function login(loginUrl, username, password, service) { } export default async function npmProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (group && service) { - const widget = await getServiceWidget(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" }); diff --git a/src/widgets/omada/proxy.js b/src/widgets/omada/proxy.js index 8e8994a5b..f4da12936 100644 --- a/src/widgets/omada/proxy.js +++ b/src/widgets/omada/proxy.js @@ -33,10 +33,10 @@ async function login(loginUrl, username, password, controllerVersionMajor) { } export default async function omadaProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (widget) { const { url } = widget; diff --git a/src/widgets/openmediavault/proxy.js b/src/widgets/openmediavault/proxy.js index e1f97a56e..9cda42e86 100644 --- a/src/widgets/openmediavault/proxy.js +++ b/src/widgets/openmediavault/proxy.js @@ -12,14 +12,14 @@ const BG_POLL_PERIOD = 500; const logger = createLogger(PROXY_NAME); async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return null; } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/openwrt/proxy.js b/src/widgets/openwrt/proxy.js index 977db8ca9..0a0da3ff5 100644 --- a/src/widgets/openwrt/proxy.js +++ b/src/widgets/openwrt/proxy.js @@ -17,14 +17,14 @@ const PARAMS = { }; async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return null; } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/photoprism/proxy.js b/src/widgets/photoprism/proxy.js index 509bfa0c7..fe5096b3a 100644 --- a/src/widgets/photoprism/proxy.js +++ b/src/widgets/photoprism/proxy.js @@ -6,14 +6,14 @@ import createLogger from "utils/logger"; const logger = createLogger("photoprismProxyHandler"); export default async function photoprismProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/pihole/proxy.js b/src/widgets/pihole/proxy.js index 35873fa92..bf24624d4 100644 --- a/src/widgets/pihole/proxy.js +++ b/src/widgets/pihole/proxy.js @@ -33,7 +33,7 @@ async function login(widget, service) { } export default async function piholeProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; let endpoint = "stats/summary"; if (!group || !service) { @@ -41,7 +41,7 @@ export default async function piholeProxyHandler(req, res) { return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid widget configuration" }); diff --git a/src/widgets/plex/proxy.js b/src/widgets/plex/proxy.js index d80330654..2956f2809 100644 --- a/src/widgets/plex/proxy.js +++ b/src/widgets/plex/proxy.js @@ -16,14 +16,14 @@ const tvCacheKey = `${proxyName}__tv`; const logger = createLogger(proxyName); async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("Invalid or missing service '%s' or group '%s'", service, group); return null; } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/pyload/proxy.js b/src/widgets/pyload/proxy.js index d9469d1cb..a380c865d 100644 --- a/src/widgets/pyload/proxy.js +++ b/src/widgets/pyload/proxy.js @@ -67,11 +67,11 @@ async function login(loginUrl, service, username, password = "") { } export default async function pyloadProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; try { if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (widget) { const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget })); diff --git a/src/widgets/qbittorrent/proxy.js b/src/widgets/qbittorrent/proxy.js index e1a0f0552..aead75820 100644 --- a/src/widgets/qbittorrent/proxy.js +++ b/src/widgets/qbittorrent/proxy.js @@ -21,14 +21,14 @@ async function login(widget) { } export default async function qbittorrentProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/qnap/proxy.js b/src/widgets/qnap/proxy.js index 508c8a46f..07917d28c 100644 --- a/src/widgets/qnap/proxy.js +++ b/src/widgets/qnap/proxy.js @@ -77,14 +77,14 @@ async function apiCall(widget, endpoint, service) { } export default async function qnapProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); return res.status(400).json({ error: "Invalid proxy service type" }); diff --git a/src/widgets/rutorrent/proxy.js b/src/widgets/rutorrent/proxy.js index 47c761916..e0ae44fec 100644 --- a/src/widgets/rutorrent/proxy.js +++ b/src/widgets/rutorrent/proxy.js @@ -45,10 +45,10 @@ const getTorrentInfo = (data) => ({ }); export default async function rutorrentProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (group && service) { - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); if (widget) { const api = widgets?.[widget.type]?.api; diff --git a/src/widgets/suwayomi/proxy.js b/src/widgets/suwayomi/proxy.js index d4d716752..def811ccf 100644 --- a/src/widgets/suwayomi/proxy.js +++ b/src/widgets/suwayomi/proxy.js @@ -114,14 +114,14 @@ function extractCounts(responseJSON, fields) { } export default async function suwayomiProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/tdarr/proxy.js b/src/widgets/tdarr/proxy.js index 9e26fdc06..88da30fdf 100644 --- a/src/widgets/tdarr/proxy.js +++ b/src/widgets/tdarr/proxy.js @@ -8,14 +8,14 @@ const proxyName = "tdarrProxyHandler"; const logger = createLogger(proxyName); export default async function tdarrProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/transmission/proxy.js b/src/widgets/transmission/proxy.js index 823def054..8b8049bc8 100644 --- a/src/widgets/transmission/proxy.js +++ b/src/widgets/transmission/proxy.js @@ -11,14 +11,14 @@ const headerCacheKey = `${proxyName}__headers`; const logger = createLogger(proxyName); export default async function transmissionProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/unifi/proxy.js b/src/widgets/unifi/proxy.js index 98c98f377..559065e33 100644 --- a/src/widgets/unifi/proxy.js +++ b/src/widgets/unifi/proxy.js @@ -14,13 +14,13 @@ const prefixCacheKey = `${proxyName}__prefix`; const logger = createLogger(proxyName); async function getWidget(req) { - const { group, service } = req.query; + const { group, service, index } = req.query; let widget = null; if (group === "unifi_console" && service === "unifi_console") { // info widget - const index = req.query?.query ? JSON.parse(req.query.query).index : undefined; - widget = await getPrivateWidgetOptions("unifi_console", index); + const infowidgetIndex = req.query?.query ? JSON.parse(req.query.query).index : undefined; + widget = await getPrivateWidgetOptions("unifi_console", infowidgetIndex); if (!widget) { logger.debug("Error retrieving settings for this Unifi widget"); return null; @@ -32,7 +32,7 @@ async function getWidget(req) { return null; } - widget = await getServiceWidget(group, service); + widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/urbackup/proxy.js b/src/widgets/urbackup/proxy.js index 94b8eeff6..4e7a0a8d5 100644 --- a/src/widgets/urbackup/proxy.js +++ b/src/widgets/urbackup/proxy.js @@ -3,8 +3,8 @@ import { UrbackupServer } from "urbackup-server-api"; import getServiceWidget from "utils/config/service-helpers"; export default async function urbackupProxyHandler(req, res) { - const { group, service } = req.query; - const serviceWidget = await getServiceWidget(group, service); + const { group, service, index } = req.query; + const serviceWidget = await getServiceWidget(group, service, index); const server = new UrbackupServer({ url: serviceWidget.url, diff --git a/src/widgets/watchtower/proxy.js b/src/widgets/watchtower/proxy.js index b3155a1e9..588d08ee0 100644 --- a/src/widgets/watchtower/proxy.js +++ b/src/widgets/watchtower/proxy.js @@ -8,14 +8,14 @@ const proxyName = "watchtowerProxyHandler"; const logger = createLogger(proxyName); export default async function watchtowerProxyHandler(req, res) { - const { group, service, endpoint } = req.query; + const { group, service, endpoint, index } = req.query; if (!group || !service) { logger.debug("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); + const widget = await getServiceWidget(group, service, index); if (!widget) { logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); diff --git a/src/widgets/xteve/proxy.js b/src/widgets/xteve/proxy.js index 421f2b499..453e36456 100644 --- a/src/widgets/xteve/proxy.js +++ b/src/widgets/xteve/proxy.js @@ -7,13 +7,13 @@ import getServiceWidget from "utils/config/service-helpers"; const logger = createLogger("xteveProxyHandler"); export default async function xteveProxyHandler(req, res) { - const { group, service } = req.query; + const { group, service, index } = req.query; if (!group || !service) { return res.status(400).json({ error: "Invalid proxy service type" }); } - const widget = await getServiceWidget(group, service); + const widget = await getServiceWidget(group, service, index); const api = widgets?.[widget.type]?.api; if (!api) { return res.status(403).json({ error: "Service does not support API calls" });