mirror of
				https://github.com/karl0ss/homepage.git
				synced 2025-10-31 06:24:02 +00:00 
			
		
		
		
	Enhancement: multiple widgets per service (#4338)
This commit is contained in:
		
							parent
							
								
									385511f773
								
							
						
					
					
						commit
						907abee1aa
					
				| @ -153,6 +153,18 @@ labels: | |||||||
|   - homepage.widget.fields=["field1","field2"] # optional |   - 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: | You can add specify fields for e.g. the [CustomAPI](../widgets/services/customapi.md) widget by using array-style dot notation: | ||||||
| 
 | 
 | ||||||
| ```yaml | ```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. | 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. | 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 |       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 | ## 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. | 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. |     description: Watch movies and TV shows. | ||||||
|     server: localhost |     server: localhost | ||||||
|     container: plex |     container: plex | ||||||
|     widget: |     widgets: | ||||||
|       type: tautulli |       - type: tautulli | ||||||
|       url: http://172.16.1.1:8181 |         url: http://172.16.1.1:8181 | ||||||
|       key: aabbccddeeffgghhiijjkkllmmnnoo |         key: aabbccddeeffgghhiijjkkllmmnnoo | ||||||
|  |       - type: uptimekuma | ||||||
|  |         url: http://172.16.1.2:8080 | ||||||
|  |         slug: aaaaaaabbbbb | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| ## Info Widgets | ## Info Widgets | ||||||
|  | |||||||
| @ -154,7 +154,9 @@ export default function Item({ service, group, useEqualHeights }) { | |||||||
|           </div> |           </div> | ||||||
|         )} |         )} | ||||||
| 
 | 
 | ||||||
|         {service.widget && <Widget service={service} />} |         {service.widgets.map((widget) => ( | ||||||
|  |           <Widget widget={widget} service={service} key={widget.index} /> | ||||||
|  |         ))} | ||||||
|       </div> |       </div> | ||||||
|     </li> |     </li> | ||||||
|   ); |   ); | ||||||
|  | |||||||
| @ -3,22 +3,24 @@ import { useTranslation } from "next-i18next"; | |||||||
| import ErrorBoundary from "components/errorboundry"; | import ErrorBoundary from "components/errorboundry"; | ||||||
| import components from "widgets/components"; | import components from "widgets/components"; | ||||||
| 
 | 
 | ||||||
