设备管控能力规划
本文是 能力评估 + 实现蓝图(2026-07-05)。把现有「只读监控」升级为「可操作管控」:服务器/NAS/摄像头的文件操作、状态查看、电源控制、摄像头录制回放。评估已认可,本文档作为后续实现的依据。
背景与范围
源自「NAS 页面能否升级为运维管理」的讨论。经过对现有代码的核对,真实需求收敛为三类设备的 8 个管控动作,而非通用运维平台(不需要 WebShell / 进程 / 容器 / 服务管理那些重活)。
| 设备 | 动作 |
|---|---|
| 🖥️ 服务器 | 操作文件 / 看状态 / 重启 |
| 🗄️ NAS | 操作文件 / 看状态 / 关机·重启 |
| 📹 摄像头 | 实时画面 / 回放(录制) |
结论:8 项中 4 项已完成、3 项低成本低扩展、真正的工程量只在「摄像头录制回放」一项。
当前能力盘点(已有资产)
| 层 | 能力 | 位置 |
|---|---|---|
| 主机采集 | SSH 跑 /proc 脚本采 CPU/内存/磁盘/网络/负载;或 Agent 上报 gopsutil | ssh.go、sysinfo.go |
| NAS 采集 | Agent 上报(飞牛/绿联等国产 NAS)、群晖 Synology API、TrueNAS REST API、SNMP fallback | agent_ws.go、nas_synology.go、nas_truenas.go、nas_snmp.go |
| 摄像头实时 | ONVIF 取流 + RTSP→WS(jessibuca 解码) | camera.go、rtsp_session.go |
| 文件管理 | SMB + WebDAV + Alist + Agent 四后端 | filemanager/ |
| Agent RPC | 双向 WS,cmd/resp/data 三通道 + caps 协商 + io.Pipe 流式 | agent_ws.go |
| 凭证 | SSH 私钥/密码双通道,AES 加密存储,TOFU hostkey | ssh.go:87-168 |
| 告警/通知 | 阈值状态机 + 去重冷却 + 通知中心 13 类渠道 | alert.go + internal/notify |
关键设计洞察
🔑 SSH 通道已经是通用 exec
ssh.go:173-191 的 sshExec.Run(ctx, cmd) 早已存在,SSHCollector 一直用它跑采集脚本。这意味着 「重启服务器」根本不需要扩展协议、不需要 WebShell —— 直接复用现有 SSH 连接跑一条 sudo -n reboot 即可。
把电源操作限定为 reboot / shutdown 两个白名单动作,安全风险从「开放任意命令」降为「两个固定命令」,且无需触碰 Agent 协议。
🔑 Agent 协议可扩展但本次用不到
agent_ws.go:51-61 的 AgentEnvelope 是开放结构,加 Op:exec + CapabilityExec 是约定式扩展。但 本规划范围不需要 —— 文件操作走现有 Agent 文件 RPC,电源操作走 SSH/厂商 API。Agent exec 通道留作未来「进程 kill / 容器启停」的入口。
逐项实现路径
🖥️ 服务器
| 动作 | 现状 | 实现 | 工作量 |
|---|---|---|---|
| 操作文件 | 🟡 看部署 | 装了 Agent 的服务器 → 直接复用 agent.go 文件 RPC;只能 SSH 的 → 补 SFTP 后端(可后置) | 0 / +2-3 天 |
| 看状态 | ✅ | 已有 | 0 |
| 重启 | 🟢 极轻 | 复用 sshExec.Run("sudo -n reboot"),加电源 API | 0.5-1 天 |
SFTP 后端(可选,后置):用 github.com/pkg/sftp(基于 golang.org/x/crypto/ssh,SSH client 已有),实现 filemanager.Backend 接口。优先推荐「服务器装 Agent 统一走 Agent 文件管理」,SFTP 仅作「只能 SSH 不能装 Agent」的兜底。
🗄️ NAS
| 动作 | 现状 | 实现 | 工作量 |
|---|---|---|---|
| 操作文件 | ✅ | SMB(群晖/威联通/飞牛原生支持)+ WebDAV | 0 |
| 看状态 | ✅ | 厂商 API 采集 | 0 |
| 关机·重启 | 🟢 轻 | 见下方分发 | 1-2 天 |
电源操作按 NAS 协议分发:
| NAS 协议 | 关机/重启方式 |
|---|---|
| api(群晖) | 复用现成的 synoCall 调 SYNO.Core.System.Power,method=shutdown / reboot |
| api(TrueNAS) | REST API POST /system/reboot / POST /system/shutdown(TrueNAS v2.0+) |
| snmp | 不支持(协议限制),UI 隐藏电源按钮 |
| 兜底 | SSH sudo -n shutdown |
📹 摄像头
| 动作 | 现状 | 实现 | 工作量 |
|---|---|---|---|
| 实时画面 | ✅ | 已有 RTSP→WS + jessibuca | 0 |
| 录制回放 | 🔴 缺口 | 方案 B:服务端 FFmpeg 录制(见下) | 8-12 天 |
摄像头录制回放 · 方案 B 详细设计
服务端常驻 FFmpeg 把 RTSP 录成 HLS 分片存盘,前端时间轴选时间 → 动态生成 m3u8 → jessibuca 播放。不依赖摄像头型号,能看历史录像。
录制格式
HLS(.ts 分片,2-4 秒一片,-c copy 直拷流不重编码,省 CPU;前提是摄像头输出 H264/H265,绝大多数满足)。jessibuca 已支持 HLS,前端零新增依赖。
ffmpeg -rtsp_transport tcp -i {rtsp_url} \
-c copy -f hls \
-hls_time 3 -hls_list_size 0 \
-hls_segment_filename {dir}/%Y%m%d%H%M%S.ts \
-strftime 1 \
{dir}/live.m3u8存储布局
{PTPATRONUS_DATA_DIR}/recordings/
└── {camera_id}/
└── 2026-07-05/
├── 140000.ts
├── 140003.ts
├── 140006.ts
├── ...
└── live.m3u8 # FFmpeg 滚动写的(回放不直接用)回放时 动态生成 m3u8(从目标时间对应的分片开始),不复用 live.m3u8。
进程管理
新建 internal/nvr/ 包(不污染 monitor,避免循环依赖):
internal/nvr/
├── recorder.go # 单路录制 supervisor:启停 FFmpeg + 退出重启 + 指数退避
├── manager.go # cameraID → *recorder 的 map(进程内单实例,同 AgentWSHub 模式)
├── manifest.go # 扫目录生成回放 m3u8 + 有录像时段索引
└── retention.go # 每日清理超期录像(复用 app.go 的 cron)- 启动时扫表拉起所有
record_enabled=true的摄像头 - 摄像头增删改时动态启停(在
monitor.Service的 target 变更回调里 hook) - FFmpeg 进程退出 → 指数退避重启(摄像头短暂断流不致死)
- 全局录制路数上限(防磁盘/CPU 暴走,默认 16)
回放流程
前端: 用户在时间轴点 2026-07-05 14:30:00
↓
GET /api/v1/monitor/cameras/:id/playback.m3u8?time=2026-07-05T14:30:00Z
↓
后端 manifest.go:
1. 扫 recordings/{id}/2026-07-05/ 按文件名时间戳排序
2. 二分定位含 14:30:00 的分片
3. 从该分片起拼一个 m3u8(限制 N 片 = 约 N×3 秒窗口,VOD 而非 LIVE)
4. 返回 application/vnd.apple.mpegurl
↓
前端 jessibuca 加载该 m3u8,常规 HLS 播放
← 进度条拖动 = 重新请求新 time 的 m3u8时间轴索引
GET /api/v1/monitor/cameras/:id/recordings?date=2026-07-05 → 该日有录像的时段列表([{start, end}, ...],由扫 .ts 文件名首尾聚合)。前端渲染为时间轴条的可点区段。
存储估算(必看)
| 码率 | 单摄/小时 | 单摄/天 | 保留 7 天 |
|---|---|---|---|
| 1080p H264 ~2Mbps | ~900 MB | ~21.6 GB | ~150 GB |
| 720p ~1Mbps | ~450 MB | ~10.8 GB | ~76 GB |
4 路 1080p 录 7 天 ≈ 600 GB。必须:
- 录制默认关闭,按摄像头粒度显式开启
- 保留天数可配(默认 7 天)
- 录制目录可配到独立大盘(避免撑爆系统盘)
- UI 在开启录制时显示存储预估警示
API 设计
# 录制控制
POST /api/v1/monitor/cameras/:id/record {enabled, retention_days}
GET /api/v1/monitor/cameras/:id/record/status → {recording, pid, uptime, error}
# 回放
GET /api/v1/monitor/cameras/:id/recordings?date=YYYY-MM-DD → 时段列表
GET /api/v1/monitor/cameras/:id/playback.m3u8?time=RFC3339 → 动态 m3u8(直接喂 jessibuca)
# 分片静态资源(回放 m3u8 内部引用)
GET /api/v1/monitor/recordings/{camera_id}/{date}/{file}.ts → 文件(FileServer,带 admin 鉴权)数据模型扩展
MonitorTarget 增字段:
RecordEnabled bool `json:"record_enabled" gorm:"default:false"`
RetentionDays int `json:"retention_days,omitempty"` // 默认 7
RecordProfile string `json:"record_profile,omitempty"` // main | sub(主/子码流)前端
DeviceDetail加「电源操作」按钮组(重启/关机,二次确认)- 摄像头详情加「录像」开关 + 保留天数 + 存储预估
- 摄像头详情加「回放」Tab:时间轴(按日 + 时段条) + jessibuca 播放器(复用
RtspPlayer.vue,src 切换为动态 m3u8) - 移动端同设计(Flutter 复用现有播放器组件)
电源操作 · 统一 API
POST /api/v1/monitor/targets/:id/power {action: "reboot" | "shutdown"} RequireAdmin服务端按 target.type + ResolveProtocol(target) 分发到对应执行器(host→SSH、nas→厂商API、camera→不支持返回 400)。所有电源动作入库审计(新增 power_action_log 表 或 复用 system log)。
工作量与里程碑
| 里程碑 | 内容 | 人天 |
|---|---|---|
| M1 电源闭环 | 服务器 reboot + NAS 关机/重启(SSH + 群晖 + TrueNAS)+ 前端按钮 + 审计 | 2-3 |
| M2 录制内核 | internal/nvr/ 包:FFmpeg supervisor + manager + 配置项 + 启动 hook | 3-4 |
| M3 回放链路 | 动态 m3u8 生成 + 时段索引 API + ts FileServer + TTL 清理 cron | 3-4 |
| M4 前端回放 | 时间轴组件 + 回放 Tab + 录制开关 + 存储预估(Web + Flutter) | 2-3 |
| M5(可选) SFTP | filemanager SFTP 后端,给「只能 SSH」的服务器补文件操作 | 2-3 |
合计:M1-M4 约 10-14 人天(含录制回放);加 M5 约 12-17 人天。
安全设计
| 风险 | 对策 |
|---|---|
| 电源操作 = 高危 | 严格白名单 action ∈ {reboot, shutdown},不接受任意命令;RequireAdmin + 二次确认弹窗 + 入库审计 |
| SSH sudo 权限 | 采集脚本探测 sudo 能力,UI 提示用户配置 /etc/sudoers 的 NOPASSWD;root 登录则不需要 |
| 录制磁盘爆炸 | 默认关闭 + 保留天数 + 全局路数上限 + 独立盘可配 + 开启时存储预估警示 |
| 回放越权 | m3u8/ts 端点都走 admin 鉴权;ts 路径做 safepath 钳制(复用 filemanager 的 ALLOWED_ROOTS 模式) |
| FFmpeg 进程失控 | supervisor 指数退避 + 最大重启频率;崩了不拉垮主进程 |
依赖
| 依赖 | 用途 | 引入方式 |
|---|---|---|
ffmpeg(系统二进制) | RTSP→HLS 录制 | 运行时探测;Docker 镜像 apt install ffmpeg;类似 jessibuca 资源的手动部署策略 |
github.com/pkg/sftp(仅 M5) | SFTP 文件后端 | 纯 Go,基于已用的 golang.org/x/crypto/ssh |
待决策 / 风险
- FFmpeg 部署:服务端要装 FFmpeg。Docker 镜像需重打(加 ffmpeg),自部署需文档说明。是否接受这个新系统依赖?
- 录制存储位置:默认放
PTPATRONUS_DATA_DIR/recordings,还是强制要求用户配独立路径?推荐默认 + 可覆盖。 - 多副本部署:录制 manager 是进程内 map,多副本会重复录制。当前 PTPatronus 单副本为主,暂不处理,文档注明限制(同 AgentWSHub)。
- TrueNAS API 字段:关机/重启端点需真机校验(TrueNAS 版本间有差异)。
- 回放性能:扫目录生成 m3u8 在大量分片时可能慢,可加 manifest 缓存(按 camera+date 缓存,录制时增量更新)。
与现有模块的关系
- 不动 monitor 采集/告警/通知链路 —— 管控是叠加层
- 复用 filemanager(Agent/SMB)、notify(电源/录制告警)、SSH 通道、jessibuca、app.go cron、safepath 模式
- 新增
internal/nvr/包、电源 API handler、前端电源按钮 + 回放 Tab、MonitorTarget字段、审计表