6.1 KiB
外网与本地访问地址共享同一数据设计
背景
用户澄清后的真实问题不是“两台服务器之间同步数据”,而是:同一套项目系统通过外网地址访问、通过本地/内网地址访问时,应该看到同一份业务数据。
当前仓库同时提供:
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。
外网地址 / 域名
-> 同一个 Nginx
-> 同一个 NestJS server
-> 同一个 PostgreSQL volume
本地 / 内网地址
-> 同一个 Nginx
-> 同一个 NestJS server
-> 同一个 PostgreSQL volume
前端继续使用同域 API:
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,并使用:
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 应保持:
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:增加部署文档关键语句校验,避免后续文档丢失这个约束。
验证方式
实现后需要验证:
pnpm deploy:verify通过。pnpm --filter web type-check通过。pnpm --filter server type-check通过。- 正式栈启动后,外网地址访问
/api/v1/config/ai返回 200。 - 同一台服务器的本地/内网地址访问
/api/v1/config/ai返回 200。 - 从外网地址创建一条业务数据后,从本地/内网地址刷新能看到。
- 从本地/内网地址修改同一条业务数据后,从外网地址刷新能看到。
风险与处理
- 如果外网地址和本地地址分别指向不同 Compose project,数据仍会分叉。处理方式是停止其中一套业务栈,只保留一个正式业务栈。
- 如果
NEXT_PUBLIC_API_URL被配置成绝对 URL,不同入口可能绕到不同后端。处理方式是正式同域部署保持/api/v1。 - 如果机器上还有外层 Nginx 或宝塔面板,多 Host 需要全部反代到同一套 Compose Nginx。
- 本地/LAN 演示栈仍然有价值,但它是独立演示环境,不承诺与正式业务数据一致。