# 系统架构 本文描述直播组件服务的运行边界、数据流和扩展点。实现代码分别位于 `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)