Files
lxc-streamutils/docs/architecture.md
T
felis f79852d8e6 add account localization and switchable rooms
Centralize control and OBS copy in a shared TOML catalog, persist the selected locale per account, and broadcast language changes to component streams. Allow account owners to atomically switch their Bilibili room and restart the shared listener without changing component URLs.
2026-07-18 23:28:05 -07:00

87 lines
4.6 KiB
Markdown

# 系统架构
本文描述直播组件服务的运行边界、数据流和扩展点。实现代码分别位于 `apps/server-rust` 与
`apps/overlay`。
## 核心目标
- 每个账户拥有一个可切换的 Bilibili 直播间、一个 CookieCloud 来源和一条独立监听连接。
- 组件不绑定或选择直播源;账户事件流会提供给该账户所有启用的组件实例。
- 平台原始命令先转换成稳定的领域事件,组件不直接依赖 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。
## 状态所有权
| 状态 | 权威来源 | 内存副本 | 说明 |
| ---------------- | ------------ | ------------------------ | ----------------------------- |
| 用户、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)