Files
ftb-project-management/docs/superpowers/specs/2026-07-02-local-cloud-single-source-data-design.md
2026-07-02 14:58:30 +08:00

140 lines
6.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 外网与本地访问地址共享同一数据设计
## 背景
用户澄清后的真实问题不是“两台服务器之间同步数据”,而是:同一套项目系统通过外网地址访问、通过本地/内网地址访问时,应该看到同一份业务数据。
当前仓库同时提供:
- `docker-compose.prod.yml`:云服务器生产栈,使用 `postgres_data``redis_data``server_data`
- `docker-compose.local.yml`:本地服务器/LAN 栈,使用 `local_postgres_data``local_redis_data``local_server_data`
如果为了同一套业务同时启动这两套栈,外网地址和本地地址很可能分别打到不同的 Compose project、不同的 PostgreSQL volume因此会看到两套数据。
## 目标
- 外网访问地址和本地/内网访问地址进入同一套运行中的服务栈。
- 同一套服务栈只使用一个 PostgreSQL 主数据 volume。
- 用户从任意入口创建或修改产品、项目、版本、任务、测试用例、Bug 后,从另一个入口刷新能看到相同数据。
- 文档明确区分“同一套系统的多个访问入口”和“独立本地演示环境”。
- 避免把这个需求误实现为双向同步或跨服务器数据库复制。
## 非目标
- 不做本地数据库和云端数据库的双向同步。
- 不要求本地服务器连接云服务器数据库。
- 不迁移 AI Provider 配置文件到数据库。
- 不引入后台同步任务、冲突合并队列或数据库复制方案。
## 推荐方案
采用“单部署栈,多访问入口”的模式。
无论用户从外网域名、服务器公网 IP、局域网 IP还是本机地址访问都应该进入同一个 Nginx然后由该 Nginx 转发到同一个 `web``server`,最终读写同一个 PostgreSQL。
```text
外网地址 / 域名
-> 同一个 Nginx
-> 同一个 NestJS server
-> 同一个 PostgreSQL volume
本地 / 内网地址
-> 同一个 Nginx
-> 同一个 NestJS server
-> 同一个 PostgreSQL volume
```
前端继续使用同域 API
```env
NEXT_PUBLIC_API_URL=/api/v1
```
这样浏览器从哪个地址打开页面,就向该地址下的 `/api/v1` 发请求。只要这些地址最终落到同一套 Nginx/server数据天然一致。
## 部署模式
### 1. 生产/正式业务栈
正式业务只运行一套 Compose例如云服务器上运行 `docker-compose.prod.yml`
该栈可以同时支持:
- 外网域名:`https://pm.example.com`
- 公网 IP`http://x.x.x.x`
- 内网 IP`http://192.168.1.50`
- 本机访问:`http://localhost`
这些访问方式只要都进入同一个 Nginx就会共享同一份数据。
### 2. 本地/LAN 演示栈
`docker-compose.local.yml` 只能用于一套独立的本地/LAN 演示环境。它默认使用 `local_*` volumes不会和生产栈共享数据。
文档必须明确:如果用户希望外网地址和本地地址看到同一份正式业务数据,不要同时运行 local 栈和 prod 栈来承载同一套业务。
## 配置设计
### Nginx
当前 `deploy/nginx/default.conf.template` 只有一个 `server` block并使用
```nginx
server_name ${SERVER_NAME};
```
因为同一个 Nginx 容器只有这一套 80 端口 server block即使 Host 不匹配Nginx 也会把请求落到该默认 server block。因此外网域名和内网 IP 在多数单站点部署中已经可以进入同一套服务。
文档需要补充:
- `SERVER_NAME` 可以保留主域名。
- 如果服务器上存在外层 Nginx、多站点配置或严格 host 路由,需要把外网域名和内网访问名都转发到同一套 Compose Nginx。
- 不要为外网和本地访问分别启动两套 Compose。
### 前端 API
`NEXT_PUBLIC_API_URL` 应保持:
```env
NEXT_PUBLIC_API_URL=/api/v1
```
不要为外网和本地访问分别写成不同绝对 API 地址。绝对地址可能让不同入口打到不同后端,重新制造数据分叉。
### NextAuth URL
`NEXTAUTH_URL` 继续使用主要正式访问地址。当前系统的业务数据读写不依赖浏览器 localStorage 作为主存储;数据一致性的关键是 `/api/v1` 是否落到同一个后端和数据库。
如果后续接入 OAuth 回调,需要再评估多 Host 登录体验,但这不影响本次业务数据一致性设计。
## 文档与脚本调整
需要更新:
- `docs/deployment.md`:新增“同一部署多入口访问”说明;强调外网/内网要进入同一套 Compose。
- `docs/architecture.md`:生产部署边界改为“一个正式业务栈可以有多个访问入口”。
- `docs/decisions.md`:增加决策,外网/本地地址一致性通过同一部署栈保证,不做数据同步。
- `docs/workflow.md`:部署流程提示不要为同一业务同时启动 prod 和 local 两套栈。
- `.env.production.example`:注释说明 `SERVER_NAME` 是主要域名,内网 IP 通常也会命中同一 Nginx多站点环境需显式路由。
- `.env.local-server.example`:注释说明该文件用于独立本地/LAN 演示库,不会与生产数据一致。
- `scripts/verify-production-deploy.mjs`:增加部署文档关键语句校验,避免后续文档丢失这个约束。
## 验证方式
实现后需要验证:
1. `pnpm deploy:verify` 通过。
2. `pnpm --filter web type-check` 通过。
3. `pnpm --filter server type-check` 通过。
4. 正式栈启动后,外网地址访问 `/api/v1/config/ai` 返回 200。
5. 同一台服务器的本地/内网地址访问 `/api/v1/config/ai` 返回 200。
6. 从外网地址创建一条业务数据后,从本地/内网地址刷新能看到。
7. 从本地/内网地址修改同一条业务数据后,从外网地址刷新能看到。
## 风险与处理
- 如果外网地址和本地地址分别指向不同 Compose project数据仍会分叉。处理方式是停止其中一套业务栈只保留一个正式业务栈。
- 如果 `NEXT_PUBLIC_API_URL` 被配置成绝对 URL不同入口可能绕到不同后端。处理方式是正式同域部署保持 `/api/v1`
- 如果机器上还有外层 Nginx 或宝塔面板,多 Host 需要全部反代到同一套 Compose Nginx。
- 本地/LAN 演示栈仍然有价值,但它是独立演示环境,不承诺与正式业务数据一致。