www-server 后端网关
定位
www-server 是 WebUI 面向浏览器的后端边界,负责 HTTP API、WebSocket 实时通道和静态资源托管。它不直接承载时间线存储、MQTT 路由、插件运行等核心业务逻辑,而是复用已有子模块的稳定接口。
第一阶段目标:
- 为 WebUI 提供统一 HTTP API;
- 将时间线事件查询接到 Genapsed 分配的 Timeline MySQL 只读连接;
- 接入 MQTT 桥接,把组件状态、事件、日志和命令与浏览器 API 隔离;
- 为浏览器提供 WebSocket 实时事件广播;
- 托管
web-ui/dist静态产物; - 保持实现轻量,暂不引入额外 Web 框架依赖。
架构边界
Browser / WebUI
-> www-server HTTP API / WebSocket
-> Timeline MySQL read connection
-> Runtime State in-memory browser bridge cache
-> Genapsed JSONL logs
-> MQTT Bridge -> MQTT Broker / Genapsed 路由
约束:
- WebUI 不直接连接 MQTT Broker;
- WebUI 不直接读取数据库或文件系统;
- MySQL 承载正式记录、配置、时间线和可查询数据,初始化和权限由 Genapsed 负责;
- MQTT 承载事件、命令、ACK、结果和组件状态心跳;
- WebSocket 只面向浏览器推送后端桥接后的实时变化;
www-server不重新定义时间线事件模型;- 大文件仍通过 HTTP 数据面提供引用和授权访问,不能放进 MQTT 或 WebSocket 消息体;
- 错误应直接暴露为明确 JSON 错误,不使用 fallback 掩盖问题。
初版接口
| 方法 | 路径 | 说明 |
|---|---|---|
GET | /api/health | 服务健康检查 |
GET | /api/status | 健康检查别名 |
GET | /api/build-info | 返回 WebUI 构建信息、Git 提交和开发模式状态 |
GET | /api/components | 读取管理控制台组件清单、状态、字段元数据和当前配置 |
GET | /api/components/{component_id}/config | 读取单个组件配置和运行状态 |
POST | /api/components/{component_id}/config | 提交配置变更请求,由 Genapsed 校验、保存并触发重载 |
GET | /api/timeline/events | 查询时间线事件列表 |
GET | /api/timeline/events/{event_id} | 获取单个时间线事件 |
POST | /api/timeline/events | 向 Genapsed 发送 MiniVLM 风格时间线事件入库请求 |
GET | /api/logs | 查询 JSONL 运行日志 |
GET | /api/mqtt/bridge | 查询 MQTT 桥接状态 |
GET | /api/mqtt/messages | 查询最近 MQTT 入站消息 |
POST | /api/components/{component_id}/command | 发布组件 MQTT 命令 |
POST | /api/dev/mqtt/publish | 原始 MQTT 发布入口已关闭,避免绕过 Genapsed 权限 |
GET | /api/ws | WebSocket 实时通道 |
时间线查询参数
GET /api/timeline/events 支持:
start、end:Unix 时间戳;camera_id、event_type;subject/subjects;object/objects;action/actions;attention_required;keyword;limit、offset;order=desc|asc。
时间线写入
POST /api/timeline/events 可以直接接收 MiniVLM timeline_event_generated payload,也可以接收 Genapsed 风格信封:
{
"msg_id": "message-id",
"priority": "normal",
"metadata": {
"trace_id": "trace-id"
},
"payload": {
"event_id": "evt-001",
"camera_id": "living_room",
"time_range": {
"start": 1741000000,
"end": 1741000030
},
"summary": "有人把快递盒放在门口。",
"event_type": "object_moved"
}
}
请求成功只表示 MQTT 入库请求已发送。Genapsed 完成入库后发布 timeline_event_stored result,
服务再向 /api/ws 广播 timeline.event_created。
MQTT 桥接
后端桥接默认订阅:
everpast/+/status/+:组件状态心跳,写入进程内桥接缓存,并广播component.status_updated;everpast/+/event/+:事件消息只记录和广播,时间线入库由 Genapsed 处理;everpast/+/log/+:组件日志实时通知,校验并广播到 WebSocket,不替组件重复落盘;everpast/+/ack/+和everpast/+/result/+:记录到进程内桥接缓存,并转发给浏览器调试面板;timeline_event_storedresult:广播timeline.event_created。
UI 发出的管理配置变更会发布到:
everpast/www_server/config/normal/genapsed
UI 发出的组件命令会发布到:
everpast/www_server/cmd/<priority>/genapsed
配置变更由 Genapsed 校验权限、写入配置数据库、渲染目标组件配置文件并发布 reload_config。
组件命令也先提交给 Genapsed,由 Genapsed 校验后以自己的身份向目标组件发布标准命令。
配置读取使用 Genapsed 分配给 www_server 的 MySQL 只读连接,默认从
data/genapsed/database/credentials.json 的 system.www_server.sqlalchemy_url 读取,也可通过
EVERPAST_WWW_CONFIG_DATABASE_URL 覆盖。如果配置主库不可用,配置 API 会返回
config_database_unconfigured 或 config_database_unavailable,不会回退到本地默认配置。
www-server 这个仓库名含连字符,MQTT topic 中使用 www_server 作为后端网关组件 ID。
www-server 自身运行数据默认写入主仓库 data/www_server/,日志默认按天写入
data/logs/www_server/YYYY-MM-DD.jsonl。日志查询递归读取 data/logs,该根目录可通过
Genapsed 管理的组件配置或 EVERPAST_WWW_LOG_PATH 覆盖。
管理配置接口
当前代码内置以下管理组件定义,用于 WebUI 生成配置表单并展示运行状态:
| 组件 ID | 名称 | 当前状态 |
|---|---|---|
www_server | 后端网关 | 已接入管理控制台 |
web-ui | Web UI | 已接入管理控制台 |
timeline | 时间线存储 | 已接入管理控制台,数据库类型只支持 mysql |
genapsed | Genapsed 中枢 | 已接入管理控制台 |
mqtt | MQTT Broker | 已接入管理控制台,并驱动后端 MQTT 桥接配置 |
minivlm | MiniVLM | 已接入管理控制台,开发模式默认启用 worker |
nvr_bridge | NVR-Bridge | 已接入管理控制台,开发模式默认启用 Frigate worker |
frigate | Frigate NVR | 已接入管理控制台;开发模式默认由 Genapsed 渲染配置并启动容器 |
plugins | 插件系统 | 已接入管理控制台 |
POST /api/components/{component_id}/config 不直接写本地配置文件,也不直接修改目标组件。
它会先在 www-server 内完成字段归一化和基础校验,再通过 MQTT 发布
request_config_update 到 everpast/www_server/config/normal/genapsed。Genapsed 负责最终权限校验、
写入配置主库、渲染目标组件配置文件,并发布 reload_config。
WebUI 配置表单由 /api/components 返回的字段元数据动态渲染。当前已支持 text、password、
number、boolean、select、array 和 object 类型;数组和对象字段在前端以 JSON 文本编辑,
提交前解析为结构化值,后端再按字段类型做基础校验。
后续路线
- 接入 Genapsed 管理服务,开放插件、权限和完整生命周期接口;
- 增加媒体文件授权访问接口;
- 为 MQTT 命令增加 ACK 关联、超时重试和失败审计;
- 引入认证和局域网设备首次配置流程;
- 接入 Genapsed 分配的 MySQL 连接和数据库权限审计展示。
反向代理(Unified API Gateway)
www-server 内置反向代理能力,作为后端服务(如 Frigate NVR)的统一入口。这样鉴权、日志等横切关注点只需在网关层实现。
配置方式
通过环境变量 EVERPAST_WWW_PROXY_ROUTES 注入 JSON 路由配置:
[
{
"prefix": "/nvr",
"upstream": "http://127.0.0.1:5000",
"strip_prefix": true
}
]
prefix— 匹配的 URL 路径前缀,如/nvrupstream— 上游服务基础 URLstrip_prefix— 转发时是否去除 prefix(true→/nvr/api/stats转发为/api/stats)
静态资源路径重写
Frigate NVR 的 Web 前端使用绝对路径引用静态资源(如 <script src="/assets/main-xxx.js">)。当通过反向代理的子路径(如 /nvr/)访问时,浏览器会将这些路径解析为 http://host:8080/assets/xxx,导致 404。
www-server 在代理 HTML 响应时自动将 /assets/ 重写为路径前缀形式(如 /nvr/assets/),使得静态资源请求重新经过反向代理到达上游服务。同时重写 Frigate 的 window.baseUrl 以修正 API 调用路径。
实现模块
| 文件 | 职责 |
|---|---|
proxy.py | ProxyRoute、ReverseProxyConfig(路由匹配与解析)、ReverseProxy(aiohttp 异步请求转发) |
config.py | 从 EVERPAST_WWW_PROXY_ROUTES 环境变量解析配置 |
server.py | 在 create_app() 中优先注册普通路由,再动态注册代理路由 |