# 03 · 历史工程迁移方案（按 FUJITSM 标准）

> 目标：把对话包里的 v3 单体工程，迁移为符合 FUJITSM 自托管规范、可生产运行的服务。
> FUJITSM = 你的家庭/自托管主机（hostname FUJITSM，当前 Q558：Linux Mint/Ubuntu 24.04，IP 192.168.88.8，虚拟网 10.10.10.8，经 Cloudflare Tunnel + netf.top 暴露）。

---

## 1. 工程现状盘点

**技术栈**：Flask 3.0.3 + Flask-SQLAlchemy + Flask-CORS + gunicorn 22 + SQLite + 保利威 v4 API（Mock/Real 切换）。

**文件结构（解包后 `trae_polyv_export/`）**
```
live-console-backend/
  app.py                  # 主应用，39 个 API（重要：含硬编码凭证与若干缺陷）
  requirements.txt        # flask/flask-sqlalchemy/gunicorn/werkzeug
  Dockerfile             # python:3.12-slim，gunicorn 2 worker
  gunicorn_config.py      # 2 worker，post_fork 初始化 DB
  deploy.sh              # 通用 Linux：nginx + systemd + www-data
  deploy-config.json      # port 5199 / nginx 8890 / py3.12
  instance/liveconsole.db # SQLite（种子数据）
  templates/              # 8 个页面（admin + 前端）
  static/uploads/         # 上传图片
oect-deploy-*             # 静态前端部署到 OECT 的能力与 token
prd_md.md / 需求点清单.md / polyv-integration-guide.md / project-summary.html  # 文档
```

**已知缺陷（与 RAG 对查联动，详见 `04_RAG对查报告.md`）**
| 缺陷 | 严重度 | 说明 |
|---|---|---|
| 自定义鉴权不识别真实观众 | P0 | `User.query.filter_by(status="active").first()` 取首个用户放行，等于无闸门 |
| 创建频道未设 custom 观看条件 | P0 | 请求体未带 `masterAuthSetting.authType=custom`，保利威不会回调，闸门根本没接上 |
| 创建后未调 basic/get | P1 | create/create-init 响应都**不含** pushUrl/watchUrl，需 `basic/get` 回填，否则真实部署推流/观看地址为空 |
| 密钥明文硬编码双套 | P1 | `app.py` 与 `需求点清单.md` 各一套不同 PolyV 凭证，须确认并外部化 |
| view_limit_type 枚举不统一 | P2 | app.py 用 none/code/phone/external/direct，需求清单用 none/whitelist/password/level |
| CORS 白名单错位 | P1 | 允许 live.awulv.cn/qh.netf.top，但前端实际部署 claw.awulv.cn |
| 回放授权缺失 | P0 | 仅直播自定义授权，回放（VOD）未接 PlaySafe/vid 授权 |

## 2. FUJITSM 标准（本机迁移遵循）

- **密钥**：一律环境变量 / `.env`（权限 600），**禁止**进源码、进 git。
- **服务托管**：systemd user service（`~/.config/systemd/user/`）或 docker-compose，崩溃 `Restart=always`。
- **目录**：项目放 `/opt/live-console`（或 `/srv/`），SQLite 落到持久盘 `/mnt/data/live-console/`。
- **反代/暴露**：沿用 Cloudflare Tunnel（netf.top 手册），`*.netf.top` 子域；不直曝公网 IP。
- **日志**：journald 或 `/var/log/live-console/`，不写临时文件。
- **依赖隔离**：venv 或容器，不污染系统 Python。

## 3. 目标架构

```
观众浏览器 ──┐
节目单/大厅/回放(静态,OECT:claw.awulv.cn) ──┤
                                          ├─→ Cloudflare Tunnel ─→ nginx(:8890) ─→ gunicorn(:5199) ─→ Flask ─→ SQLite(/mnt/data/live-console)
保利威观看页 ─→ 自定义授权回调(/api/auth/custom-auth) ─┘
                                          └─→ 保利威 API (api.polyv.net)  [服务端中转, 密钥在 env]
```

## 4. 关键修复清单（迁移时一并做）

