Initial LisgloSIPS V2 implementation

This commit is contained in:
hectorzhao
2026-06-22 10:56:38 +08:00
commit 5fa1bd35e9
303 changed files with 35644 additions and 0 deletions
+138
View File
@@ -0,0 +1,138 @@
# Recording Transfer and Playback Runbook
> 任务:S24 - 录音搬运与播放
> 完成时间:2026-06-21 20:20 +08:00
## 1. 目标
S24 完成录音闭环的最小能力:
- Server B 通过 Tailscale 私网从 Server A 拉取 `.ready` 录音文件。
- 拉取后校验文件大小和 SHA-256。
- 本地 `.part-*` 临时文件校验成功后原子改名。
- 校验和数据库标记成功后才允许删除 Server A 源 `.ready` 文件。
- API 提供受 RBAC 保护的播放入口,通过 Nginx `X-Accel-Redirect` 进入内部 `/_recordings/` location,支持 Range。
## 2. 代码产物
| 文件 | 说明 |
| --- | --- |
| `apps/worker-recording/src/transfer.ts` | 录音扫描、SSH 拉取、大小校验、SHA-256、原子落盘、源文件安全删除、`recordings` 入库 |
| `apps/worker-recording/src/main.ts` | Recording Worker 主循环 |
| `apps/worker-recording/src/transfer.spec.ts` | 搬运、哈希、删除保护和路径安全测试 |
| `apps/api/src/modules/recordings/*` | `GET /api/v2/recordings/:id/play` 播放入口 |
| `apps/api/src/modules/recordings/recordings.service.spec.ts` | Nginx 内部路径和路径穿越防护测试 |
| `infra/server-a/s19/scripts/lisglosips-recording-finalize` | A 端 finalizer 仓库副本,ready 目录改为 `0770`,便于专用拉取用户校验后删除源文件 |
## 3. Worker 环境变量
```text
DATABASE_URL
RECORDING_REMOTE_HOST=lisglosips-a
RECORDING_SSH_CONFIG=/etc/lisglosips/recording/ssh_config
RECORDING_REMOTE_READY_DIR=/dev/shm/voip_rec/ready
RECORDING_LOCAL_ROOT=/data/recordings
RECORDING_SCAN_INTERVAL_MS=10000
RECORDING_MAX_FILES_PER_SCAN=50
RECORDING_DELETE_SOURCE_AFTER_COPY=true
```
本地开发可使用 `.codex-private/ssh/config`;生产或 B 端 systemd 应使用 `/etc/lisglosips/recording/` 下的受控 key 和 known_hosts。
## 4. 服务器变更
### Server B
- 新增 `/etc/lisglosips/recording/a_pull_ed25519`,权限 `0640 root:lisglosips`
- 新增 `/etc/lisglosips/recording/known_hosts`
- 录音持久化目录仍为 `/data/recordings`,由 `lisglo-recorder:lisglosips` 管理。
- 备份目录:`/var/backups/lisglosips-s24/20260621T121100Z`
### Server A
- 新增专用用户 `lisglo-rec-pull`,加入 `rtpengine` 组。
- 安装 B 端录音拉取公钥到 `/var/lib/lisglo-rec-pull/.ssh/authorized_keys`
-`/dev/shm/voip_rec/ready` 调整为 `0770 rtpengine:rtpengine`
- 更新 `/usr/local/sbin/lisglosips-recording-finalize`,让 ready 根目录和 ready 子目录保持 `0770`
- 备份目录:`/var/backups/lisglosips-s24/20260621T121130Z`
未调整 SSH 管理端口,未禁止 SSH key 登录,未重启 OpenSIPS、RTPEngine、Nginx 或 API。
## 5. 播放路径
API
```text
GET /api/v2/recordings/:id/play
Permission: recordings.play
```
API 只查询状态为 `READY` 的录音,返回:
```text
X-Accel-Redirect: /_recordings/<storage-key>
Content-Type: audio/wav 或 audio/mpeg
Cache-Control: private, no-store
```
Nginx 已在 S05 配置:
```text
location /_recordings/ {
internal;
alias /data/recordings/;
add_header Accept-Ranges bytes always;
}
```
外部直接访问 `/_recordings/...` 返回 `404`,只能经 API 鉴权后内部转发。
## 6. 验证结果
本地:
- `corepack pnpm@10.33.0 exec vitest run apps/worker-recording/src/transfer.spec.ts apps/api/src/modules/recordings/recordings.service.spec.ts` 通过,5 条测试。
- `corepack pnpm@10.33.0 --filter @lisglosips/worker-recording build` 通过。
- `corepack pnpm@10.33.0 typecheck` 通过。
- `corepack pnpm@10.33.0 lint` 通过。
- `corepack pnpm@10.33.0 build` 通过。
服务器冒烟:
- A 生成测试文件 `/dev/shm/voip_rec/incoming/s24-smoke/s24-smoke.wav`
- A finalizer 生成 `/dev/shm/voip_rec/ready/s24-smoke/s24-smoke.wav.ready`
- B 使用 `lisglo-recorder` 和专用 SSH key 从 A 私网拉取。
- B 落盘 `/data/recordings/s24-smoke/s24-smoke.wav`,大小 `31`,属主 `lisglo-recorder:lisglosips`
- A/B SHA-256 一致:`c72dd3909a317ad55817435b7ed5a0db6947455ffc66e09a2dde787fe4a8c348`
- 校验成功后 A 源 `.ready` 文件已删除。
- 外部直接访问 `https://127.0.0.1/_recordings/s24-smoke/s24-smoke.wav` 返回 `404`,内部录音 location 未暴露。
## 7. 当前限制
B 当前 `/opt/lisglosips/current` 仍是 S05 placeholder release,未切换到完整 monorepo API/Worker release。因此 S24 本次完成代码、构建、服务器私网拉取链路和 Nginx 内部播放保护验证;正式 `lisglosips@recording-worker.service` 随完整应用 release 发布时启用。
## 8. 回滚
Server A
```bash
sudo cp -a /var/backups/lisglosips-s24/20260621T121130Z/lisglosips-recording-finalize /usr/local/sbin/lisglosips-recording-finalize
sudo chmod 0755 /usr/local/sbin/lisglosips-recording-finalize
sudo userdel -r lisglo-rec-pull
sudo chmod 0750 /dev/shm/voip_rec/ready
```
Server B
```bash
sudo rm -rf /etc/lisglosips/recording
sudo rm -rf /data/recordings/s24-smoke
```
代码回滚:
- 恢复 `apps/worker-recording/src/main.ts` 到骨架版本。
- 删除或恢复 `apps/worker-recording/src/transfer.ts``transfer.spec.ts`
- 删除 `apps/api/src/modules/recordings/` 并从 `AppModule` 移除 `RecordingsModule`
- 恢复 `apps/worker-recording/package.json``tsconfig.json``pnpm-lock.yaml`
- 重新执行 `corepack pnpm@10.33.0 build`