Homepage 集成 UniFi Controller 服务 Widget:配置、认证与状态展示实战指南
【免费下载链接】homepageA highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.项目地址: https://gitcode.com/GitHub_Trending/ho/homepage
Homepage 是一个高度可定制的主页/应用仪表盘项目,支持通过服务 API 集成展示各类应用的实时状态。其中 UniFi Controller 服务 Widget 允许你在仪表盘上直接展示 UniFi Network Controller 的通用连接状态——包括网关在线时长(uptime)、WAN/LAN/WLAN 三个子系统的连通性、在线用户数与已接入设备数。本文将基于仓库中的官方文档 unifi-controller.md 及 unifi 模块源码 展开,从配置写法、认证机制、显示逻辑到故障排查,完整讲解该 Widget 的接入与底层原理,读完即可在自己的 Homepage 上正确接入 UniFi Controller。
Widget 能展示什么
该 Widget 读取 UniFi Network Controller 的站点(Site)健康数据,并将其中的关键指标渲染为若干状态块(Block):
- uptime:网关系统运行时长(以天为单位,保留一位小数);
- wan:WAN 子系统连通状态(up / down);
- lan:LAN 子系统连通状态(up / down);
- lan_users / lan_devices:LAN 在线用户数与已接入设备数;
- wlan / wlan_users / wlan_devices:WLAN 对应指标。
Allowed fields: ["uptime", "wan", "lan", "lan_users", "lan_devices", "wlan", "wlan_users", "wlan_devices"](最多展示四个)。注意:设备不支持的字段将不会显示——例如纯交换机或未启用无线网络的设备,WLAN 相关字段会被自动隐藏(详见下文渲染逻辑)。这也意味着最终实际展示的字段组合会随设备型号动态变化。
前置要求
在配置前务必注意官方文档中的警告:
认证时请使用至少具有读取权限的本地账号(local account with at least read privileges)。
不要使用云端 Ubiquiti 账号,而是使用 UniFi 控制器中创建的本地管理员/只读账号,以保证 API 认证与数据读取的可靠性。
配置方法
UniFi Controller 服务 Widget 的配置写在 Homepage 的services.yaml中(默认配置骨架见 src/skeleton/services.yaml)。在目标服务条目下的widget:中声明:
widget: type: unifi url: https://unifi.host.or.ip:port site: Site Name # optional username: user password: pass key: unifiapikey # required if using API key instead of username/password各参数说明如下:
| 参数 | 是否必填 | 说明 |
|---|---|---|
type | 必填 | 固定为unifi |
url | 必填 | UniFi Controller 的地址,格式https://unifi.host.or.ip:port,注意保留端口 |
site | 可选 | 站点名称;不填时自动使用控制器的默认站点(default site) |
username/password | 与key二选一 | 本地账号的用户名密码 |
key | 与用户名密码二选一 | UniFi API Key,使用 API Key 认证时必须提供,此时不再需要用户名密码 |
从配置解析看,site参数在 service-helpers.js 中被单独提取并注入 widget 对象:if (type === "unifi") { if (site) widget.site = site; }。也就是说site是 unifi 类型独有的可选参数。
另外值得说明的是安全处理:username、password、key、apiKey属于私有选项,在 widget-helpers.js 的cleanWidgetGroups中会被从发送到前端的配置中剔除,url也会被删除(仅search、glances类型保留),确保凭据不会泄露到浏览器端。
使用 Info Widget(信息栏)形式
除了挂载在服务条目下,UniFi 状态也可以作为独立的info widget使用,配置在widgets.yaml中(见 docs/widgets/info/unifi_controller.md):
- unifi_console: url: https://unifi.host.or.ip:port site: Site Name # optional username: user password: pass key: unifiapikey # required if using API key instead of username/password两者的区别在于挂载位置(服务条目 vs. 顶部信息栏),底层数据源与认证逻辑完全一致。在 proxy.js 中可以看到,代理处理器会区分两种场景:当请求的group与service均为unifi_console时,走 info widget 分支,通过getPrivateWidgetOptions("unifi_console", index)读取私有配置;否则走常规服务 widget 分支,通过getServiceWidget(group, service, index)解析服务配置。
底层实现原理
1. API 定义与数据端点
widget 的 API 模板定义在 widget.js:
const widget = { api: "{url}{prefix}/api/{endpoint}", proxyHandler: unifiProxyHandler, mappings: { "stat/sites": { endpoint: "stat/sites" }, }, };即前端通过stat/sites端点(最终请求{url}{prefix}/api/stat/sites)获取所有站点的健康数据。{prefix}会根据设备类型动态决定(见下文),这是为了兼容 UDM Pro / UDM SE 等内置控制器(UDMP)与独立控制器之间 API 路径的差异。
2. 认证流程与缓存机制
认证是这套 Widget 最复杂的部分,实现在 utils/proxy/handlers/unifi.js 与 proxy.js 中,整体流程如下:
- 前缀探测:当没有缓存的 prefix 时,代理先向
widget.url发起一次探测请求,读取响应头:- 若存在
x-csrf-token或access-control-expose-headers相关头,则判定为 UDMP 设备,prefix = "/proxy/network",并捕获 CSRF Token; - 否则为传统独立控制器,
prefix为空字符串。
- 若存在
- API Key 快捷认证:若配置了
key,则直接以X-API-KEY: <key>请求头调用 API(Accept: application/json),无需登录流程。这也是文档要求“使用 API Key 时无需用户名密码”的源码依据(proxy.js)。 - 会话登录:未配置 API Key 时,代理会带上 cookie 与 CSRF Token 先请求 API;若返回
401,则自动向登录端点 POST 用户名密码(请求体包含username、password、remember: true、rememberMe: true,见 handlers/unifi.js)。 - 登录校验:登录响应需满足
meta.rc === "ok"或包含login_time/update_time字段才视为成功(isSuccessfulLoginResponse),随后将 cookie 写入 cookie-jar 并重放数据请求。 - prefix 缓存:探测出的 prefix 会以
unifiProxyHandler__prefix.<service>为键缓存在内存中(memory-cache),避免每次请求都重复探测;登录成功后 cookie 同样由 cookie-jar.js 管理。这也正是“凭据错误后需要重启服务/重建容器清缓存”的原因——prefix 与 cookie 均缓存在服务端内存中。
3. 前端渲染逻辑
component.jsx 负责把stat/sites数据渲染成状态块:
- 站点选择:若配置了
site,按desc(站点描述)匹配;否则取name === "default"的默认站点。若配置的站点找不到,会渲染错误Site '<site>' not found(对应测试用例见 component.test.jsx)。 - 数据解析:从
defaultSite.health中分别取出wan、lan、wlan三个子系统的健康项;status === "ok"视为 up,status === "unknown"的子系统会被隐藏(show = false)——这就是“设备不支持的字段不显示”的实现方式。 - uptime 计算:读取
wan["gw_system-stats"].uptime(单位秒),除以 86400 换算为天,保留一位小数后拼接unifi.days本地化后缀(component.jsx)。 - 字段互斥显示:当
lan与wlan同时可用时,只显示各自的用户数(lan_users/wlan_users);当只有一个子系统可用时,才额外补充显示该子系统的设备数(lan_devices/wlan_devices)与连通状态,确保总块数不超过四个。 - 空数据兜底:若所有子系统都不可见且无 uptime,则显示
unifi.empty_data占位块。
上述行为均有对应的 Vitest 测试覆盖,例如默认站点数据存在时渲染 uptime、WAN 状态与用户数的用例(component.test.jsx),可以作为理解渲染规则的参考。
常见问题排查
1. 输入正确凭据后仍报 "API Error"
官方文档给出了明确提示:如果输入了例如错误的凭据并收到 "API Error",可能需要重建容器或重启服务以清除缓存。这是因为登录失败后,服务端内存中的 prefix/cookie 缓存可能处于不一致状态(见上文“缓存机制”)。
2. 提示 "Site 'xxx' not found"
site参数必须与控制器中站点的desc字段精确匹配。如果拿不准站点名称,可以省略site参数让 Widget 自动使用默认站点。
3. 某些字段不显示
属于正常现象:lan_devices、lan、wlan_devices、wlan等字段仅在对应子系统存在且状态不是unknown时展示;设备不支持的子系统会被自动隐藏。
4. 认证失败
确认使用的是具有读取权限的本地账号,并检查url是否包含正确的端口(默认https://<host>:8443或 UDM 系列的其他端口)。
小结
UniFi Controller 服务 Widget 是 Homepage 中认证逻辑较为完整的一类集成:它同时支持用户名密码会话登录与 API Key 直连两种模式,能自动适配 UDMP 与独立控制器两种 API 路径,并依据设备实际能力动态渲染最多四个状态字段。配置只需在services.yaml的服务条目下声明type: unifi及对应凭据即可。如需深入了解实现细节,可继续阅读 unifi/widget.js、unifi/proxy.js、unifi/component.jsx 及通用认证处理器 utils/proxy/handlers/unifi.js。
【免费下载链接】homepageA highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.项目地址: https://gitcode.com/GitHub_Trending/ho/homepage
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考