diff --git a/Readme.md b/Readme.md index 2c296c3..5990810 100644 --- a/Readme.md +++ b/Readme.md @@ -1,64 +1,96 @@ -# PRD ③ 管理端后端(Go managerd)V1 +# managerd -## 1. 目标与运行方式 +`managerd` 是 RK3588 管理端后端服务,负责设备发现、设备注册表维护、代理访问设备端 `rk3588-agent`,并提供内嵌 Web UI、OpenAPI 页面和 HTTP API。 -- 提供本机 HTTP API 给 React UI 使用。 -- 负责 UDP 广播发现(Option A),维护设备缓存与在线状态。 -- 通过设备端 `rk3588-agent` 完成配置/模型等运维操作,并可通过 agent 代理读取 graphs/logs。 -- 支持批量任务(并发、进度、失败原因)并通过 SSE 推送。 +## 当前状态 -运行:单可执行 `managerd`,默认监听 `127.0.0.1:18080`(可配置)。 +- 提供 HTTP API,供前端或其他本地工具调用。 +- 内嵌 Web UI,启动后可直接在浏览器访问。 +- 支持 UDP 广播发现设备,并维护内存中的设备在线状态。 +- 支持代理访问设备端 agent 的常用接口,以及 `/v1/*` 通用透传。 +- 支持批量任务执行,并通过 SSE 推送逐设备状态。 +- 模板从本地 `templates/*.json` 读取。 -## 2. 外部依赖与约束 +## 运行方式 -- Go 版本:>= 1.22(建议) -- 标准库优先;Web 框架可选 `chi`/`gin`(建议 chi + net/http)。 -- 不需要数据库(V1 用内存 + 可选本地 JSON 持久化)。 +默认通过单个可执行文件运行: -## 3. 模块划分 +```bash +managerd +``` -### 3.1 Discovery +也可以指定配置文件路径: -- 向所有可用网卡的广播地址发送 UDP discover。 -- 监听本地 UDP socket 收集 replies(时间窗默认 1200ms)。 -- 去重规则:按 `device_id` 去重,以最新 reply 为准。 +```bash +managerd path/to/managerd.json +``` -### 3.2 Device Registry +程序启动后: -- 内存缓存: - - `device_id -> {ip, agent_port, media_port, device_name, version, git_sha, last_seen_ms, online}` -- 定时刷新(可配置间隔):对 online 设备拉取 `GET /v1/graphs`(agent 代理),更新摘要。 -- Offline 规则:超过 `offline_after_ms`(如 10000ms)未见到,则标记离线。 +- `GET /` 会重定向到 `/ui` +- `GET /ui` 为内嵌管理页面 +- `GET /openapi.json` 返回 OpenAPI 描述 +- `GET /health` 返回健康检查结果 -### 3.3 Device Client +当前仓库中的示例配置监听地址为 `0.0.0.0:18080`。 -- 统一超时:connect 1s、overall 3s(可配置)。 -- 统一错误包装:返回 `error_code + message + device_id`。 -- Token: - - 全局默认 token(V1) - - 可预留 per-device token(P1) +## 依赖 -### 3.4 Templates/Config Builder +- Go `1.23.3` +- `github.com/go-chi/chi/v5` +- `github.com/go-chi/cors` +- `github.com/google/uuid` -- 模板库来源: - - V1:managerd 内置(embed)或本地 `templates/` 目录读取 -- 返回前端表单 schema:V1 允许手工维护(避免解析占位符带来的不确定性)。 -- 生成 root config:基于模板与 params,产出 `{global,templates,instances}`。 +## 主要模块 -### 3.5 Task Runner +### Discovery -- 任务类型: - - `config_apply`(对 N 台设备下发 config) +- 向所有可用广播网卡发送 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` - - `model_upload`(V1 可先做单设备;批量后续) -- 并发控制:默认 5(可配置)。 -- 状态:`pending/running/success/failed`(逐设备与整体)。 -- 推送:SSE `GET /api/tasks/:id/events`。 + - `media_start` + - `media_restart` + - `media_stop` -## 4. managerd 对前端 API 规格(V1) +## API 概览 -### 4.1 Discovery +### 基础接口 + +- `GET /health` +- `GET /openapi.json` +- `GET /` -> 302 跳转到 `/ui` +- `GET /ui` + +### Discovery #### `POST /api/discovery/search` @@ -71,62 +103,123 @@ Request: Response: ```json -{ "items": [ {"device_id":"...","ip":"...","agent_port":9100,"media_port":9000,"device_name":"...","version":"...","git_sha":"..."} ] } +{ + "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 + } + ] +} ``` -### 4.2 Devices +### Devices #### `GET /api/devices` -Response: +返回当前注册表中的设备列表: ```json -{ "items": [ {"device_id":"...","online":true,"last_seen_ms":0,"ip":"...","agent_port":9100,"media_port":9000,"device_name":"...","version":"...","git_sha":"...","graphs":[...]} ] } +{ + "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": {} + } + ] +} ``` -#### `GET /api/devices/:id` +#### `POST /api/devices` -Response:包含 `info`、`graphs_summary`、`last_seen`。 - -### 4.3 Device actions(代理调用) - -以下全部通过 agent: - -- `POST /api/devices/:id/reload` → agent `POST /v1/media-server/reload` -- `POST /api/devices/:id/rollback` → agent `POST /v1/media-server/rollback` -- `GET /api/devices/:id/graphs` → agent `GET /v1/graphs` -- `GET /api/devices/:id/graphs/:name` → agent `GET /v1/graphs/{name}` -- `GET /api/devices/:id/logs?limit=200` → agent `GET /v1/logs/recent?limit=200` - -### 4.4 Config apply - -#### `POST /api/devices/:id/config/apply` - -Request: +手工添加一个设备到注册表: ```json -{ "config": { } } +{ + "device_id": "demo-device", + "device_name": "Demo", + "ip": "192.168.1.10", + "agent_port": 9100, + "media_port": 9000 +} ``` -Behavior:调用 agent `PUT /v1/config`。 +#### `GET /api/devices/{id}` -### 4.5 Model upload +直接返回该设备对象。 -#### `POST /api/devices/:id/models/upload` +### 设备代理接口 -Request:`multipart/form-data`,字段: +以下接口由 managerd 转发到设备端 agent: -- `name`: string -- `file`: binary +- `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` -Behavior:读取文件流,转发为 agent `PUT /v1/models/{name}`(raw body)。 +#### 通用透传 -### 4.6 Templates +- `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` +- `GET /api/templates/{name}` -### 4.7 Tasks +当前模板数据来自本地 `templates/*.json`。 + +### Tasks #### `POST /api/tasks` @@ -135,51 +228,48 @@ Request: ```json { "type": "config_apply", - "device_ids": ["..."], - "payload": { "config": {} } + "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/:id/events` (SSE) +#### `GET /api/tasks` -Event `device_update` data: +返回当前内存中的任务列表。 -```json -{ "device_id":"...","status":"running|success|failed","progress":0.0,"error":"" } -``` +#### `GET /api/tasks/{id}/events` -## 5. 错误处理规范(managerd → 前端) - -- 成功:2xx + `{"ok":true}` 或正常业务 JSON -- 失败:4xx/5xx + - -```json -{ "error": { "code": "...", "message": "...", "device_id": "...", "detail": "..." } } -``` - -建议错误码: - -- `DISCOVERY_FAILED` -- `DEVICE_NOT_FOUND` -- `DEVICE_OFFLINE` -- `DEVICE_HTTP_ERROR` -- `DEVICE_TIMEOUT` -- `TASK_NOT_FOUND` -- `VALIDATION_ERROR` - -## 6. 配置文件(managerd)建议 - -`managerd.json`: +SSE 事件名为 `device_update`,数据格式: ```json { - "listen": "127.0.0.1:18080", + "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, @@ -188,9 +278,28 @@ Event `device_update` data: } ``` -## 7. 验收标准 +字段说明: -1. Search 可发现设备并更新 registry;离线判断正确。 -2. 可通过 agent 读取 graphs/logs(`GET /v1/graphs`、`GET /v1/logs/recent`)。 -3. 单设备 `config/apply`、`reload`、`rollback` 可用,错误可定位。 -4. 批量 `config_apply` 任务可并发执行并通过 SSE 输出逐台结果。 +- `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 ./... +```