feat: stabilize fnos lan delivery flow
This commit is contained in:
130
docs/FNOS_LAN_DELIVERY_RUNBOOK_2026-03-27.md
Normal file
130
docs/FNOS_LAN_DELIVERY_RUNBOOK_2026-03-27.md
Normal file
@@ -0,0 +1,130 @@
|
||||
# StoryForge fnOS / NAS LAN Delivery Runbook
|
||||
|
||||
日期:2026-03-27
|
||||
|
||||
## 目标
|
||||
|
||||
这份 runbook 统一说明 StoryForge 在 fnOS / NAS 局域网交付时的默认主链。
|
||||
|
||||
默认原则只有一条:NAS SSH 隧道是主链,Windows `7860` 只做自检。
|
||||
|
||||
## 默认链路
|
||||
|
||||
1. 先把 Windows `cutvideo` 通过 fnOS 的 SSH 隧道暴露到 NAS。
|
||||
2. 再让 StoryForge 的 NAS 侧服务统一指向 NAS 隧道地址。
|
||||
3. 最后用一键 smoke 验证整条链路是否可用。
|
||||
|
||||
推荐默认顺序:
|
||||
|
||||
```bash
|
||||
./scripts/deploy_fnos_cutvideo_tunnel.sh
|
||||
./scripts/deploy_fnos_storyforge_lan_stack.sh
|
||||
./scripts/smoke_fnos_storyforge_lan.sh
|
||||
```
|
||||
|
||||
## 默认端口
|
||||
|
||||
- Windows `cutvideo` 自检口:`http://192.168.31.18:7860`
|
||||
- NAS 主链 `cutvideo` 入口:`http://192.168.31.188:19186`
|
||||
- NAS 兼容/上传入口:`http://192.168.31.188:19181`
|
||||
- StoryForge collector:`http://127.0.0.1:8081`
|
||||
- fnOS 内部 n8n:`http://127.0.0.1:5670`
|
||||
|
||||
## 默认路由
|
||||
|
||||
- StoryForge 的 `CUTVIDEO_BASE_URL` 默认应指向 `http://192.168.31.188:19186`
|
||||
- `19186` 是交付主链,不要再把 `7860` 当成 StoryForge 默认主入口
|
||||
- `7860` 仅用于确认 Windows 上的 `cutvideo` 服务本身是否活着
|
||||
- 如果任务涉及上传或 staging,再顺带确认 `19181` 可达
|
||||
|
||||
## 重启后验证
|
||||
|
||||
### Windows 重启后
|
||||
|
||||
- 先确认 `22 / 3389 / 5985` 仍可达
|
||||
- 再检查 `http://192.168.31.18:7860/api/bootstrap`
|
||||
- 如果 `7860` 超时,但管理通道正常,优先判断为 `cutvideo` 服务未起来
|
||||
- 如果 `7860` 可达,再确认 Windows 任务计划程序 `\Codex\cutvideo-web` 仍在托管服务
|
||||
|
||||
### fnOS 重启后
|
||||
|
||||
- 先跑 `./scripts/deploy_fnos_cutvideo_tunnel.sh`
|
||||
- 再跑 `./scripts/deploy_fnos_storyforge_lan_stack.sh`
|
||||
- 确认 `19186` 和 `19181` 都重新可达
|
||||
- 确认 StoryForge collector 仍然把 `CUTVIDEO_BASE_URL` 指向 `19186`
|
||||
|
||||
### StoryForge 服务重启后
|
||||
|
||||
- 检查 collector 还能正常返回 health
|
||||
- 检查 NAS 侧服务没有回退到 Windows 直连 `7860`
|
||||
- 检查 smoke 是否还能把 real-cut 链路跑通
|
||||
|
||||
## Smoke 命令
|
||||
|
||||
```bash
|
||||
./scripts/smoke_fnos_storyforge_lan.sh
|
||||
```
|
||||
|
||||
这条 smoke 应该至少覆盖:
|
||||
|
||||
- `19186` 可达
|
||||
- `19181` 可达
|
||||
- `cutvideo` 在线
|
||||
- StoryForge NAS 侧链路可用
|
||||
|
||||
## 故障分流
|
||||
|
||||
### 1. `19186` 不通
|
||||
|
||||
先看 fnOS 的 SSH 隧道是否还在:
|
||||
|
||||
- 重新执行 `./scripts/deploy_fnos_cutvideo_tunnel.sh`
|
||||
- 确认 Windows 主机可连
|
||||
- 再确认 Windows `7860` 本身是否正常
|
||||
|
||||
### 2. `7860` 不通,但 `22 / 3389 / 5985` 还通
|
||||
|
||||
这通常是 Windows 上的 `cutvideo` 没启动,不是网络地址失效。
|
||||
|
||||
优先检查:
|
||||
|
||||
- Windows 任务计划程序 `\Codex\cutvideo-web`
|
||||
- `D:\ai-code\cutvideo\.venv`
|
||||
- `http://192.168.31.18:7860/api/bootstrap`
|
||||
|
||||
### 3. `19186` 通,但 StoryForge 链路失败
|
||||
|
||||
说明隧道大概率是好的,问题更可能在 NAS 侧服务配置。
|
||||
|
||||
优先检查:
|
||||
|
||||
- `./scripts/deploy_fnos_storyforge_lan_stack.sh` 是否已重新跑过
|
||||
- `CUTVIDEO_BASE_URL` 是否仍然是 `http://192.168.31.188:19186`
|
||||
- collector 是否回退到了 Windows 直连 `7860`
|
||||
|
||||
### 4. `19186` 和 `7860` 都正常,但 smoke 失败
|
||||
|
||||
优先看失败点属于哪一层:
|
||||
|
||||
- 只是 `collector` health 失败,先看 NAS 侧服务
|
||||
- 只是上传失败,先看 `19181`
|
||||
- 只是 `cutvideo` 任务失败,先看 Windows 服务日志
|
||||
|
||||
### 5. Windows 或 fnOS 重启后出现“短时间都不通”
|
||||
|
||||
先按默认顺序重新跑:
|
||||
|
||||
```bash
|
||||
./scripts/deploy_fnos_cutvideo_tunnel.sh
|
||||
./scripts/deploy_fnos_storyforge_lan_stack.sh
|
||||
./scripts/smoke_fnos_storyforge_lan.sh
|
||||
```
|
||||
|
||||
如果这三步后仍失败,再进入对应故障分流。
|
||||
|
||||
## 维护原则
|
||||
|
||||
- 默认主链永远是 NAS SSH 隧道
|
||||
- Windows `7860` 只做自检,不做 StoryForge 默认入口
|
||||
- 交付时先保证 `19186` 稳,再谈其他端口
|
||||
- 新人接手时,先跑 smoke,再看详细日志
|
||||
@@ -19,7 +19,7 @@ cp .env.example .env
|
||||
- `BOOTSTRAP_SUPERADMIN_USERNAME=storyforge-admin`
|
||||
- `BOOTSTRAP_SUPERADMIN_PASSWORD=your_strong_admin_password`
|
||||
- `STORYFORGE_INTERNAL_BASE_URL=http://collector:8081`,用于 Docker 内的 n8n 回调 `collector`
|
||||
- `CUTVIDEO_BASE_URL=http://<windows-lan-ip>:7860`
|
||||
- `CUTVIDEO_BASE_URL=http://192.168.31.188:19186`,默认主链走 NAS SSH 隧道
|
||||
- `CUTVIDEO_API_KEY=` 如果 Windows 服务启用了鉴权
|
||||
- `HUOBAO_BASE_URL=http://127.0.0.1:5678`
|
||||
- `WHISPER_BIN=` 指向你现有本地 ASR 可执行文件时填写
|
||||
@@ -33,7 +33,8 @@ cp .env.example .env
|
||||
- 如果你单独重建 `collector`,要确保运行时仍带上 `CUTVIDEO_BASE_URL`,否则容器会退回空值
|
||||
- `collector` 容器不要直接复用宿主机的 `N8N_BASE_URL=http://127.0.0.1:5670`,否则容器内会连回自己并导致 webhook 调度失败
|
||||
- 当前更稳定的 NAS 转发地址是 `http://192.168.31.188:19186`
|
||||
- Windows 直连地址 `http://192.168.31.18:7860` 仍可作为主机内自检入口,但不再建议作为 StoryForge 主链默认值
|
||||
- Windows 直连地址 `http://192.168.31.18:7860` 仅用于主机内自检,不再建议作为 StoryForge 主链默认值
|
||||
- 只要是 StoryForge 的 fnOS / NAS 联调与交付,优先把 `CUTVIDEO_BASE_URL` 视为 `19186`,把 `7860` 视为 Windows 本机自检口
|
||||
- 当前已验证可用的本机 HTTP ASR 入口是 `http://host.docker.internal:8088/transcribe`
|
||||
- 如果你用的是本机 `mac-whisper-service`,建议同时以 `WHISPER_TIMEOUT_MS=120000` 启动,否则长视频会直接 504
|
||||
|
||||
@@ -41,13 +42,19 @@ cp .env.example .env
|
||||
|
||||
```bash
|
||||
./scripts/deploy_fnos_cutvideo_tunnel.sh
|
||||
./scripts/deploy_fnos_storyforge_lan_stack.sh
|
||||
./scripts/smoke_fnos_storyforge_lan.sh
|
||||
```
|
||||
|
||||
它会做三件事:
|
||||
如果你只想先把底座打通,也可以先跑前两步,再单独 smoke。
|
||||
|
||||
它们分别负责:
|
||||
|
||||
- 在 fnOS 上生成并持久化 Windows SSH 隧道密钥
|
||||
- 把 fnOS 公钥写入 Windows OpenSSH 管理员授权文件
|
||||
- 在 fnOS 上常驻 `19186 -> Windows 127.0.0.1:7860` 和 `19181 -> Windows 127.0.0.1:8081`,并写入 `@reboot` 自启动
|
||||
- 把 StoryForge 的 NAS 侧服务统一切到 `http://192.168.31.188:19186` 的默认主链
|
||||
- 通过一键 smoke 校验 `cutvideo`、`collector` 和整条 LAN 交付链路
|
||||
|
||||
`cutvideo` 维护补充(2026-03-27):
|
||||
|
||||
@@ -261,7 +268,7 @@ npm run capture -- \
|
||||
- `GET /api/bootstrap` 恢复为 `200`,`GET /api/uploads` 返回 `405 Method Not Allowed`
|
||||
- 上面的 `405` 是正常现象,说明上传接口存在且只接受 `POST`
|
||||
- `StoryForge collector` 的 `/v2/integrations/health` 已重新识别到 `cutvideo.reachable=true`、`supports_uploads=true`
|
||||
- fnOS 局域网调试链现在默认走 `http://192.168.31.188:19186`,不再依赖 Windows 机器直接开放 `7860`
|
||||
- fnOS 局域网调试链现在默认走 `http://192.168.31.188:19186`,Windows 机器直接开放 `7860` 仅保留为自检入口
|
||||
- 如果 UI 里 `自动剪辑` 再次掉线,先按 [`WINDOWS_CUTVIDEO_OPERATIONS_2026-03-27.md`](/Users/kris/code/StoryForge-gitea/docs/WINDOWS_CUTVIDEO_OPERATIONS_2026-03-27.md) 检查 Windows 任务计划程序和 `.venv`
|
||||
|
||||
## 8. `huobao-drama` AI 视频链路验证
|
||||
|
||||
@@ -0,0 +1,249 @@
|
||||
# fnOS LAN Delivery Stabilization Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** 把 StoryForge 的 fnOS / NAS 局域网交付链做成仓库内一键可复现、可 smoke、可恢复的稳定版本。
|
||||
|
||||
**Architecture:** 继续采用“Windows 运行 cutvideo + fnOS 通过 SSH 隧道暴露 19186/19181 + fnOS collector 默认走 19186 + fnOS Web 默认走 fnOS collector”的交付路径。在仓库内新增统一部署入口、统一 LAN smoke、补齐 healthz 路由可见性与前端提示,并把运维说明统一到同一条主链。
|
||||
|
||||
**Tech Stack:** Bash, Python 3, FastAPI, vanilla JS, Docker Compose, fnOS SSH helpers
|
||||
|
||||
---
|
||||
|
||||
### Task 1: 落地统一部署入口
|
||||
|
||||
**Files:**
|
||||
- Create: `scripts/deploy_fnos_storyforge_lan_stack.sh`
|
||||
- Modify: `scripts/deploy_fnos_storyforge_web.sh`
|
||||
- Modify: `scripts/deploy_fnos_storyforge_collector.sh`
|
||||
- Test: `tests/test_production_baseline.py`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,约束统一部署入口存在并串联 tunnel / collector / web**
|
||||
|
||||
```python
|
||||
def test_repo_contains_fnos_lan_stack_deploy_entrypoint(self) -> None:
|
||||
script_path = ROOT / "scripts" / "deploy_fnos_storyforge_lan_stack.sh"
|
||||
self.assertTrue(script_path.exists())
|
||||
content = script_path.read_text(encoding="utf-8")
|
||||
self.assertIn("deploy_fnos_cutvideo_tunnel.sh", content)
|
||||
self.assertIn("deploy_fnos_storyforge_collector.sh", content)
|
||||
self.assertIn("deploy_fnos_storyforge_web.sh", content)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run: `python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_repo_contains_fnos_lan_stack_deploy_entrypoint -v`
|
||||
Expected: FAIL with missing `deploy_fnos_storyforge_lan_stack.sh`
|
||||
|
||||
- [ ] **Step 3: 写最小实现**
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(CDPATH= cd -- "$(dirname "$0")/.." && pwd)"
|
||||
|
||||
bash "$ROOT/scripts/deploy_fnos_cutvideo_tunnel.sh"
|
||||
bash "$ROOT/scripts/deploy_fnos_storyforge_collector.sh"
|
||||
bash "$ROOT/scripts/deploy_fnos_storyforge_web.sh"
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 跑测试确认通过**
|
||||
|
||||
Run: `python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_repo_contains_fnos_lan_stack_deploy_entrypoint -v`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: 完成后补脚本语法校验**
|
||||
|
||||
Run: `bash -n scripts/deploy_fnos_storyforge_lan_stack.sh`
|
||||
Expected: exit 0
|
||||
|
||||
### Task 2: 落地统一 LAN smoke
|
||||
|
||||
**Files:**
|
||||
- Create: `scripts/smoke_fnos_storyforge_lan.sh`
|
||||
- Modify: `tests/test_production_baseline.py`
|
||||
- Modify: `README.md`
|
||||
- Modify: `docs/LAN_E2E_GUIDE_2026-03-18.md`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,约束 LAN smoke 覆盖 web / healthz / auto-session / integrations / tunnel**
|
||||
|
||||
```python
|
||||
def test_repo_contains_fnos_lan_smoke_script(self) -> None:
|
||||
script_path = ROOT / "scripts" / "smoke_fnos_storyforge_lan.sh"
|
||||
self.assertTrue(script_path.exists())
|
||||
content = script_path.read_text(encoding="utf-8")
|
||||
for expected in [
|
||||
"/healthz",
|
||||
"/v2/auth/auto-session",
|
||||
"/v2/integrations/health",
|
||||
"/api/bootstrap",
|
||||
"19181",
|
||||
]:
|
||||
self.assertIn(expected, content)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run: `python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_repo_contains_fnos_lan_smoke_script -v`
|
||||
Expected: FAIL with missing `smoke_fnos_storyforge_lan.sh`
|
||||
|
||||
- [ ] **Step 3: 写最小实现**
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
FNOS_HOST="${FNOS_HOST:-192.168.31.188}"
|
||||
WEB_PORT="${STORYFORGE_WEB_V4_DEV_PORT:-19192}"
|
||||
COLLECTOR_PORT="${STORYFORGE_COLLECTOR_DEV_PORT:-19193}"
|
||||
CUTVIDEO_PORT="${CUTVIDEO_FORWARD_PORT:-19186}"
|
||||
COMPAT_PORT="${STORYFORGE_COMPAT_FORWARD_PORT:-19181}"
|
||||
```
|
||||
|
||||
继续补:
|
||||
- 访问 `http://$FNOS_HOST:$WEB_PORT/`
|
||||
- 校验 `storyforge-runtime-config.js`
|
||||
- 访问 `http://$FNOS_HOST:$COLLECTOR_PORT/healthz`
|
||||
- `POST /v2/auth/auto-session` 获取 token
|
||||
- 带 token 调 `GET /v2/integrations/health`
|
||||
- 校验 `cutvideo.reachable == true`
|
||||
- 访问 `http://$FNOS_HOST:$CUTVIDEO_PORT/api/bootstrap`
|
||||
- 访问 `http://$FNOS_HOST:$COMPAT_PORT/`
|
||||
|
||||
- [ ] **Step 4: 跑测试确认通过**
|
||||
|
||||
Run: `python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_repo_contains_fnos_lan_smoke_script -v`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: 完成后做脚本语法校验**
|
||||
|
||||
Run: `bash -n scripts/smoke_fnos_storyforge_lan.sh`
|
||||
Expected: exit 0
|
||||
|
||||
### Task 3: 收口 healthz 与前端依赖文案
|
||||
|
||||
**Files:**
|
||||
- Modify: `collector-service/app/core_main.py`
|
||||
- Modify: `web/storyforge-web-v4/assets/app.js`
|
||||
- Modify: `tests/test_production_baseline.py`
|
||||
|
||||
- [ ] **Step 1: 写失败测试,约束 healthz 暴露局域网路由信息**
|
||||
|
||||
```python
|
||||
def test_healthz_exposes_lan_routing_summary(self) -> None:
|
||||
response = self.client.get("/healthz")
|
||||
self.assertEqual(response.status_code, 200, response.text)
|
||||
payload = response.json()
|
||||
self.assertIn("lanRouting", payload)
|
||||
self.assertIn("cutvideoRouteMode", payload["lanRouting"])
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 跑测试确认当前失败**
|
||||
|
||||
Run: `python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_healthz_exposes_lan_routing_summary -v`
|
||||
Expected: FAIL because `lanRouting` is absent
|
||||
|
||||
- [ ] **Step 3: 写最小实现**
|
||||
|
||||
```python
|
||||
"lanRouting": {
|
||||
"collectorBaseUrl": DEFAULT_EXTERNAL_BASE_URL,
|
||||
"cutvideoBaseUrl": CUTVIDEO_BASE_URL,
|
||||
"liveRecorderBaseUrl": LIVE_RECORDER_BASE_URL,
|
||||
"cutvideoRouteMode": "fnos_tunnel" if ":19186" in CUTVIDEO_BASE_URL else "direct",
|
||||
}
|
||||
```
|
||||
|
||||
前端同步收口:
|
||||
|
||||
```javascript
|
||||
if (key === "cutvideo" && detail.baseUrl.includes(":19186")) {
|
||||
extra = "当前通过 fnOS NAS SSH 隧道访问 Windows cutvideo。";
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: 跑测试确认通过**
|
||||
|
||||
Run: `python3 -m unittest tests.test_production_baseline.ProductionBaselineTests.test_healthz_exposes_lan_routing_summary -v`
|
||||
Expected: PASS
|
||||
|
||||
- [ ] **Step 5: 做前端语法校验**
|
||||
|
||||
Run: `node --check web/storyforge-web-v4/assets/app.js`
|
||||
Expected: exit 0
|
||||
|
||||
### Task 4: 统一 README / LAN 运维手册并补最终回归
|
||||
|
||||
**Files:**
|
||||
- Modify: `README.md`
|
||||
- Modify: `docs/LAN_E2E_GUIDE_2026-03-18.md`
|
||||
- Create: `docs/FNOS_LAN_DELIVERY_RUNBOOK_2026-03-27.md`
|
||||
|
||||
- [ ] **Step 1: 更新主入口文档**
|
||||
|
||||
把 README 收口成三条默认命令:
|
||||
|
||||
```bash
|
||||
./scripts/deploy_fnos_cutvideo_tunnel.sh
|
||||
./scripts/deploy_fnos_storyforge_lan_stack.sh
|
||||
./scripts/smoke_fnos_storyforge_lan.sh
|
||||
```
|
||||
|
||||
- [ ] **Step 2: 更新 LAN E2E**
|
||||
|
||||
把 `CUTVIDEO_BASE_URL=http://<windows-lan-ip>:7860` 改成“主链默认使用 `http://192.168.31.188:19186`,Windows 直连仅作自检”。
|
||||
|
||||
- [ ] **Step 3: 写新的运维 runbook**
|
||||
|
||||
包含:
|
||||
- 默认端口
|
||||
- 默认路由
|
||||
- 故障分流
|
||||
- fnOS 重启后如何验证 tunnel / web / collector
|
||||
- smoke 命令与预期结果
|
||||
|
||||
- [ ] **Step 4: 跑最终验证**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
bash -n scripts/deploy_fnos_storyforge_lan_stack.sh
|
||||
bash -n scripts/smoke_fnos_storyforge_lan.sh
|
||||
python3 -m unittest tests.test_production_baseline -v
|
||||
node --check web/storyforge-web-v4/assets/app.js
|
||||
git diff --check
|
||||
```
|
||||
|
||||
Expected:
|
||||
- scripts syntax all pass
|
||||
- unittest pass
|
||||
- JS syntax pass
|
||||
- `git diff --check` clean
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add README.md docs/LAN_E2E_GUIDE_2026-03-18.md docs/FNOS_LAN_DELIVERY_RUNBOOK_2026-03-27.md docs/superpowers/plans/2026-03-27-fnos-lan-delivery-stabilization.md scripts/deploy_fnos_storyforge_lan_stack.sh scripts/smoke_fnos_storyforge_lan.sh tests/test_production_baseline.py collector-service/app/core_main.py web/storyforge-web-v4/assets/app.js
|
||||
git commit -m "feat: stabilize fnos lan delivery flow"
|
||||
```
|
||||
|
||||
## Self-Review
|
||||
|
||||
### Spec coverage
|
||||
|
||||
- 统一部署入口:Task 1 覆盖
|
||||
- LAN smoke:Task 2 覆盖
|
||||
- 前后端状态收口:Task 3 覆盖
|
||||
- 文档与运维统一:Task 4 覆盖
|
||||
|
||||
### Placeholder scan
|
||||
|
||||
- 没有保留 TBD / TODO / “后续补”
|
||||
- 每个任务都给了明确文件和验证命令
|
||||
|
||||
### Type consistency
|
||||
|
||||
- 统一使用 `deploy_fnos_storyforge_lan_stack.sh`
|
||||
- 统一使用 `smoke_fnos_storyforge_lan.sh`
|
||||
- `healthz` 新字段统一命名为 `lanRouting`
|
||||
Reference in New Issue
Block a user