浏览代码

Merge pull request #657 from benphelps/fix-649

Fix: allow multiple instances of certain widgets
shamoon 2 年之前
父节点
当前提交
f6b6c64b93

+ 12 - 11
src/widgets/homebridge/proxy.js

@@ -10,7 +10,7 @@ const proxyName = "homebridgeProxyHandler";
 const sessionTokenCacheKey = `${proxyName}__sessionToken`;
 const logger = createLogger(proxyName);
 
-async function login(widget) {
+async function login(widget, service) {
   const endpoint = "auth/login";
   const api = widgets?.[widget.type]?.api
   const loginUrl = new URL(formatApiCall(api, { endpoint, ...widget }));
@@ -26,7 +26,7 @@ async function login(widget) {
   try {
     const { access_token: accessToken, expires_in: expiresIn } = JSON.parse(data.toString());
   
-    cache.put(sessionTokenCacheKey, accessToken, (expiresIn * 1000) - 5 * 60 * 1000); // expiresIn (s) - 5m
+    cache.put(`${sessionTokenCacheKey}.${service}`, accessToken, (expiresIn * 1000) - 5 * 60 * 1000); // expiresIn (s) - 5m
     return { accessToken };
   } catch (e) {
     logger.error("Unable to login to Homebridge API: %s", e);
@@ -35,10 +35,11 @@ async function login(widget) {
   return { accessToken: false };
 }
 
-async function apiCall(widget, endpoint) {
+async function apiCall(widget, endpoint, service) {
+  const key = `${sessionTokenCacheKey}.${service}`;
   const headers = {
     "content-type": "application/json",
-    "Authorization": `Bearer ${cache.get(sessionTokenCacheKey)}`,
+    "Authorization": `Bearer ${cache.get(key)}`,
   }
 
   const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
@@ -51,7 +52,7 @@ async function apiCall(widget, endpoint) {
 
   if (status === 401) {
     logger.debug("Homebridge API rejected the request, attempting to obtain new session token");
-    const { accessToken } = login(widget);
+    const { accessToken } = login(widget, service);
     headers.Authorization = `Bearer ${accessToken}`;
 
     // retry the request, now with the new session token
@@ -83,14 +84,14 @@ export default async function homebridgeProxyHandler(req, res) {
     return res.status(400).json({ error: "Invalid proxy service type" });
   }
 
-  if (!cache.get(sessionTokenCacheKey)) {
-    await login(widget);
+  if (!cache.get(`${sessionTokenCacheKey}.${service}`)) {
+    await login(widget, service);
   }
 
-  const { data: statusData } = await apiCall(widget, "status/homebridge");
-  const { data: versionData } = await apiCall(widget, "status/homebridge-version");
-  const { data: childBridgeData } = await apiCall(widget, "status/homebridge/child-bridges");
-  const { data: pluginsData } = await apiCall(widget, "plugins");
+  const { data: statusData } = await apiCall(widget, "status/homebridge", service);
+  const { data: versionData } = await apiCall(widget, "status/homebridge-version", service);
+  const { data: childBridgeData } = await apiCall(widget, "status/homebridge/child-bridges", service);
+  const { data: pluginsData } = await apiCall(widget, "plugins", service);
 
   return res.status(200).send({
       status: statusData?.status,

+ 6 - 6
src/widgets/npm/proxy.js

@@ -10,7 +10,7 @@ const proxyName = "npmProxyHandler";
 const tokenCacheKey = `${proxyName}__token`;
 const logger = createLogger(proxyName);
 
-async function login(loginUrl, username, password) {
+async function login(loginUrl, username, password, service) {
   const authResponse = await httpProxy(loginUrl, {
     method: "POST",
     body: JSON.stringify({ identity: username, secret: password }),
@@ -27,7 +27,7 @@ async function login(loginUrl, username, password) {
     
     if (status === 200) {
       const expiration = new Date(data.expires) - Date.now();
-      cache.put(tokenCacheKey, data.token, expiration - (5 * 60 * 1000)); // expiration -5 minutes
+      cache.put(`${tokenCacheKey}.${service}`, data.token, expiration - (5 * 60 * 1000)); // expiration -5 minutes
     }
   } catch (e) {
     logger.error(`Error ${status} logging into npm`, authResponse[2]);
@@ -53,9 +53,9 @@ export default async function npmProxyHandler(req, res) {
       let contentType;
       let data;
       
-      let token = cache.get(tokenCacheKey);
+      let token = cache.get(`${tokenCacheKey}.${service}`);
       if (!token) {
-        [status, token] = await login(loginUrl, widget.username, widget.password);
+        [status, token] = await login(loginUrl, widget.username, widget.password, service);
         if (status !== 200) {
           logger.debug(`HTTTP ${status} logging into npm api: ${token}`);
           return res.status(status).send(token);
@@ -72,8 +72,8 @@ export default async function npmProxyHandler(req, res) {
 
       if (status === 403) {
         logger.debug(`HTTTP ${status} retrieving data from npm api, logging in and trying again.`);
-        cache.del(tokenCacheKey);
-        [status, token] = await login(loginUrl, widget.username, widget.password);
+        cache.del(`${tokenCacheKey}.${service}`);
+        [status, token] = await login(loginUrl, widget.username, widget.password, service);
 
         if (status !== 200) {
           logger.debug(`HTTTP ${status} logging into npm api: ${data}`);

+ 9 - 6
src/widgets/plex/proxy.js

@@ -58,6 +58,9 @@ async function fetchFromPlexAPI(endpoint, widget) {
 
 export default async function plexProxyHandler(req, res) {
   const widget = await getWidget(req);
+  
+  const { service } = req.query;
+
   if (!widget) {
     return res.status(400).json({ error: "Invalid proxy service type" });
   }
@@ -74,18 +77,18 @@ export default async function plexProxyHandler(req, res) {
     streams = apiData.MediaContainer._attributes.size;
   }
 
-  let libraries = cache.get(librariesCacheKey);
+  let libraries = cache.get(`${librariesCacheKey}.${service}`);
   if (libraries === null) {
     logger.debug("Getting libraries from Plex API");
     [status, apiData] = await fetchFromPlexAPI("/library/sections", widget);
     if (apiData && apiData.MediaContainer) {
       libraries = [].concat(apiData.MediaContainer.Directory);
-      cache.put(librariesCacheKey, libraries, 1000 * 60 * 60 * 6);
+      cache.put(`${librariesCacheKey}.${service}`, libraries, 1000 * 60 * 60 * 6);
     }
   }
 
-  let movies = cache.get(moviesCacheKey);
-  let tv = cache.get(tvCacheKey);
+  let movies = cache.get(`${moviesCacheKey}.${service}`);
+  let tv = cache.get(`${tvCacheKey}.${service}`);
   if (movies === null || tv === null) {
     movies = 0;
     tv = 0;
@@ -100,8 +103,8 @@ export default async function plexProxyHandler(req, res) {
           tv += size;
         }
       }
-      cache.put(tvCacheKey, tv, 1000 * 60 * 10);
-      cache.put(moviesCacheKey, movies, 1000 * 60 * 10);
+      cache.put(`${tvCacheKey}.${service}`, tv, 1000 * 60 * 10);
+      cache.put(`${moviesCacheKey}.${service}`, movies, 1000 * 60 * 10);
     });
   }
   

+ 13 - 13
src/widgets/pyload/proxy.js

@@ -11,7 +11,7 @@ const logger = createLogger(proxyName);
 const sessionCacheKey = `${proxyName}__sessionId`;
 const isNgCacheKey = `${proxyName}__isNg`;
 
-async function fetchFromPyloadAPI(url, sessionId, params) {
+async function fetchFromPyloadAPI(url, sessionId, params, service) {
   const options = {
     body: params
       ? Object.keys(params)
@@ -25,10 +25,10 @@ async function fetchFromPyloadAPI(url, sessionId, params) {
   };
 
   // see https://github.com/benphelps/homepage/issues/517
-  const isNg = cache.get(isNgCacheKey);
+  const isNg = cache.get(`${isNgCacheKey}.${service}`);
   if (isNg && !params) {
     delete options.body;
-    options.headers.Cookie = cache.get(sessionCacheKey);
+    options.headers.Cookie = cache.get(`${sessionCacheKey}.${service}`);
   }
 
   // eslint-disable-next-line no-unused-vars
@@ -43,19 +43,19 @@ async function fetchFromPyloadAPI(url, sessionId, params) {
   return [status, returnData, responseHeaders];
 }
 
-async function login(loginUrl, username, password = '') {
-  const [status, sessionId, responseHeaders] = await fetchFromPyloadAPI(loginUrl, null, { username, password });
+async function login(loginUrl, service, username, password = '') {
+  const [status, sessionId, responseHeaders] = await fetchFromPyloadAPI(loginUrl, null, { username, password }, service);
   
   // this API actually returns status 200 even on login failure
   if (status !== 200 || sessionId === false) {
     logger.error(`HTTP ${status} logging into Pyload API, returned: ${JSON.stringify(sessionId)}`);
   } else if (responseHeaders['set-cookie']?.join().includes('pyload_session')) {
     // Support pyload-ng, see https://github.com/benphelps/homepage/issues/517
-    cache.put(isNgCacheKey, true);
+    cache.put(`${isNgCacheKey}.${service}`, true);
     const sessionCookie = responseHeaders['set-cookie'][0];
-    cache.put(sessionCacheKey, sessionCookie, 60 * 60 * 23 * 1000); // cache for 23h
+    cache.put(`${sessionCacheKey}.${service}`, sessionCookie, 60 * 60 * 23 * 1000); // cache for 23h
   } else {
-    cache.put(sessionCacheKey, sessionId);
+    cache.put(`${sessionCacheKey}.${service}`, sessionId);
   }
 
   return sessionId;
@@ -72,14 +72,14 @@ export default async function pyloadProxyHandler(req, res) {
         const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
         const loginUrl = `${widget.url}/api/login`;
 
-        let sessionId = cache.get(sessionCacheKey) ?? await login(loginUrl, widget.username, widget.password);
-        let [status, data] = await fetchFromPyloadAPI(url, sessionId);
+        let sessionId = cache.get(`${sessionCacheKey}.${service}`) ?? await login(loginUrl, service, widget.username, widget.password);
+        let [status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
 
         if (status === 403 || status === 401) {
           logger.info('Failed to retrieve data from Pyload API, trying to login again...');
-          cache.del(sessionCacheKey);
-          sessionId = await login(loginUrl, widget.username, widget.password);
-          [status, data] = await fetchFromPyloadAPI(url, sessionId);
+          cache.del(`${sessionCacheKey}.${service}`);
+          sessionId = await login(loginUrl, service, widget.username, widget.password);
+          [status, data] = await fetchFromPyloadAPI(url, sessionId, null, service);
         }
 
         if (data?.error || status !== 200) {

+ 3 - 3
src/widgets/transmission/proxy.js

@@ -25,12 +25,12 @@ export default async function transmissionProxyHandler(req, res) {
     return res.status(400).json({ error: "Invalid proxy service type" });
   }
 
-  let headers = cache.get(headerCacheKey);
+  let headers = cache.get(`${headerCacheKey}.${service}`);
   if (!headers) {
     headers = {
       "content-type": "application/json",
     }
-    cache.put(headerCacheKey, headers);
+    cache.put(`${headerCacheKey}.${service}`, headers);
   }
 
   const url = new URL(formatApiCall(widgets[widget.type].api, { endpoint, ...widget }));
@@ -55,7 +55,7 @@ export default async function transmissionProxyHandler(req, res) {
   if (status === 409) {
     logger.debug("Transmission is rejecting the request, but returning a CSRF token");
     headers[csrfHeaderName] = responseHeaders[csrfHeaderName];
-    cache.put(headerCacheKey, headers);
+    cache.put(`${headerCacheKey}.${service}`, headers);
 
     // retry the request, now with the CSRF token
     [status, contentType, data, responseHeaders] = await httpProxy(url, {

+ 3 - 2
src/widgets/unifi/proxy.js

@@ -58,6 +58,7 @@ async function login(widget) {
 
 export default async function unifiProxyHandler(req, res) {
   const widget = await getWidget(req);
+  const { service } = req.query;
   if (!widget) {
     return res.status(400).json({ error: "Invalid proxy service type" });
   }
@@ -68,7 +69,7 @@ export default async function unifiProxyHandler(req, res) {
   }
 
   let [status, contentType, data, responseHeaders] = [];
-  let prefix = cache.get(prefixCacheKey);
+  let prefix = cache.get(`${prefixCacheKey}.${service}`);
   if (prefix === null) {
     // auto detect if we're talking to a UDM Pro, and cache the result so that we
     // don't make two requests each time data from Unifi is required
@@ -77,7 +78,7 @@ export default async function unifiProxyHandler(req, res) {
     if (responseHeaders?.["x-csrf-token"]) {
       prefix = udmpPrefix;
     }
-    cache.put(prefixCacheKey, prefix);
+    cache.put(`${prefixCacheKey}.${service}`, prefix);
   }
 
   widget.prefix = prefix;