feat: expose storyforge through public cloud gateway

This commit is contained in:
kris
2026-03-23 17:52:09 +08:00
parent aeccea585e
commit 841985c0d2
7 changed files with 238 additions and 1 deletions

View File

@@ -132,6 +132,7 @@ N8N_BASE_URL=http://127.0.0.1:5670
- `collector-service``http://127.0.0.1:8081` - `collector-service``http://127.0.0.1:8081`
- `n8n``http://127.0.0.1:5670` - `n8n``http://127.0.0.1:5670`
- `cli-proxy-api``http://127.0.0.1:8317` - `cli-proxy-api``http://127.0.0.1:8317`
- 公网入口:`https://test.hyzq.net/storyforge/`
默认会创建最高权限账号: 默认会创建最高权限账号:
@@ -151,6 +152,11 @@ N8N_BASE_URL=http://127.0.0.1:5670
- 触发 `real_cut_pipeline` - 触发 `real_cut_pipeline`
- 触发 `ai_video_pipeline` - 触发 `ai_video_pipeline`
- 历史旧运行链已完成移除,当前运行时只保留 StoryForge 自身服务与外部执行引擎 - 历史旧运行链已完成移除,当前运行时只保留 StoryForge 自身服务与外部执行引擎
- 当前公网接入采用“云服务器 HTTPS 入口 + 本机桥接隧道 + 本机现网执行链”模式:
- `https://test.hyzq.net/storyforge/` 由云服务器 `nginx` 提供 HTTPS 入口
- `/storyforge/` 静态页反向代理到本机 `Web V4` 静态服务
- `/storyforge/v2/*``/storyforge/openapi.json``/storyforge/healthz` 反向代理到本机 `collector-service`
- `cutvideo / huobao / 本机模型 / NAS 录制` 继续由本机和局域网执行链提供
## 说明 ## 说明

View File

@@ -0,0 +1,37 @@
# StoryForge `test.hyzq.net/storyforge` 公网入口
当前公网接入不是把执行链整体迁到云服务器,而是:
1. 云服务器 `nginx` 提供 `https://test.hyzq.net/storyforge/`
2. 本机 `collector-service` 继续承接业务与局域网执行引擎
3. 本机 `Web V4` 静态服务继续承接前端
4. 本机通过 SSH 反向隧道把这两个入口桥接到云服务器
## 端口映射
- 云服务器 `127.0.0.1:18181` -> 本机 `127.0.0.1:8081`
- 云服务器 `127.0.0.1:18191` -> 本机 `127.0.0.1:3918`
## 本机常驻服务
- `com.storyforge.collector`
- `com.storyforge.web-v4`
- `com.storyforge.cloud-bridge`
## 云服务器 `nginx` 路由
- `/storyforge/` -> `127.0.0.1:18191`
- `/storyforge/v2/*` -> `127.0.0.1:18181`
- `/storyforge/openapi.json` -> `127.0.0.1:18181/openapi.json`
- `/storyforge/healthz` -> `127.0.0.1:18181/healthz`
## 当前优点
- 不需要把 `cutvideo / huobao / NAS live-recorder / 本机模型` 全部搬上云
- 公网入口统一
- 本机现网能力不需要改造即可对外开放
## 当前限制
- 本机桥接断开时,公网入口不可用
- 这是公网 staging / 私有运营入口,更接近“公网可访问的现网桥接”,不是最终完全云原生部署

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.storyforge.cloud-bridge</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/ssh</string>
<string>-N</string>
<string>-i</string>
<string>/Users/kris/.ssh/id_ed25519_kylin188</string>
<string>-o</string>
<string>BatchMode=yes</string>
<string>-o</string>
<string>ExitOnForwardFailure=yes</string>
<string>-o</string>
<string>ServerAliveInterval=30</string>
<string>-o</string>
<string>ServerAliveCountMax=3</string>
<string>-o</string>
<string>StrictHostKeyChecking=no</string>
<string>-o</string>
<string>UserKnownHostsFile=/Users/kris/.ssh/known_hosts</string>
<string>-R</string>
<string>127.0.0.1:18181:127.0.0.1:8081</string>
<string>-R</string>
<string>127.0.0.1:18191:127.0.0.1:3918</string>
<string>ubuntu@111.231.132.51</string>
</array>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/kris/code/StoryForge/data/collector/cloud-bridge.log</string>
<key>StandardErrorPath</key>
<string>/Users/kris/code/StoryForge/data/collector/cloud-bridge.log</string>
</dict>
</plist>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>com.storyforge.web-v4</string>
<key>ProgramArguments</key>
<array>
<string>/usr/bin/python3</string>
<string>-m</string>
<string>http.server</string>
<string>3918</string>
<string>--bind</string>
<string>127.0.0.1</string>
<string>--directory</string>
<string>/Users/kris/code/StoryForge/web/storyforge-web-v4</string>
</array>
<key>WorkingDirectory</key>
<string>/Users/kris/code/StoryForge/web/storyforge-web-v4</string>
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<key>StandardOutPath</key>
<string>/Users/kris/code/StoryForge/data/collector/web-v4.log</string>
<key>StandardErrorPath</key>
<string>/Users/kris/code/StoryForge/data/collector/web-v4.log</string>
</dict>
</plist>

