frontend-independent-delivery-plan.md
8.33 KB
前端独立交付方案
1. 文档目的
本文用于回答两个问题:
- 当前 BettaFish 是否已经具备“前端独立于 Flask 交付”的基础条件。
- 如果要把后端最终收口为 API-only,推荐采用什么交付拓扑,以及应该按什么顺序推进。
本文面向项目维护者和后续继续做结构优化的开发者,重点是给出可执行的迁移方案,而不是只停留在方向判断。
2. 当前现状
2.1 已经完成的能力
- 本地开发主链路已经切换为
python -m scripts.dev.start_local默认拉起Vite HMR + Flask API。 - 前端源码已经完全迁移到
apps/web_ui/。 - 后端根入口已经收口到
python -m apps.web_api。 - 前端当前使用
createWebHashHistory,因此独立静态托管时不依赖服务端做 history fallback。 - Vite 开发模式已经通过
/api与/socket.io代理对接http://127.0.0.1:5000。
2.2 当前仍然耦合的地方
- 生产 / 部署兼容构建仍输出到
static/frontend/。 -
apps/web_api/app.py的根路由仍负责:- 开发态重定向到
BETTAFISH_FRONTEND_DEV_URL - 非开发态返回
static/frontend/index.html - 构建产物缺失时返回
templates/index.html
- 开发态重定向到
- Docker 当前仍通过后端镜像把前端产物复制到
/app/static/frontend,然后直接启动python -m apps.web_api。
2.3 当前已经暴露出的独立交付阻塞点
- 前端 HTTP 请求全部使用相对路径
/api/...。 - 报告预览和下载也使用相对路径
/api/report/...。 - 引擎工作台 iframe 不是走相对路径,而是按当前页面 hostname 拼接
:8501 / :8502 / :8503。 - 后端未发现通用 HTTP CORS 配置;当前只看到
SocketIO(..., cors_allowed_origins="*")。 - Streamlit 当前按端口直接启动,未配置
--server.baseUrlPath,因此还不能直接无改动地挂到/engines/*这样的代理子路径下。 -
apps/web_ui/vite.config.ts的构建base仍固定为/static/frontend/,这适合当前 Flask 托管模式,不适合作为长期的独立前端交付默认值。
3. 方案选项
方案 A:继续由 Flask 托管 static/frontend/
这是当前基线方案,不是真正的“前端独立交付”。
优点:
- 改动最少。
- 当前 Docker 已经可用。
- 不需要新增代理层。
缺点:
- 后端无法真正收口为 API-only。
- 前端构建产物仍耦合在后端镜像里。
- 首页与部署链路继续依赖 Flask 根路由逻辑。
结论:
- 只能作为过渡基线,不适合作为最终目标。
方案 B:同源反向代理的独立前端服务
推荐方案。前端单独构建并由静态服务承载,但浏览器仍然只访问一个同源入口。
建议拓扑:
flowchart LR
U["Browser"] --> FE["Frontend Proxy / Static Service"]
FE -->|" /api , /socket.io "| API["Flask API :5000"]
FE -->|" /engines/insight "| I["Streamlit Insight :8501"]
FE -->|" /engines/media "| M["Streamlit Media :8502"]
FE -->|" /engines/query "| Q["Streamlit Query :8503"]
API --> DB["PostgreSQL"]
优点:
- 浏览器仍保持同源访问,前端现有大量相对路径
/api/...可以基本保留。 - 报告下载、报告预览 iframe 和 API 调用都可以继续走同源代理。
- 后端可以逐步去掉首页 HTML 托管职责。
- 比“跨域直连 API”改动更小、风险更低。
缺点:
- 需要新增前端静态服务和代理配置。
- Streamlit 需要补齐路径代理能力,不能只靠当前
:8501/:8502/:8503端口拼接。
结论:
- 这是当前最适合落地的主方案。
方案 C:前端独立域名 / 独立源,直接跨域访问后端
例如前端静态站点部署到 CDN、Vercel 或单独域名,直接调用 https://api.example.com。
优点:
- 前后端边界最彻底。
- 后端可以更纯粹地只提供 API。
缺点:
- 需要系统性引入 API Base URL、报告预览 / 下载 URL、引擎 URL 等运行时配置。
- 需要给 Flask API 增加可靠的 HTTP CORS 策略。
- 需要为报告预览 iframe、下载、新开窗口、引擎 iframe 重新设计 URL 生成逻辑。
- 发布面更大,回归风险更高。
结论:
- 适合作为第二阶段能力,不适合当前直接切换。
4. 推荐结论
当前推荐采用:
- 短期继续保留现有
static/frontend/托管作为兼容链路。 - 中期引入“同源反向代理的独立前端服务”。
- 在新链路稳定后,再删除 Flask 根路由中的前端 HTML 托管逻辑。
- 若未来确实需要 CDN / 独立域名前端,再在此基础上推进跨域版本。
换句话说,当前最优路径不是“直接删掉 Flask 托管”,而是先把前端真正迁移到独立服务,再让后端自然退化成 API-only。
5. 推荐实施顺序
阶段 1:前端运行时 URL 抽象
目标:让前端不再把“当前同源 + 端口拼接”写死在各组件里。
建议动作:
- 为前端新增统一 URL 配置层,例如:
VITE_PUBLIC_BASE_PATHVITE_API_BASE_URLVITE_SOCKET_BASE_URLVITE_REPORT_BASE_URLVITE_ENGINE_BASE_URL
- 把当前各处硬编码的
/api/...、报告下载 URL、报告预览 URL、引擎 URL 拼接收敛到一个统一模块。 - 保持默认值仍兼容本地开发:
VITE_PUBLIC_BASE_PATH=/-
VITE_API_BASE_URL=空值时回退同源/api -
VITE_ENGINE_BASE_URL=空值时回退当前行为
注意:
- 这一阶段不需要立即切部署,只要先把 URL 生成逻辑从页面代码里抽离出来。
阶段 2:引擎 iframe 去端口耦合
目标:让前端不再依赖浏览器直接访问 :8501/:8502/:8503。
建议动作:
- 为三个 Streamlit 应用增加可配置的
baseUrlPath。 - 代理层统一暴露:
/engines/insight/engines/media/engines/query
- 把前端
buildEngineUrl()从“当前 hostname + port”改成“统一代理路径”。
这是当前 API-only 收口前的关键前置条件,因为不处理这一层,前端即使独立交付,工作台仍然被后端主机和端口强耦合。
阶段 3:建立独立前端服务
目标:让前端构建产物由独立静态服务承载,而不是继续由 Flask 返回首页。
建议动作:
- 新增独立前端镜像,例如:
infra/frontend/Dockerfile-
infra/frontend/nginx.conf或infra/frontend/Caddyfile
- 前端服务负责:
- 承载静态页面
- 反向代理
/api - 反向代理
/socket.io - 反向代理
/engines/*
- Docker Compose 从当前的
bettafish + db两服务,演进为:frontendapidb- 必要时保留
streamlit子服务或继续由api管控子进程
阶段 4:收口 Flask 根路由
只有在以下条件全部满足后,才建议真正删除 Flask 的前端 HTML 托管:
- 前端已经有独立交付入口。
-
/api、/socket.io、报告下载与预览已经在新链路下验证通过。 - 三个引擎 iframe 已经不再依赖裸端口访问。
- Docker / 本地部署文档已经切到新入口。
- 旧的
static/frontend/链路有明确回退窗口或已经确认无需保留。
届时可以把 apps/web_api/app.py 的根路由收口为:
- API 健康检查
- 404 / 说明页
- 或明确跳转到新的前端入口
6. 当前最值得优先做的具体任务
P0
- 收口前端 URL 配置层,替换散落在前端中的相对路径与端口拼接。
- 为 Streamlit 引擎评估并补齐
baseUrlPath方案。
P1
- 新增独立前端静态服务镜像与代理配置。
- 调整 Docker Compose 为前端 / API 分离拓扑。
P2
- 收口 Flask 根路由。
- 移除后端镜像中对
static/frontend/的复制依赖。
7. 不建议现在立刻做的事
- 不建议现在直接删除
apps/web_api/app.py返回static/frontend/index.html的分支。 - 不建议直接切到“跨域前端直连 API”的方案。
- 不建议在没有处理 Streamlit iframe 之前就宣布后端已经 API-only。
8. 完成标准
当以下条件满足时,可以认为“前端独立交付”已经完成:
- 本地开发继续保持
Vite HMR + Flask API不回退。 - 部署环境中的首页不再由 Flask 返回 HTML。
- 前端构建产物不再复制进后端镜像。
- 报告预览、下载、Socket.IO 和引擎工作台均在新拓扑下正常工作。
-
apps/web_api/app.py不再承担前端页面托管职责。