Enhancement: multiple widgets per service (#4338)

pull/4337/head^2
shamoon 3 days ago
parent 385511f773
commit 907abee1aa
No known key found for this signature in database

@ -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

@ -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.

@ -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
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

@ -154,7 +154,9 @@ export default function Item({ service, group, useEqualHeights }) {
</div>
)}
{service.widget && <Widget service={service} />}
{service.widgets.map((widget) => (
<Widget widget={widget} service={service} key={widget.index} />
))}
</div>
</li>
);

@ -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 (
<ErrorBoundary>
<ServiceWidget service={service} />
<ServiceWidget service={fullService} />
</ErrorBoundary>
);
}
return (
<div className="bg-theme-200/50 dark:bg-theme-900/20 rounded m-1 flex-1 flex flex-col items-center justify-center p-1 service-missing">
<div className="font-thin text-sm">{t("widget.missing_type", { type: service.widget.type })}</div>
<div className="font-thin text-sm">{t("widget.missing_type", { type: widget.type })}</div>
</div>
);
}

@ -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" });
}

@ -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;
}

@ -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);

@ -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" });

@ -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" });

@ -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);

@ -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) {

@ -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);

@ -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" });

@ -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) {

@ -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" });

@ -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);

@ -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);

@ -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" });

@ -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" } });

@ -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 {

@ -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" });

@ -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" });

@ -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);

@ -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" });

@ -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;

@ -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" });

@ -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);

@ -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" });

@ -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;

@ -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);

@ -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);

@ -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);

@ -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" });

@ -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);

@ -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 }));

@ -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);

@ -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" });

@ -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;

@ -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);

@ -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);

@ -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);

@ -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);

@ -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,

@ -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);

@ -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" });

Loading…
Cancel
Save