View File

@@ -0,0 +1,101 @@
location = /storyforge {
auth_basic off;
return 301 /storyforge/;
}
location = /storyforge/healthz {
auth_basic off;
proxy_pass http://127.0.0.1:18181/healthz;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location = /storyforge/openapi.json {
auth_basic off;
proxy_pass http://127.0.0.1:18181/openapi.json;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location = /storyforge/api/v1/app/update/latest {
auth_basic off;
proxy_pass http://127.0.0.1:18181/api/v1/app/update/latest;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location ^~ /storyforge/downloads/ {
auth_basic off;
proxy_pass http://127.0.0.1:18181/downloads/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
location ^~ /storyforge/v2/ {
auth_basic off;
proxy_pass http://127.0.0.1:18181/v2/;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_buffering off;
}
location ^~ /storyforge/docs {
auth_basic off;
rewrite ^/storyforge(/docs.*)$ $1 break;
proxy_pass http://127.0.0.1:18181;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_buffering off;
}
location ^~ /storyforge/redoc {
auth_basic off;
rewrite ^/storyforge(/redoc.*)$ $1 break;
proxy_pass http://127.0.0.1:18181;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_buffering off;
}
location ^~ /storyforge/ {
auth_basic off;
rewrite ^/storyforge/?(.*)$ /$1 break;
proxy_pass http://127.0.0.1:18191;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
proxy_buffering off;
}

View File

@@ -14,6 +14,7 @@
- 目录已经从 `output/ui/` 原型区独立出来,并接上了第一层真实业务接口 - 目录已经从 `output/ui/` 原型区独立出来,并接上了第一层真实业务接口
- 这里面向国内平台的 Web 承载,当前覆盖 `douyin``xiaohongshu``bilibili``kuaishou``wechat_video` - 这里面向国内平台的 Web 承载,当前覆盖 `douyin``xiaohongshu``bilibili``kuaishou``wechat_video`
- `YouTube` 目前明确不在本轮范围内 - `YouTube` 目前明确不在本轮范围内
- 已支持通过 `https://test.hyzq.net/storyforge/` 做公网访问
- 通用的项目、内容源、复盘、集成等流程可以正常使用 - 通用的项目、内容源、复盘、集成等流程可以正常使用
- 平台工作台和运行时数据目前只有 `douyin` 做到了完整实现,其余平台统一按 `待接入工作台` 处理 - 平台工作台和运行时数据目前只有 `douyin` 做到了完整实现,其余平台统一按 `待接入工作台` 处理
- 当前保留的核心页面结构: - 当前保留的核心页面结构:
@@ -99,6 +100,14 @@ python3 -m http.server 3918
- `http://127.0.0.1:8081` - `http://127.0.0.1:8081`
如果页面部署在:
- `https://test.hyzq.net/storyforge/`
前端会自动把默认后端切到同源的:
- `https://test.hyzq.net/storyforge`
## 后续建议 ## 后续建议
- 继续补多平台各自更深的专属采集与解析能力,而不只是一套统一抽象层 - 继续补多平台各自更深的专属采集与解析能力,而不只是一套统一抽象层

View File

@@ -1,5 +1,20 @@
const STORAGE_KEY = "storyforge-web-v4-session"; const STORAGE_KEY = "storyforge-web-v4-session";
const DEFAULT_BACKEND_URL = "http://127.0.0.1:8081";
function detectDefaultBackendUrl() {
if (typeof window === "undefined") {
return "http://127.0.0.1:8081";
}
const { origin, hostname, port, pathname } = window.location;
if (/^https?:/i.test(origin) && pathname.startsWith("/storyforge")) {
return `${origin}/storyforge`;
}
if ((hostname === "127.0.0.1" || hostname === "localhost") && port && port !== "8081") {
return "http://127.0.0.1:8081";
}
return "http://127.0.0.1:8081";
}
const DEFAULT_BACKEND_URL = detectDefaultBackendUrl();
const navButtons = document.querySelectorAll("[data-screen-target]"); const navButtons = document.querySelectorAll("[data-screen-target]");
const screens = Array.from(document.querySelectorAll("[data-screen]")); const screens = Array.from(document.querySelectorAll("[data-screen]"));