| export default function Widget({ service }) { | export default function Widget({ widget, service }) { | ||||||
|   const { t } = useTranslation("common"); |   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) { |   if (ServiceWidget) { | ||||||
|     return ( |     return ( | ||||||
|       <ErrorBoundary> |       <ErrorBoundary> | ||||||
|         <ServiceWidget service={service} /> |         <ServiceWidget service={fullService} /> | ||||||
|       </ErrorBoundary> |       </ErrorBoundary> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   return ( |   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="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> |     </div> | ||||||
|   ); |   ); | ||||||
| } | } | ||||||
|  | |||||||
| @ -9,8 +9,8 @@ const logger = createLogger("servicesProxy"); | |||||||
| 
 | 
 | ||||||
| export default async function handler(req, res) { | export default async function handler(req, res) { | ||||||
|   try { |   try { | ||||||
|     const { service, group } = req.query; |     const { service, group, index } = req.query; | ||||||
|     const serviceWidget = await getServiceWidget(group, service); |     const serviceWidget = await getServiceWidget(group, service, index); | ||||||
|     let type = serviceWidget?.type; |     let type = serviceWidget?.type; | ||||||
| 
 | 
 | ||||||
|     // exceptions
 |     // exceptions
 | ||||||
| @ -41,7 +41,7 @@ export default async function handler(req, res) { | |||||||
|         const endpoint = mapping?.endpoint; |         const endpoint = mapping?.endpoint; | ||||||
|         const endpointProxy = mapping?.proxyHandler || serviceProxyHandler; |         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); |           logger.debug("Unsupported method: %s", req.method); | ||||||
|           return res.status(403).json({ error: "Unsupported method" }); |           return res.status(403).json({ error: "Unsupported method" }); | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -354,8 +354,12 @@ export function cleanServiceGroups(groups) { | |||||||
|       if (typeof cleanedService.weight !== "number") { |       if (typeof cleanedService.weight !== "number") { | ||||||
|         cleanedService.weight = 0; |         cleanedService.weight = 0; | ||||||
|       } |       } | ||||||
| 
 |       if (!cleanedService.widgets) cleanedService.widgets = []; | ||||||
|       if (cleanedService.widget) { |       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
 |         // whitelisted set of keys to pass to the frontend
 | ||||||
|         // alphabetical, grouped by widget(s)
 |         // alphabetical, grouped by widget(s)
 | ||||||
|         const { |         const { | ||||||
| @ -495,7 +499,7 @@ export function cleanServiceGroups(groups) { | |||||||
| 
 | 
 | ||||||
|           // spoolman
 |           // spoolman
 | ||||||
|           spoolIds, |           spoolIds, | ||||||
|         } = cleanedService.widget; |         } = widgetData; | ||||||
| 
 | 
 | ||||||
|         let fieldsList = fields; |         let fieldsList = fields; | ||||||
|         if (typeof fields === "string") { |         if (typeof fields === "string") { | ||||||
| @ -507,160 +511,160 @@ export function cleanServiceGroups(groups) { | |||||||
|           } |           } | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         cleanedService.widget = { |         const widget = { | ||||||
|           type, |           type, | ||||||
|           fields: fieldsList || null, |           fields: fieldsList || null, | ||||||
|           hide_errors: hideErrors || false, |           hide_errors: hideErrors || false, | ||||||
|           service_name: service.name, |           service_name: service.name, | ||||||
|           service_group: serviceGroup.name, |           service_group: serviceGroup.name, | ||||||
|  |           index, | ||||||
|         }; |         }; | ||||||
| 
 | 
 | ||||||
|         if (type === "azuredevops") { |         if (type === "azuredevops") { | ||||||
|           if (userEmail) cleanedService.widget.userEmail = userEmail; |           if (userEmail) widget.userEmail = userEmail; | ||||||
|           if (repositoryId) cleanedService.widget.repositoryId = repositoryId; |           if (repositoryId) widget.repositoryId = repositoryId; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (type === "beszel") { |         if (type === "beszel") { | ||||||
|           if (systemId) cleanedService.widget.systemId = systemId; |           if (systemId) widget.systemId = systemId; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (type === "coinmarketcap") { |         if (type === "coinmarketcap") { | ||||||
|           if (currency) cleanedService.widget.currency = currency; |           if (currency) widget.currency = currency; | ||||||
|           if (symbols) cleanedService.widget.symbols = symbols; |           if (symbols) widget.symbols = symbols; | ||||||
|           if (slugs) cleanedService.widget.slugs = slugs; |           if (slugs) widget.slugs = slugs; | ||||||
|           if (defaultinterval) cleanedService.widget.defaultinterval = defaultinterval; |           if (defaultinterval) widget.defaultinterval = defaultinterval; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         if (type === "docker") { |         if (type === "docker") { | ||||||
|           if (server) cleanedService.widget.server = server; |           if (server) widget.server = server; | ||||||
|           if (container) cleanedService.widget.container = container; |           if (container) widget.container = container; | ||||||
|         } |         } | ||||||
|         if (type === "unifi") { |         if (type === "unifi") { | ||||||
|           if (site) cleanedService.widget.site = site; |           if (site) widget.site = site; | ||||||
|         } |         } | ||||||
|         if (type === "proxmox") { |         if (type === "proxmox") { | ||||||
|           if (node) cleanedService.widget.node = node; |           if (node) widget.node = node; | ||||||
|         } |         } | ||||||
|         if (type === "kubernetes") { |         if (type === "kubernetes") { | ||||||
|           if (namespace) cleanedService.widget.namespace = namespace; |           if (namespace) widget.namespace = namespace; | ||||||
|           if (app) cleanedService.widget.app = app; |           if (app) widget.app = app; | ||||||
|           if (podSelector) cleanedService.widget.podSelector = podSelector; |           if (podSelector) widget.podSelector = podSelector; | ||||||
|         } |         } | ||||||
|         if (type === "iframe") { |         if (type === "iframe") { | ||||||
|           if (src) cleanedService.widget.src = src; |           if (src) widget.src = src; | ||||||
|           if (classes) cleanedService.widget.classes = classes; |           if (classes) widget.classes = classes; | ||||||
|           if (referrerPolicy) cleanedService.widget.referrerPolicy = referrerPolicy; |           if (referrerPolicy) widget.referrerPolicy = referrerPolicy; | ||||||
|           if (allowPolicy) cleanedService.widget.allowPolicy = allowPolicy; |           if (allowPolicy) widget.allowPolicy = allowPolicy; | ||||||
|           if (allowFullscreen) cleanedService.widget.allowFullscreen = allowFullscreen; |           if (allowFullscreen) widget.allowFullscreen = allowFullscreen; | ||||||
|           if (loadingStrategy) cleanedService.widget.loadingStrategy = loadingStrategy; |           if (loadingStrategy) widget.loadingStrategy = loadingStrategy; | ||||||
|           if (allowScrolling) cleanedService.widget.allowScrolling = allowScrolling; |           if (allowScrolling) widget.allowScrolling = allowScrolling; | ||||||
|           if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; |           if (refreshInterval) widget.refreshInterval = refreshInterval; | ||||||
|         } |         } | ||||||
|         if (["opnsense", "pfsense"].includes(type)) { |         if (["opnsense", "pfsense"].includes(type)) { | ||||||
|           if (wan) cleanedService.widget.wan = wan; |           if (wan) widget.wan = wan; | ||||||
|         } |         } | ||||||
|         if (["emby", "jellyfin"].includes(type)) { |         if (["emby", "jellyfin"].includes(type)) { | ||||||
|           if (enableBlocks !== undefined) cleanedService.widget.enableBlocks = JSON.parse(enableBlocks); |           if (enableBlocks !== undefined) widget.enableBlocks = JSON.parse(enableBlocks); | ||||||
|           if (enableNowPlaying !== undefined) cleanedService.widget.enableNowPlaying = JSON.parse(enableNowPlaying); |           if (enableNowPlaying !== undefined) widget.enableNowPlaying = JSON.parse(enableNowPlaying); | ||||||
|         } |         } | ||||||
|         if (["emby", "jellyfin", "tautulli"].includes(type)) { |         if (["emby", "jellyfin", "tautulli"].includes(type)) { | ||||||
|           if (expandOneStreamToTwoRows !== undefined) |           if (expandOneStreamToTwoRows !== undefined) | ||||||
|             cleanedService.widget.expandOneStreamToTwoRows = !!JSON.parse(expandOneStreamToTwoRows); |             widget.expandOneStreamToTwoRows = !!JSON.parse(expandOneStreamToTwoRows); | ||||||
|           if (showEpisodeNumber !== undefined) |           if (showEpisodeNumber !== undefined) widget.showEpisodeNumber = !!JSON.parse(showEpisodeNumber); | ||||||
|             cleanedService.widget.showEpisodeNumber = !!JSON.parse(showEpisodeNumber); |           if (enableUser !== undefined) widget.enableUser = !!JSON.parse(enableUser); | ||||||
|           if (enableUser !== undefined) cleanedService.widget.enableUser = !!JSON.parse(enableUser); |  | ||||||
|         } |         } | ||||||
|         if (["sonarr", "radarr"].includes(type)) { |         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 (type === "truenas") { | ||||||
|           if (enablePools !== undefined) cleanedService.widget.enablePools = JSON.parse(enablePools); |           if (enablePools !== undefined) widget.enablePools = JSON.parse(enablePools); | ||||||
|           if (nasType !== undefined) cleanedService.widget.nasType = nasType; |           if (nasType !== undefined) widget.nasType = nasType; | ||||||
|         } |         } | ||||||
|         if (["diskstation", "qnap"].includes(type)) { |         if (["diskstation", "qnap"].includes(type)) { | ||||||
|           if (volume) cleanedService.widget.volume = volume; |           if (volume) widget.volume = volume; | ||||||
|         } |         } | ||||||
|         if (type === "kopia") { |         if (type === "kopia") { | ||||||
|           if (snapshotHost) cleanedService.widget.snapshotHost = snapshotHost; |           if (snapshotHost) widget.snapshotHost = snapshotHost; | ||||||
|           if (snapshotPath) cleanedService.widget.snapshotPath = snapshotPath; |           if (snapshotPath) widget.snapshotPath = snapshotPath; | ||||||
|         } |         } | ||||||
|         if (["glances", "immich", "mealie", "pfsense", "pihole"].includes(type)) { |         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 (type === "glances") { | ||||||
|           if (metric) cleanedService.widget.metric = metric; |           if (metric) widget.metric = metric; | ||||||
|           if (chart !== undefined) { |           if (chart !== undefined) { | ||||||
|             cleanedService.widget.chart = chart; |             widget.chart = chart; | ||||||
|           } else { |           } else { | ||||||
|             cleanedService.widget.chart = true; |             widget.chart = true; | ||||||
|           } |           } | ||||||
|           if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; |           if (refreshInterval) widget.refreshInterval = refreshInterval; | ||||||
|           if (pointsLimit) cleanedService.widget.pointsLimit = pointsLimit; |           if (pointsLimit) widget.pointsLimit = pointsLimit; | ||||||
|           if (diskUnits) cleanedService.widget.diskUnits = diskUnits; |           if (diskUnits) widget.diskUnits = diskUnits; | ||||||
|         } |         } | ||||||
|         if (type === "mjpeg") { |         if (type === "mjpeg") { | ||||||
|           if (stream) cleanedService.widget.stream = stream; |           if (stream) widget.stream = stream; | ||||||
|           if (fit) cleanedService.widget.fit = fit; |           if (fit) widget.fit = fit; | ||||||
|         } |         } | ||||||
|         if (type === "openmediavault") { |         if (type === "openmediavault") { | ||||||
|           if (method) cleanedService.widget.method = method; |           if (method) widget.method = method; | ||||||
|         } |         } | ||||||
|         if (type === "openwrt") { |         if (type === "openwrt") { | ||||||
|           if (interfaceName) cleanedService.widget.interfaceName = interfaceName; |           if (interfaceName) widget.interfaceName = interfaceName; | ||||||
|         } |         } | ||||||
|         if (type === "customapi") { |         if (type === "customapi") { | ||||||
|           if (mappings) cleanedService.widget.mappings = mappings; |           if (mappings) widget.mappings = mappings; | ||||||
|           if (display) cleanedService.widget.display = display; |           if (display) widget.display = display; | ||||||
|           if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; |           if (refreshInterval) widget.refreshInterval = refreshInterval; | ||||||
|         } |         } | ||||||
|         if (type === "calendar") { |         if (type === "calendar") { | ||||||
|           if (integrations) cleanedService.widget.integrations = integrations; |           if (integrations) widget.integrations = integrations; | ||||||
|           if (firstDayInWeek) cleanedService.widget.firstDayInWeek = firstDayInWeek; |           if (firstDayInWeek) widget.firstDayInWeek = firstDayInWeek; | ||||||
|           if (view) cleanedService.widget.view = view; |           if (view) widget.view = view; | ||||||
|           if (maxEvents) cleanedService.widget.maxEvents = maxEvents; |           if (maxEvents) widget.maxEvents = maxEvents; | ||||||
|           if (previousDays) cleanedService.widget.previousDays = previousDays; |           if (previousDays) widget.previousDays = previousDays; | ||||||
|           if (showTime) cleanedService.widget.showTime = showTime; |           if (showTime) widget.showTime = showTime; | ||||||
|           if (timezone) cleanedService.widget.timezone = timezone; |           if (timezone) widget.timezone = timezone; | ||||||
|         } |         } | ||||||
|         if (type === "hdhomerun") { |         if (type === "hdhomerun") { | ||||||
|           if (tuner !== undefined) cleanedService.widget.tuner = tuner; |           if (tuner !== undefined) widget.tuner = tuner; | ||||||
|         } |         } | ||||||
|         if (type === "healthchecks") { |         if (type === "healthchecks") { | ||||||
|           if (uuid !== undefined) cleanedService.widget.uuid = uuid; |           if (uuid !== undefined) widget.uuid = uuid; | ||||||
|         } |         } | ||||||
|         if (type === "speedtest") { |         if (type === "speedtest") { | ||||||
|           if (bitratePrecision !== undefined) { |           if (bitratePrecision !== undefined) { | ||||||
|             cleanedService.widget.bitratePrecision = parseInt(bitratePrecision, 10); |             widget.bitratePrecision = parseInt(bitratePrecision, 10); | ||||||
|           } |           } | ||||||
|         } |         } | ||||||
|         if (type === "stocks") { |         if (type === "stocks") { | ||||||
|           if (watchlist) cleanedService.widget.watchlist = watchlist; |           if (watchlist) widget.watchlist = watchlist; | ||||||
|           if (showUSMarketStatus) cleanedService.widget.showUSMarketStatus = showUSMarketStatus; |           if (showUSMarketStatus) widget.showUSMarketStatus = showUSMarketStatus; | ||||||
|         } |         } | ||||||
|         if (type === "wgeasy") { |         if (type === "wgeasy") { | ||||||
|           if (threshold !== undefined) cleanedService.widget.threshold = parseInt(threshold, 10); |           if (threshold !== undefined) widget.threshold = parseInt(threshold, 10); | ||||||
|         } |         } | ||||||
|         if (type === "frigate") { |         if (type === "frigate") { | ||||||
|           if (enableRecentEvents !== undefined) cleanedService.widget.enableRecentEvents = enableRecentEvents; |           if (enableRecentEvents !== undefined) widget.enableRecentEvents = enableRecentEvents; | ||||||
|         } |         } | ||||||
|         if (type === "technitium") { |         if (type === "technitium") { | ||||||
|           if (range !== undefined) cleanedService.widget.range = range; |           if (range !== undefined) widget.range = range; | ||||||
|         } |         } | ||||||
|         if (type === "lubelogger") { |         if (type === "lubelogger") { | ||||||
|           if (vehicleID !== undefined) cleanedService.widget.vehicleID = parseInt(vehicleID, 10); |           if (vehicleID !== undefined) widget.vehicleID = parseInt(vehicleID, 10); | ||||||
|         } |         } | ||||||
|         if (type === "vikunja") { |         if (type === "vikunja") { | ||||||
|           if (enableTaskList !== undefined) cleanedService.widget.enableTaskList = !!enableTaskList; |           if (enableTaskList !== undefined) widget.enableTaskList = !!enableTaskList; | ||||||
|         } |         } | ||||||
|         if (type === "prometheusmetric") { |         if (type === "prometheusmetric") { | ||||||
|           if (metrics) cleanedService.widget.metrics = metrics; |           if (metrics) widget.metrics = metrics; | ||||||
|           if (refreshInterval) cleanedService.widget.refreshInterval = refreshInterval; |           if (refreshInterval) widget.refreshInterval = refreshInterval; | ||||||
|         } |         } | ||||||
|         if (type === "spoolman") { |         if (type === "spoolman") { | ||||||
|           if (spoolIds !== undefined) cleanedService.widget.spoolIds = spoolIds; |           if (spoolIds !== undefined) widget.spoolIds = spoolIds; | ||||||
|         } |         } | ||||||
|       } |         return widget; | ||||||
| 
 |       }); | ||||||
|       return cleanedService; |       return cleanedService; | ||||||
|     }), |     }), | ||||||
|   })); |   })); | ||||||
| @ -693,12 +697,11 @@ export async function getServiceItem(group, service) { | |||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function getServiceWidget(group, service) { | export default async function getServiceWidget(group, service, index) { | ||||||
|   const serviceItem = await getServiceItem(group, service); |   const serviceItem = await getServiceItem(group, service); | ||||||
|   if (serviceItem) { |   if (serviceItem) { | ||||||
|     const { widget } = serviceItem; |     const { widget, widgets } = serviceItem; | ||||||
|     return widget; |     return index > -1 && widgets ? widgets[index] : widget; | ||||||
|   } |   } | ||||||
| 
 |  | ||||||
|   return false; |   return false; | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,6 +12,7 @@ export function getURLSearchParams(widget, endpoint) { | |||||||
|   const params = new URLSearchParams({ |   const params = new URLSearchParams({ | ||||||
|     group: widget.service_group, |     group: widget.service_group, | ||||||
|     service: widget.service_name, |     service: widget.service_name, | ||||||
|  |     index: widget.index, | ||||||
|   }); |   }); | ||||||
|   if (endpoint) { |   if (endpoint) { | ||||||
|     params.append("endpoint", endpoint); |     params.append("endpoint", endpoint); | ||||||
|  | |||||||
| @ -9,10 +9,10 @@ import widgets from "widgets/widgets"; | |||||||
| const logger = createLogger("credentialedProxyHandler"); | const logger = createLogger("credentialedProxyHandler"); | ||||||
| 
 | 
 | ||||||
| export default async function credentialedProxyHandler(req, res, map) { | export default async function credentialedProxyHandler(req, res, map) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (group && service) { |   if (group && service) { | ||||||
|     const widget = await getServiceWidget(group, service); |     const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|     if (!widgets?.[widget.type]?.api) { |     if (!widgets?.[widget.type]?.api) { | ||||||
|       return res.status(403).json({ error: "Service does not support API calls" }); |       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"); | const logger = createLogger("genericProxyHandler"); | ||||||
| 
 | 
 | ||||||
| export default async function genericProxyHandler(req, res, map) { | export default async function genericProxyHandler(req, res, map) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (group && service) { |   if (group && service) { | ||||||
|     const widget = await getServiceWidget(group, service); |     const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|     if (!widgets?.[widget.type]?.api) { |     if (!widgets?.[widget.type]?.api) { | ||||||
|       return res.status(403).json({ error: "Service does not support API calls" }); |       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) { | 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) { |   if (group && service) { | ||||||
|     const widget = await getServiceWidget(group, service); |     const widget = await getServiceWidget(group, service, index); | ||||||
|     const api = widgets?.[widget.type]?.api; |     const api = widgets?.[widget.type]?.api; | ||||||
| 
 | 
 | ||||||
|     const [, mapping] = Object.entries(widgets?.[widget.type]?.mappings).find(([, value]) => value.endpoint === method); |     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) { | export default async function synologyProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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 widget = widgets?.[serviceWidget.type]; | ||||||
|   const mapping = widget?.mappings?.[endpoint]; |   const mapping = widget?.mappings?.[endpoint]; | ||||||
|   if (!widget.api || !mapping) { |   if (!widget.api || !mapping) { | ||||||
|  | |||||||
| @ -23,14 +23,14 @@ async function retrieveFromAPI(url, key) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function audiobookshelfProxyHandler(req, res) { | export default async function audiobookshelfProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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) { | export default async function beszelProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (group && service) { |   if (group && service) { | ||||||
|     const widget = await getServiceWidget(group, service); |     const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|     if (!widgets?.[widget.type]?.api) { |     if (!widgets?.[widget.type]?.api) { | ||||||
|       return res.status(403).json({ error: "Service does not support API calls" }); |       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"); | const logger = createLogger("calendarProxyHandler"); | ||||||
| 
 | 
 | ||||||
| export default async function calendarProxyHandler(req, res) { | export default async function calendarProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (group && service) { |   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); |     const integration = widget.integrations?.find((i) => i.name === endpoint); | ||||||
| 
 | 
 | ||||||
|     if (integration) { |     if (integration) { | ||||||
|  | |||||||
| @ -35,14 +35,14 @@ async function login(widget, service) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function crowdsecProxyHandler(req, res) { | export default async function crowdsecProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.error("Invalid or missing service '%s' or group '%s'", service, group); |     logger.error("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget || !widgets[widget.type].api) { | ||||||
|     logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid widget configuration" }); |     return res.status(400).json({ error: "Invalid widget configuration" }); | ||||||
|  | |||||||
| @ -40,14 +40,14 @@ function login(url, password) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function delugeProxyHandler(req, res) { | export default async function delugeProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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) { | export default async function floodProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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) { | export default async function freshrssProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { | export default async function fritzboxProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
|   const serviceWidget = await getServiceWidget(group, service); |   const serviceWidget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|   if (!serviceWidget) { |   if (!serviceWidget) { | ||||||
|     res.status(500).json({ error: { message: "Service widget not found" } }); |     res.status(500).json({ error: { message: "Service widget not found" } }); | ||||||
|  | |||||||
| @ -7,8 +7,8 @@ const proxyName = "gamedigProxyHandler"; | |||||||
| const logger = createLogger(proxyName); | const logger = createLogger(proxyName); | ||||||
| 
 | 
 | ||||||
| export default async function gamedigProxyHandler(req, res) { | export default async function gamedigProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
|   const serviceWidget = await getServiceWidget(group, service); |   const serviceWidget = await getServiceWidget(group, service, index); | ||||||
|   const url = new URL(serviceWidget.url); |   const url = new URL(serviceWidget.url); | ||||||
| 
 | 
 | ||||||
|   try { |   try { | ||||||
|  | |||||||
| @ -62,14 +62,14 @@ async function getQuery(query, { url, key }) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function homeassistantProxyHandler(req, res) { | export default async function homeassistantProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { | export default async function homeboxProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { | export default async function homebridgeProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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) { | export default async function jackettProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.error("Invalid or missing service '%s' or group '%s'", service, group); |     logger.error("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget || !widgets[widget.type].api) { | ||||||
|     logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid widget configuration" }); |     return res.status(400).json({ error: "Invalid widget configuration" }); | ||||||
|  | |||||||
| @ -12,12 +12,12 @@ const proxyName = "jdownloaderProxyHandler"; | |||||||
| const logger = createLogger(proxyName); | const logger = createLogger(proxyName); | ||||||
| 
 | 
 | ||||||
| async function getWidget(req) { | async function getWidget(req) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
|   const widget = await getServiceWidget(group, service); |   const widget = await getServiceWidget(group, service, index); | ||||||
|   if (!widget) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return null; |     return null; | ||||||
|  | |||||||
| @ -70,14 +70,14 @@ async function apiCall(widget, endpoint, service) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function KavitaProxyHandler(req, res) { | export default async function KavitaProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     return res.status(400).json({ error: "Invalid proxy service type" }); | ||||||
|  | |||||||
| @ -7,8 +7,8 @@ const proxyName = "minecraftProxyHandler"; | |||||||
| const logger = createLogger(proxyName); | const logger = createLogger(proxyName); | ||||||
| 
 | 
 | ||||||
| export default async function minecraftProxyHandler(req, res) { | export default async function minecraftProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
|   const serviceWidget = await getServiceWidget(group, service); |   const serviceWidget = await getServiceWidget(group, service, index); | ||||||
|   const url = new URL(serviceWidget.url); |   const url = new URL(serviceWidget.url); | ||||||
|   try { |   try { | ||||||
|     const pingResponse = await pingWithPromise(url.hostname, url.port || 25565); |     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) { | export default async function npmProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (group && service) { |   if (group && service) { | ||||||
|     const widget = await getServiceWidget(group, service); |     const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|     if (!widgets?.[widget.type]?.api) { |     if (!widgets?.[widget.type]?.api) { | ||||||
|       return res.status(403).json({ error: "Service does not support API calls" }); |       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) { | export default async function omadaProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (group && service) { |   if (group && service) { | ||||||
|     const widget = await getServiceWidget(group, service); |     const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|     if (widget) { |     if (widget) { | ||||||
|       const { url } = widget; |       const { url } = widget; | ||||||
|  | |||||||
| @ -12,14 +12,14 @@ const BG_POLL_PERIOD = 500; | |||||||
| const logger = createLogger(PROXY_NAME); | const logger = createLogger(PROXY_NAME); | ||||||
| 
 | 
 | ||||||
| async function getWidget(req) { | async function getWidget(req) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const widget = await getServiceWidget(group, service); |   const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|   if (!widget) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|  | |||||||
| @ -17,14 +17,14 @@ const PARAMS = { | |||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| async function getWidget(req) { | async function getWidget(req) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const widget = await getServiceWidget(group, service); |   const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|   if (!widget) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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"); | const logger = createLogger("photoprismProxyHandler"); | ||||||
| 
 | 
 | ||||||
| export default async function photoprismProxyHandler(req, res) { | export default async function photoprismProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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) { | export default async function piholeProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
|   let endpoint = "stats/summary"; |   let endpoint = "stats/summary"; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
| @ -41,7 +41,7 @@ export default async function piholeProxyHandler(req, res) { | |||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.error("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid widget configuration" }); |     return res.status(400).json({ error: "Invalid widget configuration" }); | ||||||
|  | |||||||
| @ -16,14 +16,14 @@ const tvCacheKey = `${proxyName}__tv`; | |||||||
| const logger = createLogger(proxyName); | const logger = createLogger(proxyName); | ||||||
| 
 | 
 | ||||||
| async function getWidget(req) { | async function getWidget(req) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return null; |     return null; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   const widget = await getServiceWidget(group, service); |   const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|   if (!widget) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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) { | export default async function pyloadProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   try { |   try { | ||||||
|     if (group && service) { |     if (group && service) { | ||||||
|       const widget = await getServiceWidget(group, service); |       const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|       if (widget) { |       if (widget) { | ||||||
|         const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...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) { | export default async function qbittorrentProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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) { | export default async function qnapProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     return res.status(400).json({ error: "Invalid proxy service type" }); | ||||||
|  | |||||||
| @ -45,10 +45,10 @@ const getTorrentInfo = (data) => ({ | |||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| export default async function rutorrentProxyHandler(req, res) { | export default async function rutorrentProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (group && service) { |   if (group && service) { | ||||||
|     const widget = await getServiceWidget(group, service); |     const widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|     if (widget) { |     if (widget) { | ||||||
|       const api = widgets?.[widget.type]?.api; |       const api = widgets?.[widget.type]?.api; | ||||||
|  | |||||||
| @ -114,14 +114,14 @@ function extractCounts(responseJSON, fields) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default async function suwayomiProxyHandler(req, res) { | export default async function suwayomiProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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); | const logger = createLogger(proxyName); | ||||||
| 
 | 
 | ||||||
| export default async function tdarrProxyHandler(req, res) { | export default async function tdarrProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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); | const logger = createLogger(proxyName); | ||||||
| 
 | 
 | ||||||
| export default async function transmissionProxyHandler(req, res) { | export default async function transmissionProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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); | const logger = createLogger(proxyName); | ||||||
| 
 | 
 | ||||||
| async function getWidget(req) { | async function getWidget(req) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   let widget = null; |   let widget = null; | ||||||
|   if (group === "unifi_console" && service === "unifi_console") { |   if (group === "unifi_console" && service === "unifi_console") { | ||||||
|     // info widget
 |     // info widget
 | ||||||
|     const index = req.query?.query ? JSON.parse(req.query.query).index : undefined; |     const infowidgetIndex = req.query?.query ? JSON.parse(req.query.query).index : undefined; | ||||||
|     widget = await getPrivateWidgetOptions("unifi_console", index); |     widget = await getPrivateWidgetOptions("unifi_console", infowidgetIndex); | ||||||
|     if (!widget) { |     if (!widget) { | ||||||
|       logger.debug("Error retrieving settings for this Unifi widget"); |       logger.debug("Error retrieving settings for this Unifi widget"); | ||||||
|       return null; |       return null; | ||||||
| @ -32,7 +32,7 @@ async function getWidget(req) { | |||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     widget = await getServiceWidget(group, service); |     widget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|     if (!widget) { |     if (!widget) { | ||||||
|       logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |       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"; | import getServiceWidget from "utils/config/service-helpers"; | ||||||
| 
 | 
 | ||||||
| export default async function urbackupProxyHandler(req, res) { | export default async function urbackupProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
|   const serviceWidget = await getServiceWidget(group, service); |   const serviceWidget = await getServiceWidget(group, service, index); | ||||||
| 
 | 
 | ||||||
|   const server = new UrbackupServer({ |   const server = new UrbackupServer({ | ||||||
|     url: serviceWidget.url, |     url: serviceWidget.url, | ||||||
|  | |||||||
| @ -8,14 +8,14 @@ const proxyName = "watchtowerProxyHandler"; | |||||||
| const logger = createLogger(proxyName); | const logger = createLogger(proxyName); | ||||||
| 
 | 
 | ||||||
| export default async function watchtowerProxyHandler(req, res) { | export default async function watchtowerProxyHandler(req, res) { | ||||||
|   const { group, service, endpoint } = req.query; |   const { group, service, endpoint, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); |     logger.debug("Invalid or missing service '%s' or group '%s'", service, group); | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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) { |   if (!widget) { | ||||||
|     logger.debug("Invalid or missing widget for service '%s' in group '%s'", service, group); |     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"); | const logger = createLogger("xteveProxyHandler"); | ||||||
| 
 | 
 | ||||||
| export default async function xteveProxyHandler(req, res) { | export default async function xteveProxyHandler(req, res) { | ||||||
|   const { group, service } = req.query; |   const { group, service, index } = req.query; | ||||||
| 
 | 
 | ||||||
|   if (!group || !service) { |   if (!group || !service) { | ||||||
|     return res.status(400).json({ error: "Invalid proxy service type" }); |     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; |   const api = widgets?.[widget.type]?.api; | ||||||
|   if (!api) { |   if (!api) { | ||||||
|     return res.status(403).json({ error: "Service does not support API calls" }); |     return res.status(403).json({ error: "Service does not support API calls" }); | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 shamoon
						shamoon