Files
lxc-streamutils/docs/architecture.md
T

89 lines
4.8 KiB
Markdown

# 系统架构
本文描述直播组件服务的运行边界、数据流和扩展点。实现代码分别位于 `apps/server-rust` 与
`apps/overlay`。
## 核心目标
- 每个账户拥有一个可切换的 Bilibili 直播间、一个 CookieCloud 来源和一条独立监听连接。
- 组件不绑定或选择直播源;账户事件流会提供给该账户所有启用的组件实例。同一 kind 可以拥有多个实例,每个实例独立保存名称、设置、OBS
token 和实时通道。
- 平台原始命令先转换成稳定的领域事件,组件不直接依赖 Bilibili `CMD`。
- HTTP 会话、直播源、组件、OBS token 和实时通道均以租户为边界。
- 新组件可以增加设置、投影和持久化副作用,而不修改直播连接核心。
## 运行时数据流
```mermaid
flowchart LR
CC[CookieCloud] --> PF[ProviderFactory]
BL[Bilibili Live] <--> BP[BilibiliProvider]
PF --> BP
BP --> LE[LiveEvent]
LE --> SR[SourceEventRouter]
PG[(PostgreSQL)] --> CR[Component repository/cache]
CR --> SR
SR --> EH[Durable handlers]
SR --> PJ[Passive projection]
PJ --> HUB[Component-scoped EventHub]
HUB --> WS[Authenticated WebSocket]
WS --> OBS[OBS overlay]
```
1. `SourceSupervisor` 为每个 `source_id` 保持至多一个 provider task。
2. `BilibiliProvider` 使用该用户加密保存的 CookieCloud 凭据构造 `libilibili`
客户端;crate 负责 WBI、WebSocket 与压缩包解析,adapter 再把强类型命令转换成 `LiveEvent`。
3. `SourceEventRouter` 按 `owner_id`
取得该账户所有启用组件,再由各组件的订阅声明筛选事件类型;`source_id`
只标识账户级监听,不存储在组件行中。
4. 匹配订阅后,路由器先执行可持久化的 `EventHandler`,再执行无副作用的 `EventProjection`。
5. 投影结果只发布到该 `component_id` 的广播通道。没有全局 WebSocket 事件总线。
6. OBS 使用实例级只读 token 订阅一个组件实例,不能读取控制台 API;同类型的其他实例拥有不同
`component_id`,可在不同 OBS 场景中使用不同样式。
## 状态所有权
| 状态 | 权威来源 | 内存副本 | 说明 |
| ---------------- | ------------ | ------------------------ | ----------------------------- |
| 用户、TOTP、会话 | PostgreSQL | 无 | Secret 加密,token 只保存摘要 |
| CookieCloud 凭据 | PostgreSQL | provider 构建期间解密 | 每账户一份,不返回浏览器 |
| 直播源和房间 | PostgreSQL | `SourceSupervisor` | 每账户一个可切换房间和连接 |
| 组件实例与设置 | PostgreSQL | `InMemoryComponentStore` | 写入成功后刷新热路径缓存 |
| 礼物/表情目录 | Bilibili API | provider catalog | 刷新失败保留最近成功快照 |
| 实时消息 | provider | `EventHub` 有界广播 | 不作为业务持久化机制 |
| 点歌队列和评分 | PostgreSQL | OBS revision snapshot | handler 事务写入、RLS 隔离 |
| 账户语言偏好 | PostgreSQL | React/OBS runtime | TOML 目录验证、RLS 隔离 |
| PWA 静态壳层 | Docker 镜像 | Cache Storage | 不包含 API 或用户数据 |
## 启动顺序
`AppState::build` 按以下顺序启动,避免事件进入未完成的运行时:
1. 创建 PostgreSQL 连接池并执行嵌入式迁移。
2. 构造加密和 passwordless 鉴权服务。
3. 注册内建组件定义。
4. 从 PostgreSQL hydrate 组件热路径缓存。
5. 创建有界源事件队列、路由器和每组件广播中心。
6. 为数据库中所有启用的直播源启动 provider。
7. 最后由 `main.rs` 绑定 HTTP 监听端口。
关机时先停止接收 HTTP,再取消所有 provider task,防止容器退出期间继续拉取上游事件。
## 进程与部署边界
- Node 只存在于 Docker 的前端构建阶段。
- 最终镜像仅运行 Rust 可执行文件,并从 `/app/web` 同域托管静态资源。
- 容器使用 host network,但默认只监听 `127.0.0.1:9719`。
- Nginx 负责公网 TLS、域名和 WebSocket upgrade。
- CookieCloud 与 PostgreSQL 是外部服务,不由本项目 Compose 创建。
- `libilibili` 是源码树中的相邻 crate,由 Compose named build context 注入 Rust 构建阶段。
- `resources/i18n.toml` 同时输入 Vite 和 Rust 构建;缺少默认语言键的 locale 会使后端测试失败。
## 代码导航
- 后端模块职责:[`apps/server-rust/README.md`](../apps/server-rust/README.md)
- 前端路由和状态:[`apps/overlay/README.md`](../apps/overlay/README.md)
- 组件扩展指南:[`components/README.md`](components/README.md)
- WebSocket 协议:[`protocol.md`](protocol.md)
- 安全边界:[`security.md`](security.md)