1. **密钥外部化**：`POLYV_APP_ID / POLYV_APP_SECRET / POLYV_USER_ID / POLYV_CUSTOM_AUTH_SECRET` 进 `.env`；删除 `app.py` 内 `POLYV_CONFIG` 明文；确认并用**唯一一套**有效凭证。
2. **API 路径对齐**：保留 `/channel/create`（flat body 匹配），或升级到 `/channel/create-init`（需改请求体为 `basicSetting/masterAuthSetting/playbackSetting/roles`）。二选一，别混。
3. **basic/get 补漏**：`polyv_create_channel` 成功后立即 `GET /live/v4/channel/basic/get` 回填 `push_url/watch_url`（pull_url 按规则拼 m3u8）。
4. **鉴权重做（P0）**：
   - 创建频道时设 `masterAuthSetting.authType=custom, customKey=..., customUri=<你的回调地址>/api/auth/custom-auth`；
   - 回调里从 PolyV 传入的 `url` 参数**解析 viewer userid**（观看页跳转 PolyV 时带 `?userid=xxx`），或改为前端先 `/api/auth/check` 拿 token 再带参跳转；**移除"取首个用户"逻辑**；
   - 正确处理 `vid`（回放场景），回传 `vid` 字段。
5. **回放授权（P0）**：复用同一自定义授权端点，识别 `vid` 走 VOD PlaySafe 校验。
6. **枚举统一**：`view_limit_type` 统一为 `none/whitelist/password/level`（与需求清单一致）。
7. **CORS**：origin 改为实际前端域（如 `https://claw.awulv.cn`、`https://live.netf.top`）。

## 5. 落地步骤

```bash
# 1) 落盘 + venv
sudo mkdir -p /opt/live-console /mnt/data/live-console
cd /opt/live-console
python3 -m venv venv && . venv/bin/activate
pip install flask flask-sqlalchemy flask-cors gunicorn

# 2) 放代码（从迁移后的仓库/工作区拷 app.py/templates/requirements）
#    写入 .env（600）：POLYV_APP_ID=... POLYV_APP_SECRET=... POLYV_USER_ID=... POLYV_CUSTOM_AUTH_SECRET=...
#    并把 sqlite 路径改为 /mnt/data/live-console/liveconsole.db

# 3) systemd user service: ~/.config/systemd/user/live-console.service
#    [Service] ExecStart=/opt/live-console/venv/bin/gunicorn -w 2 -b 127.0.0.1:5199 app:app
#    Restart=always
#    [Install] WantedBy=default.target
systemctl --user daemon-reload && systemctl --user enable --now live-console

# 4) nginx 反代 :8890 → :5199，经 Cloudflare Tunnel 暴露 live.netf.top
# 5) 初始化 DB：python -c "from app import app,db; app.app_context().push(); db.create_all()"
# 6) 触发 /api/seed 生成演示数据；联调创建频道→看 basic/get 回填→自定义授权回调
```

> 注：当前 `deploy.sh` 面向 `www-data + nginx + your-domain.com`，**不能直接用**在 FUJITSM；按上面 user-service + Tunnel 方式改。

## 6. 风险与回滚

| 风险 | 缓解 |
|---|---|
| 密钥用错套导致频道创建失败 | 先在 FUJITSM 用 `.env` 真实凭证 dry-run 创建 1 个测试频道验证 |
| 自定义授权回调地址须为公网可达 | 经 Cloudflare Tunnel 暴露，配置前确认 `https://<子域>.netf.top/api/auth/custom-auth` 可达 |
| SQLite 并发写 | 当前 gunicorn 2 worker + fcntl 锁基本够用；若多写压力上，迁 PostgreSQL |
| 迁移期中断现有 demo | 旧 demo 在另一端口/目录，互不影响；新服务起稳后再切 DNS |

## 7. 交付物与后续

- 本方案与 `04_RAG对查报告.md` 成对：RAG 指出"错在哪"，本方案给出"怎么迁对"。
- 建议下一步：先在本机 FUJITSM 用真实凭证跑通"创建频道→basic/get→自定义授权→观看"，再补回放授权，最后前端静态化部署到 OECT/claw.awulv.cn。
