329 lines
7.0 KiB
Markdown
329 lines
7.0 KiB
Markdown
# managerd
|
||
|
||
`managerd` 是 RK3588 管理端后端服务,负责设备发现、设备注册表维护、代理访问设备端 `rk3588-agent`,并提供内嵌 Web UI、OpenAPI 页面和 HTTP API。
|
||
|
||
## 当前状态
|
||
|
||
- 提供 HTTP API,供前端或其他本地工具调用。
|
||
- 内嵌 Web UI,启动后可直接在浏览器访问。
|
||
- 支持 UDP 广播发现设备,并维护内存中的设备在线状态。
|
||
- 支持代理访问设备端 agent 的常用接口,以及 `/v1/*` 通用透传。
|
||
- 支持批量任务执行,并通过 SSE 推送逐设备状态。
|
||
- 模板从本地 `templates/*.json` 读取。
|
||
|
||
## 运行方式
|
||
|
||
默认通过单个可执行文件运行:
|
||
|
||
```bash
|
||
managerd
|
||
```
|
||
|
||
也可以指定配置文件路径:
|
||
|
||
```bash
|
||
managerd path/to/managerd.json
|
||
```
|
||
|
||
在当前仓库的 Windows 开发环境中,推荐使用统一脚本入口:
|
||
|
||
```bat
|
||
scripts\managerd.bat build
|
||
scripts\managerd.bat start
|
||
scripts\managerd.bat stop
|
||
scripts\managerd.bat restart
|
||
scripts\managerd.bat status
|
||
```
|
||
|
||
各动作说明:
|
||
|
||
- `build`: 编译 `.\cmd\managerd`,生成根目录下的 `managerd.exe`
|
||
- `start`: 启动当前仓库中的 `managerd.exe`,并检查 `/health`
|
||
- `stop`: 停止当前仓库对应的 `managerd.exe`
|
||
- `restart`: 先停止再启动
|
||
- `status`: 查看进程状态与健康检查结果
|
||
|
||
脚本实际入口文件:
|
||
|
||
- `scripts/managerd.bat`
|
||
- `scripts/managerd.ps1`
|
||
|
||
程序启动后:
|
||
|
||
- `GET /` 会重定向到 `/ui`
|
||
- `GET /ui` 为内嵌管理页面
|
||
- `GET /openapi.json` 返回 OpenAPI 描述
|
||
- `GET /health` 返回健康检查结果
|
||
|
||
当前仓库中的示例配置监听地址为 `0.0.0.0:18080`。
|
||
|
||
## 依赖
|
||
|
||
- Go `1.23.3`
|
||
- `github.com/go-chi/chi/v5`
|
||
- `github.com/go-chi/cors`
|
||
- `github.com/google/uuid`
|
||
|
||
## 主要模块
|
||
|
||
### Discovery
|
||
|
||
- 向所有可用广播网卡发送 UDP discover 请求
|
||
- 在本地临时 UDP 端口监听回复
|
||
- 按 `device_id` 去重,并使用最新回复更新注册表
|
||
|
||
### Device Registry
|
||
|
||
- 以内存维护设备列表
|
||
- 每 2 秒执行一次离线判定
|
||
- 超过 `offline_after_ms` 未更新则标记为离线
|
||
- 每 30 秒对在线设备拉取一次 `GET /v1/graphs`,更新 `graphs` 字段
|
||
- 除 UDP 发现外,成功代理调用设备接口时也会刷新设备 `last_seen_ms`
|
||
|
||
### Agent Client
|
||
|
||
- 默认 HTTP 请求超时为 3 秒
|
||
- 对配置下发、模型上传、`/v1/config/ui/*`、人脸库上传等长操作使用 120 秒超时
|
||
- 自动附带全局 `X-RK-Token`
|
||
|
||
### Template Service
|
||
|
||
- 从本地 `templates/` 目录加载 `.json` 模板
|
||
- 返回模板列表和单个模板详情
|
||
- 当前没有实现 embed 模板加载
|
||
|
||
### Task Runner
|
||
|
||
- 支持并发执行,默认并发数为 `concurrency`
|
||
- 支持 SSE 推送逐设备状态
|
||
- 当前支持的任务类型:
|
||
- `config_apply`
|
||
- `reload`
|
||
- `rollback`
|
||
- `media_start`
|
||
- `media_restart`
|
||
- `media_stop`
|
||
|
||
## API 概览
|
||
|
||
### 基础接口
|
||
|
||
- `GET /health`
|
||
- `GET /openapi.json`
|
||
- `GET /` -> 302 跳转到 `/ui`
|
||
- `GET /ui`
|
||
|
||
### Discovery
|
||
|
||
#### `POST /api/discovery/search`
|
||
|
||
Request:
|
||
|
||
```json
|
||
{ "timeout_ms": 1200 }
|
||
```
|
||
|
||
Response:
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"device_id": "...",
|
||
"hostname": "...",
|
||
"ip": "...",
|
||
"agent_port": 9100,
|
||
"media_port": 9000,
|
||
"device_name": "...",
|
||
"version": "...",
|
||
"git_sha": "...",
|
||
"uptime_sec": 0,
|
||
"last_seen_ms": 0,
|
||
"online": true
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
### Devices
|
||
|
||
#### `GET /api/devices`
|
||
|
||
返回当前注册表中的设备列表:
|
||
|
||
```json
|
||
{
|
||
"items": [
|
||
{
|
||
"device_id": "...",
|
||
"hostname": "...",
|
||
"ip": "...",
|
||
"agent_port": 9100,
|
||
"media_port": 9000,
|
||
"device_name": "...",
|
||
"version": "...",
|
||
"git_sha": "...",
|
||
"uptime_sec": 0,
|
||
"last_seen_ms": 0,
|
||
"online": true,
|
||
"graphs": {}
|
||
}
|
||
]
|
||
}
|
||
```
|
||
|
||
#### `POST /api/devices`
|
||
|
||
手工添加一个设备到注册表:
|
||
|
||
```json
|
||
{
|
||
"device_id": "demo-device",
|
||
"device_name": "Demo",
|
||
"ip": "192.168.1.10",
|
||
"agent_port": 9100,
|
||
"media_port": 9000
|
||
}
|
||
```
|
||
|
||
#### `GET /api/devices/{id}`
|
||
|
||
直接返回该设备对象。
|
||
|
||
### 设备代理接口
|
||
|
||
以下接口由 managerd 转发到设备端 agent:
|
||
|
||
- `GET /api/devices/{id}/info` -> `GET /v1/info`
|
||
- `POST /api/devices/{id}/reload` -> `POST /v1/media-server/reload`
|
||
- `POST /api/devices/{id}/rollback` -> `POST /v1/media-server/rollback`
|
||
- `GET /api/devices/{id}/graphs` -> `GET /v1/graphs`
|
||
- `GET /api/devices/{id}/graphs/{name}` -> `GET /v1/graphs/{name}`
|
||
- `GET /api/devices/{id}/logs?limit=200` -> `GET /v1/logs/recent?limit=200`
|
||
- `POST /api/devices/{id}/config/apply` -> `PUT /v1/config`
|
||
- `GET /api/devices/{id}/models` -> `GET /v1/models`
|
||
- `POST /api/devices/{id}/media-server/start` -> `POST /v1/media-server/start`
|
||
- `POST /api/devices/{id}/media-server/restart` -> `POST /v1/media-server/restart`
|
||
- `POST /api/devices/{id}/media-server/stop` -> `POST /v1/media-server/stop`
|
||
- `GET /api/devices/{id}/media-server/status` -> `GET /v1/media-server/status`
|
||
|
||
#### 通用透传
|
||
|
||
- `ANY /api/devices/{id}/v1/*`
|
||
|
||
该路由会将 `/api/devices/{id}` 之后的路径原样透传到设备端 agent,可用于:
|
||
|
||
- `/v1/config`
|
||
- `/v1/config/ui/schema`
|
||
- `/v1/config/ui/state`
|
||
- `/v1/config/ui/plan`
|
||
- `/v1/config/ui/apply`
|
||
- `/v1/face-gallery`
|
||
- `/v1/face-gallery/reload`
|
||
- 以及其他已存在的 `/v1/*` 接口
|
||
|
||
### 模型上传
|
||
|
||
#### `POST /api/devices/{id}/models/upload`
|
||
|
||
请求类型:`multipart/form-data`
|
||
|
||
字段:
|
||
|
||
- `name`: 模型名
|
||
- `file`: 二进制文件
|
||
|
||
行为:managerd 读取上传文件,并转发为设备端 agent 的 `PUT /v1/models/{name}`。
|
||
|
||
### Templates
|
||
|
||
- `GET /api/templates`
|
||
- `GET /api/templates/{name}`
|
||
|
||
当前模板数据来自本地 `templates/*.json`。
|
||
|
||
### Tasks
|
||
|
||
#### `POST /api/tasks`
|
||
|
||
Request:
|
||
|
||
```json
|
||
{
|
||
"type": "config_apply",
|
||
"device_ids": ["device-a", "device-b"],
|
||
"payload": {
|
||
"config": {}
|
||
}
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- `config_apply` 会将 payload 作为配置内容下发到 `/v1/config`
|
||
- `media_start` 和 `media_restart` 可选接收 `{"config":"xxx"}` 形式的 payload
|
||
|
||
Response:
|
||
|
||
```json
|
||
{ "task_id": "..." }
|
||
```
|
||
|
||
#### `GET /api/tasks`
|
||
|
||
返回当前内存中的任务列表。
|
||
|
||
#### `GET /api/tasks/{id}/events`
|
||
|
||
SSE 事件名为 `device_update`,数据格式:
|
||
|
||
```json
|
||
{
|
||
"device_id": "...",
|
||
"status": "running",
|
||
"progress": 0.0,
|
||
"error": ""
|
||
}
|
||
```
|
||
|
||
## 配置文件
|
||
|
||
`managerd.json` 示例:
|
||
|
||
```json
|
||
{
|
||
"listen": "0.0.0.0:18080",
|
||
"discovery_port": 35688,
|
||
"discovery_timeout_ms": 1200,
|
||
"offline_after_ms": 10000,
|
||
"agent_token": "CHANGE_ME",
|
||
"concurrency": 5
|
||
}
|
||
```
|
||
|
||
字段说明:
|
||
|
||
- `listen`: managerd 监听地址
|
||
- `discovery_port`: 设备发现广播端口
|
||
- `discovery_timeout_ms`: 搜索超时
|
||
- `offline_after_ms`: 多久未更新则视为离线
|
||
- `agent_token`: 转发到设备端 agent 的统一 token
|
||
- `concurrency`: 批量任务并发数
|
||
|
||
## 目录说明
|
||
|
||
- `cmd/managerd`: 程序入口
|
||
- `internal/api`: API 路由与处理器
|
||
- `internal/service`: discovery、registry、task、template、agent client
|
||
- `internal/web`: 内嵌 Web UI
|
||
- `templates`: 本地模板
|
||
- `scripts/deploy`: 部署脚本
|
||
|
||
## 验证现状
|
||
|
||
当前仓库已通过:
|
||
|
||
```bash
|
||
go test ./...
|
||
```
|