☰
在 Homepage 中集成 UniFi Drive 存储状态 Widget:配置指南与源码级原理剖析
2026/10/11 21:53:51 网站建设 项目流程

在 Homepage 中集成 UniFi Drive 存储状态 Widget:配置指南与源码级原理剖析

【免费下载链接】homepageA highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.项目地址: https://gitcode.com/GitHub_Trending/ho/homepage

本文围绕 Homepage 项目中的 UniFi Drive 服务 Widget 展开,讲解如何通过一个本地 UniFi 账号,将 UniFi 网络附加存储(UNAS)设备的容量与健康状态直接展示在你的起始页仪表盘上。读完本文,你将掌握完整的 YAML 配置方法、四个可展示字段的取值含义,以及从认证、API 调用到前端渲染的完整实现链路。

UniFi Drive Widget 能做什么

UniFi Drive 是 Homepage 提供的服务型 Widget 之一(完整清单见 服务 Widget 索引),专门用于展示 UniFi Network Attached Storage(UNAS)设备的存储统计信息。它不会展示文件列表或具体数据,而是聚焦于存储池(Storage Pool)层面的关键指标:

  • 存储池总容量(Total)
  • 已用空间(Used)
  • 可用空间(Available)
  • 存储池健康状态(Status)

该 Widget 的官方文档位于 docs/widgets/services/unifi-drive.md,其描述明确指出:需要本地 UniFi 账号,且该账号至少应具备读取权限(read privileges)。

提示:如果你需要的是 UniFi Network Controller(而非 UNAS)的连接状态展示,请参阅 UniFi Controller Widget,两者的类型标识、配置参数和 API 路径均不相同,不要混用。

前置条件

在配置之前,请确认以下前提是否满足:

  1. 你拥有一台 UniFi 网络附加存储(UNAS)设备,并已将其接入你的网络。
  2. 你拥有该设备的本地 UniFi 账号(非仅限云端账号),且该账号至少具备读取权限。
  3. Homepage 实例能够通过网络访问 UNAS 设备的地址(url参数指向的地址)。

在 services.yaml 中配置 Widget

UniFi Drive Widget 的配置与 Homepage 其他服务 Widget 一样,位于服务的widget字段中(服务配置文件的骨架示例见 src/skeleton/services.yaml)。完整的配置块如下:

widget: type: unifi_drive url: https://unifi.host.or.ip username: your_username password: your_password

配置参数说明

参数必填说明
type是固定为unifi_drive,用于告诉 Homepage 加载哪个 Widget 实现
url是UNAS 设备的主机名或 IP 地址,可包含https://协议头。注意 formatApiCall 会移除 URL 末尾多余的/斜杠,因此无需担心尾斜杠问题
username是本地 UniFi 账号的用户名
password是对应用户名的密码

在url中如非默认端口,也可显式指定端口,例如https://unifi.host.or.ip:8443。

可选字段过滤

与 Homepage 其他服务 Widget 一致,UniFi Drive 支持通过fields过滤展示的指标块。官方文档明确给出的允许字段为:

["total", "used", "available", "status"]

例如只展示容量与可用空间:

widget: type: unifi_drive url: https://unifi.host.or.ip username: your_username password: your_password fields: - total - available

字段过滤由 Container 组件 实现:它会依据service.widget.fields对子级Block进行匹配,未命中字段的指标块会被隐藏。字段名若不含.,会自动以 Widget 类型(unifi_drive)作为命名空间前缀进行匹配。

前端展示逻辑:四个指标块与状态映射

前端组件实现位于 src/widgets/unifi_drive/component.jsx,它定义了该 Widget 的展示行为:

  1. 加载占位:数据未返回时,渲染 4 个带骨架动画的占位块(total/used/available/status)。
  2. 无数据兜底:当 API 返回的pools不是数组或为空数组时,仅显示一条 "No storage data available"(中文环境为"无可用存储数据"),对应 本地化文件 中的unifi_drive.no_data。
  3. 多存储池聚合:UNAS 可能包含多个存储池。组件会对所有池的容量与用量做累加:
    • totalBytes= 所有池capacity之和
    • usedBytes= 所有池usage之和
    • availableBytes=max(0, totalBytes - usedBytes)
  4. 状态归一化:任一池状态为degraded时整体显示"降级";若所有池均为fullyOperational或noDataProtectionYet(尚未配置数据保护),则整体显示"正常"(healthy)。

状态值与展示文案的映射关系如下(对应 英文本地化):

池状态(API 原始值)展示文案(en)展示文案(zh-Hans)
fullyOperationalHealthy正常
noDataProtectionYetHealthy正常
degradedDegraded已降级
其他值原样透传原样透传

容量数值通过t("common.bytes", { value })格式化为人类可读的字节单位,该翻译 key 在 Homepage 多个资源类 Widget(如 resources、glances)中被复用。Block组件还支持基于高亮规则(utils/highlights)对指标值进行颜色标记,实现代码见 src/components/services/widget/block.jsx。

