diff --git a/docs/widgets/info/unifi_controller.md b/docs/widgets/info/unifi_controller.md index e16ca40b..4c79087f 100644 --- a/docs/widgets/info/unifi_controller.md +++ b/docs/widgets/info/unifi_controller.md @@ -22,9 +22,8 @@ An optional 'site' parameter can be supplied, if it is not the widget will use t ```yaml - unifi_console: url: https://unifi.host.or.ip:port + site: Site Name # optional username: user password: pass - site: Site Name # optional + key: unifiapikey # required if using API key instead of username/password ``` - -_Added in v0.4.18, updated in 0.6.7_ diff --git a/docs/widgets/services/unifi-controller.md b/docs/widgets/services/unifi-controller.md index 45b16db2..ecb81b8c 100644 --- a/docs/widgets/services/unifi-controller.md +++ b/docs/widgets/services/unifi-controller.md @@ -25,9 +25,8 @@ Allowed fields: `["uptime", "wan", "lan", "lan_users", "lan_devices", "wlan", "w widget: type: unifi url: https://unifi.host.or.ip:port - username: username - password: password site: Site Name # optional + username: user + password: pass + key: unifiapikey # required if using API key instead of username/password ``` - -_Added in v0.4.18, updated in 0.6.7_ diff --git a/src/utils/config/widget-helpers.js b/src/utils/config/widget-helpers.js index 7c5c78cd..3b1355d6 100644 --- a/src/utils/config/widget-helpers.js +++ b/src/utils/config/widget-helpers.js @@ -38,7 +38,7 @@ export async function cleanWidgetGroups(widgets) { } }); - // delete url from the sanitized options if the widget is not a search or glances widgeth + // delete url from the sanitized options if the widget is not a search or glances widget if (widget.type !== "search" && widget.type !== "glances" && optionKeys.includes("url")) { delete sanitizedOptions.url; } diff --git a/src/widgets/unifi/proxy.js b/src/widgets/unifi/proxy.js index 559065e3..24be7dd9 100644 --- a/src/widgets/unifi/proxy.js +++ b/src/widgets/unifi/proxy.js @@ -47,7 +47,7 @@ async function login(widget, csrfToken) { const endpoint = widget.prefix === udmpPrefix ? "auth/login" : "login"; const api = widgets?.[widget.type]?.api?.replace("{prefix}", ""); // no prefix for login url const loginUrl = new URL(formatApiCall(api, { endpoint, ...widget })); - const loginBody = { username: widget.username, password: widget.password, remember: true }; + const loginBody = { username: widget.username, password: widget.password, remember: true, rememberMe: true }; const headers = { "Content-Type": "application/json" }; if (csrfToken) { headers["X-CSRF-TOKEN"] = csrfToken; @@ -75,31 +75,39 @@ export default async function unifiProxyHandler(req, res) { let [status, contentType, data, responseHeaders] = []; let prefix = cache.get(`${prefixCacheKey}.${service}`); let csrfToken; - 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 + const headers = {}; + if (widget.key) { + prefix = udmpPrefix; + headers["X-API-KEY"] = widget.key; + headers["Accept"] = "application/json"; + } else if (prefix === null) { + // auto detect if we're talking to a UDM Pro or Network API device, and cache the result + // so that we don't make two requests each time data from Unifi is required [status, contentType, data, responseHeaders] = await httpProxy(widget.url); prefix = ""; if (responseHeaders?.["x-csrf-token"]) { // Unifi OS < 3.2.5 passes & requires csrf-token prefix = udmpPrefix; csrfToken = responseHeaders["x-csrf-token"]; - } else if (responseHeaders?.["access-control-expose-headers"]) { - // Unifi OS ≥ 3.2.5 doesnt pass csrf token but still uses different endpoint + } else if ( + responseHeaders?.["access-control-expose-headers"] || + responseHeaders?.["Access-Control-Expose-Headers"] + ) { + // Unifi OS ≥ 3.2.5 doesnt pass csrf token but still uses different endpoint, same with Network API prefix = udmpPrefix; } - cache.put(`${prefixCacheKey}.${service}`, prefix); } + cache.put(`${prefixCacheKey}.${service}`, prefix); widget.prefix = prefix; const { endpoint } = req.query; const url = new URL(formatApiCall(api, { endpoint, ...widget })); - const params = { method: "GET", headers: {} }; + const params = { method: "GET", headers }; setCookieHeader(url, params); [status, contentType, data, responseHeaders] = await httpProxy(url, params); - if (status === 401) { + if (status === 401 && !widget.key) { logger.debug("Unifi isn't logged in or rejected the reqeust, attempting login."); if (responseHeaders?.["x-csrf-token"]) { csrfToken = responseHeaders["x-csrf-token"];