diff --git a/src/widgets/homebridge/proxy.js b/src/widgets/homebridge/proxy.js index 3f81051f2..0ed6d8591 100644 --- a/src/widgets/homebridge/proxy.js +++ b/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, diff --git a/src/widgets/npm/proxy.js b/src/widgets/npm/proxy.js index d6499e58c..837a77437 100644 --- a/src/widgets/npm/proxy.js +++ b/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}`); diff --git a/src/widgets/plex/proxy.js b/src/widgets/plex/proxy.js index b91b35338..e67bbaa23 100644 --- a/src/widgets/plex/proxy.js +++ b/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); }); } diff --git a/src/widgets/pyload/proxy.js b/src/widgets/pyload/proxy.js index 4a866d9cf..5a51c3b75 100644 --- a/src/widgets/pyload/proxy.js +++ b/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) { diff --git a/src/widgets/transmission/proxy.js b/src/widgets/transmission/proxy.js index cdc1e9c92..83ca141e3 100644 --- a/src/widgets/transmission/proxy.js +++ b/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, { diff --git a/src/widgets/unifi/proxy.js b/src/widgets/unifi/proxy.js index abb5986f4..9fbeafded 100644 --- a/src/widgets/unifi/proxy.js +++ b/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;