源码级原理:认证与 API 调用链路

UniFi Drive Widget 的底层实现分为三层,理解它们有助于排查问题:

1. API 定义层(widget.js)

src/widgets/unifi_drive/widget.js 定义了 Widget 的 API 模板与端点映射:

const widget = { api: "{url}{prefix}/api/{endpoint}", proxyHandler: unifiDriveProxyHandler, mappings: { storage: { endpoint: "v2/storage", }, }, };
  • API 模板为{url}{prefix}/api/{endpoint},其中{url}来自配置,{prefix}由代理层动态解析,{endpoint}由mappings.storage指定为v2/storage。
  • 前端通过useWidgetAPI(widget, "storage")请求时,最终会请求形如https://unifi.host.or.ip/proxy/drive/api/v2/storage的地址。

2. 请求上下文解析层(proxy.js)

src/widgets/unifi_drive/proxy.js 是 UniFi Drive 专属的代理入口:

  • 通过getServiceWidget(group, service, index)从当前服务配置中取出 Widget 配置。
  • 首次请求时,先对widget.url发起一次探测请求,从响应头中提取x-csrf-token,并将前缀/proxy/drive(drivePrefix)写入内存缓存。
  • 前缀缓存 key 为unifiDriveProxyHandler__prefix.{service},后续请求直接复用,避免重复探测(这一点在 proxy.test.js 的 "skips prefix detection when cached" 测试用例中有明确验证)。

3. 通用 UniFi 代理层(handlers/unifi.js)

src/utils/proxy/handlers/unifi.js 是一个可复用的 UniFi 认证代理工厂,UniFi Drive 与 UniFi Controller 等 Widget 共用此实现。其关键流程为:

  1. 携带会话请求数据:使用内存缓存的前缀构造 API URL,并通过 cookie-jar 机制附加已保存的 Cookie。
  2. 401 时自动登录:当首次请求返回401(会话失效或尚未登录)且配置中无key时,代理会向auth/login端点发起 POST 请求,请求体为{ username, password, remember: true, rememberMe: true },并携带从响应头提取的x-csrf-token。
  3. 登录成功判定:解析登录响应,若meta.rc === "ok"或存在login_time/update_time字段,则视为登录成功,并将响应中的 Set-Cookie 存入 Cookie Jar。
  4. 重放请求:登录成功后使用新 Cookie 重新请求目标端点,最终将 UNAS 返回的存储数据原样透传给前端。

整个 HTTP 请求统一走 src/utils/proxy/http.js 的httpProxy,它负责 Cookie 管理与重定向时的 Cookie 续写、gzip/deflate 响应解压,以及 Alpine/musl 环境下的 DNS 解析兜底(HOMEPAGE_PROXY_DISABLE_IPV6=true可强制走 IPv4)。

该行为在 proxy.test.js 中有完整覆盖:配置缺失返回 400、Widget 类型无 API 配置返回 403、正常路径返回 200 并缓存前缀等。

常见问题与故障排查

凭据错误导致 "API Error"

官方文档特别提示:

如果输入了错误的凭据并收到 "API Error",你可能需要重新创建容器或重启服务以清除缓存。

这与实现细节直接相关:UniFi 代理层会将前缀(/proxy/drive)和 Cookie 写入内存缓存(memory-cache)。当凭据已变更但缓存中的会话 Cookie 仍有效或残留时,代理不会触发重新登录,导致反复出现认证错误。此时重启 Homepage 容器/服务以清空内存缓存即可恢复正常。

登录返回非 200 或响应不含成功标志

代理层会分别记录错误日志(HTTP %d logging in to UniFi或Error logging in to UniFi)并返回对应状态码。请检查:

  • username/password是否正确;
  • 账号是否具备读取权限;
  • url是否能被 Homepage 容器正常访问(网络互通、证书是否被信任——注意 http.js 中 https Agent 设置了rejectUnauthorized: false,即不校验服务端证书,便于连接自签名证书的 UNAS)。

显示 "No storage data available"

当 API 正常返回但pools数组为空或缺失时会出现该提示。这可能意味着设备尚未创建任何存储池,或当前账号无权查看存储池信息。

延伸阅读

  • 其他 UniFi 系列 Widget:UniFi Controller 服务 Widget、UniFi Controller 信息 Widget
  • 全部服务 Widget 索引
  • 服务配置骨架参考:src/skeleton/services.yaml
  • 实现源码:widget.js、proxy.js、component.jsx、通用 UniFi 代理
  • 测试用例:proxy.test.js、component.test.jsx

【免费下载链接】homepageA highly customizable homepage (or startpage / application dashboard) with Docker and service API integrations.项目地址: https://gitcode.com/GitHub_Trending/ho/homepage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询