源码包:pkg/provisioning/ · 仓库文档索引:docs/README.md
- 英文包级说明:pkg/provisioning/docs/README.md — 职责边界、源码地图、集成表
- 配置与安全:pkg/provisioning/docs/CONFIG.md — 环境变量、
provisioning.json、device.*键、HTTP 与前端令牌 - 本文 — 实现细节、API 表、前端目录、curl 示例、测试命令(中文深度参考)
MoonHub 设备配网系统已成功实现,实现了"插电即用"的零配置设备体验。系统包含完整的后端 API 层和前端配网页面。
pkg/provisioning/
├── config.go # 类型定义和配置 Key
├── events.go # SSE 事件广播
├── network.go # nmcli 网络操作封装
├── network_test.go # WiFi 列表解析等单元测试
├── manager.go # DeviceManager 核心实现
└── manager_test.go # 单元测试
- 六种设备模式:
provisioning: 等待初始 WiFi 配置(热点激活)connecting: 正在连接配置的 WiFionboarding: 网络已连接,等待用户完成引导ready: 设备完全就绪maintenance: 维护模式error: 遇到不可恢复的错误
- 运行时阶段:provisioning, connecting, onboarding, ready, degraded, maintenance, restarting, error
- WiFi 网络扫描(nmcli)
- WiFi 连接(支持密码、隐藏网络)
- 已保存网络列表
- 网络遗忘功能
- 自动重连机制
- 自动创建热点配置文件
- 热点启用/禁用
- 默认热点 SSID:
MoonHub-XXXX(XXXX 为设备名后4位)
- 连接状态检测
- 互联网可达性测试
- DNS 解析测试
- 网络详细信息获取
- 可配置的故障阈值
- 冷却时间机制
- 网络退化检测
- 自动回滚到配网模式
- 自动恢复触发:连续网络故障达到阈值时自动触发恢复
- 恢复策略:
- 尝试重连上次保存的 WiFi 网络
- 失败后启用热点作为后备
- 手动恢复触发:支持通过 API 手动触发恢复流程
- 事件类型:status, action, system
- 事件级别:info, success, warning, error
- 动作阶段:started, completed, failed
- 最近事件缓存(25条)
- 清除所有已保存的 WiFi 网络
- 清除所有设备配置(device.*)
- 清除引导状态(onboarding.*)
- 启用热点并返回配网模式
- 通过 SSE 事件通知恢复进度
- 6 位数字授权码(
crypto/rand均匀随机) - 设备标识生成(YSHU-YYYY-XXX 格式)
- 授权码重新生成
- 用于 PWA 连接设备时的身份验证
web/frontend/src/
├── api/provisioning.ts # API 客户端
├── hooks/use-provisioning-sse.ts # SSE 事件订阅 Hook
├── components/provisioning/
│ ├── layout.tsx # 配网页面布局
│ ├── bottom-nav.tsx # 底部四步骤导航
│ ├── wifi-selector.tsx # WiFi 网络选择器
│ ├── auth-code.tsx # 授权码显示组件
│ └── factory-reset-dialog.tsx # 恢复出厂设置对话框
└── routes/provisioning/
├── route.tsx # 配网路由布局
├── index.tsx # WiFi 配置页(步骤1)
├── auth.tsx # 授权码页面(步骤2)
├── install.tsx # PWA 安装引导(步骤3)
└── settings.tsx # 设备设置页(步骤4)
web/frontend/public/
├── sw.js # Service Worker
├── site.webmanifest # PWA 配置(配网范围)
└── fonts/
├── manrope/
│ ├── manrope.css # Manrope 字体 CSS
│ └── Manrope-VariableFont_wght.woff2 # 字体文件(需下载)
└── material-symbols/
├── material-symbols.css # Material Symbols CSS
└── MaterialSymbolsOutlined.woff2 # 字体文件(需下载)
Service Worker 功能:
- 缓存配网页面静态资源
- 离线时从缓存提供服务
- API 调用不走缓存(实时性要求)
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ WiFi 配置页 │ ──► │ 授权码页面 │ ──► │ PWA 安装引导 │ ──► │ 设备设置页 │
│ /provisioning │ │ /provisioning/ │ │ /provisioning/ │ │ /provisioning/ │
│ │ │ auth │ │ install │ │ settings │
│ - 扫描网络 │ │ - 显示6位授权码 │ │ - iOS 指引 │ │ - 设备状态 │
│ - 选择网络 │ │ - 复制按钮 │ │ - Android 指引 │ │ - 网络恢复 │
│ - 输入密码 │ │ - 设备标识 │ │ - 安装按钮 │ │ - 恢复出厂设置 │
│ - 开始配网 │ │ - 下一步按钮 │ │ │ │ │
└─────────────────┘ └─────────────────┘ └─────────────────┘ └─────────────────┘
- 主色调:
#506070(深蓝灰) - 月晕光晕效果:
box-shadow: 0 0 60px 10px rgba(212, 228, 247, 0.4) - 玻璃态背景:
backdrop-filter: blur(12px) - Material Symbols 图标
- Manrope 字体
- HTTP API 处理器
- SSE 事件流处理
- 请求验证和响应格式化
- 授权码端点处理
- 添加
provisioning字段 - 添加
SetProvisioningHandler()方法 - 自动注册配网路由
- 环境变量控制启用/禁用
- 在
RegisterRoutes之前调用SetProvisioningHandler,确保/api/provisioning/*已挂载 - 设备管理器初始化与后台监控循环
- 中间件顺序:先
IPAllowlist(若配置了 CIDR),再ProvisioningAuth;后者在非空MOONHUB_PROVISIONING_TOKEN下校验Authorization: Bearer …或X-MoonHub-Provisioning-Token - 若
MOONHUB_PROVISIONING_ENABLED=1且对外监听(-public或 launcher 配置)但未设置 token,启动时打印告警
| 变量 | 说明 |
|---|---|
MOONHUB_PROVISIONING_ENABLED |
设为 1 时启用配网管理器与 API |
MOONHUB_ALLOW_SYSTEM_CONTROL |
设为 1 时允许通过 API 触发真实 systemctl 重启等 |
MOONHUB_PROVISIONING_TOKEN |
非空时,所有 /api/provisioning/* 请求须携带与之一致的 Bearer 或 X-MoonHub-Provisioning-Token;强烈建议在公网/局域网对外暴露监听时设置 |
设备端 provisioning.json:由 JSONConfigStore 写入,文件权限 0600,避免同机其他用户读取热点 PSK、授权码等。
前端令牌(浏览器):
- 不要把
MOONHUB_PROVISIONING_TOKEN写进 Vite 构建环境变量或仓库。 - 当接口返回 401 时,配网布局会提示输入令牌;用户输入后保存到
sessionStorage键名moonhub.provisioningToken,并刷新页面。 - 若已配置服务端 token,SSE 使用带
Authorization的fetch流式读取(标准EventSource无法自定义头)。
| 方法 | 路径 | 描述 |
|---|---|---|
| GET | /api/provisioning/status |
获取设备状态 |
| GET | /api/provisioning/events |
SSE 事件流 |
| GET | /api/provisioning/networks |
扫描 WiFi 网络 |
| GET | /api/provisioning/networks/saved |
已保存网络列表 |
| POST | /api/provisioning/network/connect |
连接 WiFi |
| POST | /api/provisioning/network/forget |
遗忘网络 |
| GET | /api/provisioning/network/diagnostics |
网络诊断 |
| POST | /api/provisioning/network/test |
测试互联网连接 |
| POST | /api/provisioning/hotspot/enable |
启用热点 |
| POST | /api/provisioning/hotspot/disable |
禁用热点 |
| POST | /api/provisioning/restart |
请求重启 |
| POST | /api/provisioning/rollback |
回滚到配网模式 |
| GET | /api/provisioning/auth-code |
获取授权码 |
| POST | /api/provisioning/auth-code/regenerate |
重新生成授权码 |
| POST | /api/provisioning/recovery/trigger |
手动触发网络恢复 |
| POST | /api/provisioning/factory-reset |
恢复出厂设置 |
MOONHUB_PROVISIONING_ENABLED=1 # 启用配网系统
MOONHUB_ALLOW_SYSTEM_CONTROL=1 # 允许系统控制(重启等){
"device.mode": "provisioning",
"device.network.provisioned": false,
"device.network.lastSsid": "",
"device.network.hotspotSsid": "MoonHub-XXXX",
"device.network.hotspotProfile": "MoonHub Hotspot",
"device.network.hotspotPassword": "MoonHub-XXXX-wifi",
"device.network.interfaceName": "wlan0",
"device.network.apEnabled": true,
"device.auth.code": "882966",
"device.auth.deviceId": "YSHU-2024-OX2",
"device.recovery.enabled": true,
"device.recovery.failureThreshold": 3,
"device.recovery.cooldownMs": 120000
}[Unit]
Description=MoonHub AI Assistant
After=network-online.target NetworkManager.service
Wants=network-online.target
[Service]
Type=simple
Environment=MOONHUB_PROVISIONING_ENABLED=1
Environment=MOONHUB_ALLOW_SYSTEM_CONTROL=1
ExecStart=/usr/local/bin/moonhub-web -public /etc/moonhub/config.json
Restart=always
[Install]
WantedBy=multi-user.targetcurl http://localhost:18800/api/provisioning/status响应:
{
"status": {
"mode": "provisioning",
"hostname": "moonhub-device",
"network": {
"provisioned": false,
"connected": false,
"hotspotSsid": "MoonHub-VICE",
"apEnabled": true
},
"runtime": {
"phase": "provisioning",
"health": "ok",
"summary": "Waiting for initial WiFi provisioning."
}
}
}curl http://localhost:18800/api/provisioning/networkscurl -X POST http://localhost:18800/api/provisioning/network/connect \
-H "Content-Type: application/json" \
-d '{"ssid": "MyNetwork", "password": "mypassword"}'curl http://localhost:18800/api/provisioning/auth-code响应:
{
"code": "882966",
"deviceId": "YSHU-2024-OX2"
}const eventSource = new EventSource('/api/provisioning/events');
eventSource.addEventListener('device', (event) => {
const data = JSON.parse(event.data);
console.log('Event:', data);
});http://localhost:18800/provisioning
curl -X POST http://localhost:18800/api/provisioning/recovery/trigger响应:
{
"ok": true
}curl -X POST http://localhost:18800/api/provisioning/factory-reset响应:
{
"ok": true
}TestNewDeviceManager- 设备管理器创建TestNormalizeMode- 模式规范化TestSanitizeHotspotSuffix- 热点 SSID 后缀生成TestDefaultHotspotSSID- 默认热点 SSID 生成TestGetStatus- 状态获取TestListWifiNetworks- WiFi 网络列表TestEventBroadcaster- 事件广播TestConfigStore- 配置存储TestDeriveRuntimeState- 运行时状态派生
go test ./pkg/provisioning/... -vpkg/provisioning/config.gopkg/provisioning/events.gopkg/provisioning/network.gopkg/provisioning/manager.gopkg/provisioning/manager_test.goweb/backend/api/provisioning.godeployment/systemd/moonhub.service
web/frontend/src/api/provisioning.tsweb/frontend/src/hooks/use-provisioning-sse.tsweb/frontend/src/components/provisioning/layout.tsxweb/frontend/src/components/provisioning/bottom-nav.tsxweb/frontend/src/components/provisioning/wifi-selector.tsxweb/frontend/src/components/provisioning/auth-code.tsxweb/frontend/src/components/provisioning/factory-reset-dialog.tsxweb/frontend/src/routes/provisioning/route.tsxweb/frontend/src/routes/provisioning/index.tsxweb/frontend/src/routes/provisioning/auth.tsxweb/frontend/src/routes/provisioning/install.tsxweb/frontend/src/routes/provisioning/settings.tsxweb/frontend/public/sw.jsweb/frontend/public/fonts/manrope/manrope.cssweb/frontend/public/fonts/material-symbols/material-symbols.css
web/backend/api/router.go- 添加 provisioning 字段和 SetProvisioningHandler 方法web/backend/main.go- 添加设备管理器初始化代码web/frontend/index.html- 添加 Material Symbols 和 Manrope 字体(支持本地字体)web/frontend/src/main.tsx- 添加 Service Worker 注册web/frontend/public/site.webmanifest- 更新 PWA 配置
设计参考:
web/frontend/public/provisioning/wifi/index.html- WiFi 配置页设计稿web/frontend/public/provisioning/auth/index.html- 授权码页设计稿web/frontend/public/provisioning/pwa/index.html- PWA 安装页设计稿
- PWA 应用 - 完整的 PWA 应用(扫描设备、输入授权码、调用设备能力)
Service Worker - 离线支持✅ 已完成网络恢复增强 - 可选的自动恢复机制优化✅ 已完成Factory Reset - 恢复出厂设置功能✅ 已完成
Service Worker 已实现,配网页面可在设备热点环境下离线可用。字体文件需要手动下载:
-
Manrope 字体
- 下载地址:https://fonts.google.com/specimen/Manrope
- 保存为:
web/frontend/public/fonts/manrope/Manrope-VariableFont_wght.woff2
-
Material Symbols 字体
- 下载地址:https://fonts.google.com/specimen/Material+Symbols+Outlined
- 保存为:
web/frontend/public/fonts/material-symbols/MaterialSymbolsOutlined.woff2
如未下载字体文件,系统会自动回退到 Google Fonts CDN(需要互联网连接)。