# 设备端远程管理接口清单表(V1:对外 rk3588-agent;对内 media-server) > 目标:供管理端(Go)与设备端联调对照。 > > 约定: > - 对外:`rk3588-agent` HTTP 端口默认 `9100`(可配置),UDP 发现端口默认 `35688`。 > - 对内:`media-server` 继续提供现有 `/api/*`(默认 9000),由 agent 本机调用。 --- ## 0. 通用规范 ### 0.1 基础 URL - Agent Base: `http://:` - Media-server Base(仅 agent 使用): `http://127.0.0.1:` ### 0.2 鉴权(agent 对外) - Header:`X-RK-Token: ` - **必须鉴权**:所有写接口(PUT/POST 会改变设备状态或写盘) - **读接口**:默认可不鉴权;若 `agent.require_token_for_read=true` 则也必须鉴权 ### 0.3 统一响应格式 - 成功(通用):`{"ok":true}` - 失败(通用):`{"error":""}`(与现有 `ErrorJson()` 风格一致) ### 0.4 统一错误码/HTTP 状态(agent 对外) agent 不要求返回结构化 error_code(保持 `{"error":...}`),但**要求 HTTP 状态码语义稳定**: | 场景 | HTTP | error.message 示例 | |---|---:|---| | 未鉴权/Token 错误/缺失 | 401 | `unauthorized` | | JSON 解析失败 / 字段缺失 / 校验失败 | 400 | `invalid json: ...` / `validation failed: ...` | | 资源不存在 | 404 | `not found` | | 方法不允许 | 405 | `method not allowed` | | 冲突(如 node_id 不唯一等) | 409 | `... not unique ...` | | 写盘失败/内部异常/reload 崩溃性失败 | 500 | `internal error: ...` | | 上传过大 | 413(推荐)或 400 | `payload too large` | ### 0.5 端口与协议 - UDP 发现:`35688/udp`(可配置) - Agent HTTP:`9100/tcp`(默认,可配置) - Media-server HTTP:`9000/tcp`(默认,可配置) --- ## 1. UDP 发现协议(Option A) ### 1.1 Discover 请求(manager → broadcast) **发送目标**:同网段广播地址 `255.255.255.255:35688` 或各网卡广播地址 **数据格式**:两行 UTF-8 文本 Line1(固定魔法串): ``` RK3588SYS_DISCOVERY_V1 ``` Line2(JSON): ```json {"type":"discover","req_id":"","reply_port":0} ``` 字段: - `type`: 固定 `discover` - `req_id`: 管理端生成 UUID,用于匹配回复 - `reply_port`: 保留字段(V1 可固定 0) ### 1.2 Discover 回复(device → manager 单播) **发送目标**:请求报文 source ip:source port 两行文本: Line1: ``` RK3588SYS_DISCOVERY_V1 ``` Line2(JSON): ```json { "type":"discover_reply", "req_id":"", "device_id":"rk3588-...", "device_name":"rk3588-cam-01", "hostname":"rk3588", "ip":"10.0.0.21", "agent_port":9100, "media_port":9000, "version":"0.0.0-dev", "build_id":"20260418.141245", "build_type":"release", "git_sha":"e5894c2", "uptime_sec":12345 } ``` 字段说明: - `device_id`: 稳定唯一(优先 `/etc/machine-id`,否则 MAC/序列号;最后 fallback 生成并落盘) - `device_name`: 可配置的人类可读名称(agent 配置 `device_name`) - `agent_port`: agent HTTP 端口 - `media_port`: media-server HTTP 端口(用于调试/展示;管理端可不直连) --- ## 2. 设备信息(agent 对外) ### 2.1 `GET /v1/info` 用途:设备列表/详情页展示、联调确认端口/版本/配置路径。 **Auth**:读接口(见 0.2) Response 200: ```json { "device_id":"rk3588-...", "device_name":"rk3588-cam-01", "hostname":"rk3588", "ip":"10.0.0.21", "agent_port":9100, "media_port":9000, "version":"0.0.0-dev", "build_id":"20260418.141245", "build_type":"release", "git_sha":"e5894c2", "config_path":"/etc/rk3588sys/config.json", "last_good_path":"/etc/rk3588sys/config.json.last_good.json", "uptime_sec":12345 } ``` 失败:401/500 + `{"error":"..."}` --- ## 3. 配置下发(agent 对外) ### 3.1 `PUT /v1/config` 用途:管理端上传完整 root config(可含 templates/instances),agent 原子写盘后触发 `media-server` reload。 **Auth**:必须(401) Headers: - `Content-Type: application/json` - `X-RK-Token: ...` Body:root config JSON agent 处理步骤(必须满足): 1) 解析 JSON(语法有效即可;语义校验交由 media-server reload) 2) 原子写入 `config_path` 3) 调用 media-server:`POST /api/config/reload` 4) 若 reload 失败:调用 media-server:`POST /api/config/rollback`,并返回 500(包含 reload/rollback 错误信息) Response 200: ```json {"ok":true} ``` 失败: - 400:JSON 解析失败 - 500:写盘失败 / reload 失败(已尝试 rollback) --- ## 4. 模型管理(agent 对外) ### 4.1 `PUT /v1/models/{name}` 用途:上传模型文件并落盘,维护 manifest,返回可引用的 `path`。 **Auth**:必须(401) Path params: - `name`: string(建议仅允许 `[A-Za-z0-9._-]`,超出则 400) Headers: - `Content-Type: application/octet-stream` - `Content-Length: `(必须) - `X-RK-Token: ...` - `X-Model-Sha256: `(可选;若存在必须匹配实际 sha256,否则 400) Body:二进制文件(建议限制扩展名白名单 `.rknn`;V1 可由 name 或内容类型控制) Response 200: ```json { "ok": true, "name": "yolov5s-640", "sha256": "...", "path": "/opt/rk3588sys/models/files/yolov5s-640__abcd.rknn", "size": 12345678 } ``` 失败: - 400:缺 Content-Length / name 非法 / sha256 不匹配 - 413(推荐)或 400:超过 `max_upload_mb` - 500:写盘失败/manifest 更新失败 ### 4.2 `GET /v1/models` 用途:列出设备端已有模型。 **Auth**:读接口(见 0.2) Response 200: ```json { "items": [ { "name": "yolov5s-640", "sha256": "...", "path": "/opt/rk3588sys/models/files/...", "size": 123, "mtime_ms": 1730000000000 } ] } ``` 失败:500 + `{"error":"..."}` --- ## 5. 业务图热更新/回滚(agent 对外) ### 5.1 `POST /v1/media-server/reload` 用途:触发本机 media-server `POST /api/config/reload`。 **Auth**:必须(401) Response 200:`{"ok":true}` 失败:500 + `{"error":"..."}` ### 5.2 `POST /v1/media-server/rollback` 用途:触发本机 media-server `POST /api/config/rollback`。 **Auth**:必须(401) Response 200:`{"ok":true}` 失败:500 + `{"error":"..."}` ### 5.3 `PUT /v1/media-server/configs/{name}` 用途:上传 media-server 配置文件到 `agent.media_server_process.configs_dir`。 **Auth**:必须(401) Headers: - `Content-Type: application/json` - `X-RK-Token: ...` Path params: - `name`: string(仅允许 `[A-Za-z0-9._-]`,禁止 `/`、`\\`、`..`;若无 `.json` 后缀则自动追加) Body:media-server 配置 JSON Response 200: ```json { "ok": true, "name": "cam1.json", "path": "/opt/rk3588sys/configs/cam1.json", "size": 1234, "mtime_ms": 1730000000000 } ``` 失败: - 400:name 非法 / Content-Type 非 application/json / JSON 无效 / 空 body - 401:unauthorized - 413:超过 `max_upload_mb` - 501:`configs_dir` 未配置 - 500:写盘失败 ### 5.4 `PUT /v1/media-server/binary` 用途:更新 media-server 可执行文件(仅允许在 media-server 未运行时覆盖)。 **Auth**:必须(401) Headers: - `Content-Type: application/octet-stream` - `Content-Length: `(必须) - `X-RK-Token: ...` - `X-Binary-Sha256: `(可选;若存在必须匹配实际 sha256,否则 400) Body:二进制文件(media-server 可执行文件) Response 200: ```json { "ok": true, "path": "/opt/rk3588sys/bin/media-server", "sha256": "...", "size": 12345678, "mtime_ms": 1730000000000, "backup_path": "/opt/rk3588sys/bin/media-server.bak.20260117-123000" } ``` 失败: - 400:缺 Content-Length / Content-Type 非 application/octet-stream / sha256 不匹配 - 401:unauthorized - 409:media-server 正在运行(需先 stop) - 413:超过 `max_upload_mb` - 501:未启用进程控制(agent 配置 `agent.media_server_process.enable=false` 或未配置) - 500:写盘/替换失败 ## 6. 主程序进程控制(agent 对外) > 说明:该能力用于“启动/重启/关闭主程序(media-server)并选择加载哪个配置文件”。 > > agent 启动 media-server 时会显式传入:`--config `,因此不依赖 media-server 内部默认配置。 > > 路径说明: > - `agent.media_server_process` 下的 `exec_path/work_dir/configs_dir/pid_file` 支持绝对或相对路径;若为相对路径,则以 agent 启动参数 `--config ` 的**配置文件所在目录**为基准解析。 > - `agent.config_path` 建议使用**绝对路径**;若使用相对路径: > - `PUT /v1/config` 写盘时,以 **agent 进程当前工作目录(CWD)** 为基准; > - 通过 `/v1/media-server/start|restart`(缺省 config)启动时,会把该相对路径原样传给 media-server,最终以 **media_server_process.work_dir** 为基准解析。 ### 6.1 `POST /v1/media-server/start` 用途:启动本机 media-server(若已运行则幂等返回 ok;若已运行但 config 不同则 409)。 **Auth**:必须(401) Body(可选,JSON): ```json {"config":"cam1"} ``` 说明:若请求 body 非空,则必须带 `Content-Type: application/json`;若 body 为空,可不带该 header。 `config` 解析规则: - 为空/缺省:使用 `agent.config_path` - 非空:只允许文件名/配置名(禁止包含 `/`、`\\`、`..`);若不带扩展名自动补 `.json`;最终从 `agent.media_server_process.configs_dir` 下解析为 `/.json` Response 200: ```json {"ok":true,"running":true,"pid":1234,"config_path":"/etc/rk3588sys/config.json"} ``` 失败: - 400:config 不合法 / config 文件不存在 - 409:已运行但 config 不同(提示使用 restart) - 501:未启用进程控制(agent 配置 `agent.media_server_process.enable=false` 或未配置) - 500:启动失败 / 写 pidfile 失败 ### 6.2 `POST /v1/media-server/restart` 用途:重启本机 media-server(可切换 config)。 **Auth**:必须(401) Body(可选,JSON): ```json {"config":"cam1"} ``` 说明:若请求 body 非空,则必须带 `Content-Type: application/json`;若 body 为空,可不带该 header。 Response 200: ```json {"ok":true,"running":true,"pid":1234,"config_path":"/home/orangepi/Desktop/OrangePi3588Media/configs/cam1.json"} ``` 失败: - 400:config 不合法 / config 文件不存在 - 501:未启用进程控制 - 500:停止/启动失败 ### 6.3 `POST /v1/media-server/stop` 用途:停止本机 media-server(未运行也返回 ok)。 **Auth**:必须(401) Response 200: ```json {"ok":true,"running":false,"pid":1234,"config_path":"/etc/rk3588sys/config.json"} ``` 失败: - 501:未启用进程控制 - 500:停止失败 ### 6.4 `GET /v1/media-server/status` 用途:查询本机 media-server 是否在运行(以 agent 的 pidfile 记录为准)。 **Auth**:读接口(见 0.2) Response 200: ```json {"ok":true,"running":true,"pid":1234,"config_path":"/etc/rk3588sys/config.json","version":"RK3588 Media Server v0.1.0 (git abc1234)"} ``` 失败: - 501:未启用进程控制 - 500:读取 pidfile/进程存活检测失败 ## 7. 只读代理接口(agent 对外,推荐管理端统一走 agent) ### 7.1 `GET /v1/graphs` 代理 media-server:`GET /api/graphs` ### 7.2 `GET /v1/graphs/{name}` 代理 media-server:`GET /api/graphs/{name}` ### 7.3 `GET /v1/logs/recent?limit=200` 代理 media-server:`GET /api/logs/recent?limit=...`