# 外网与本地访问地址共享同一数据设计 ## 背景 用户澄清后的真实问题不是“两台服务器之间同步数据”,而是:同一套项目系统通过外网地址访问、通过本地/内网地址访问时,应该看到同一份业务数据。 当前仓库同时提供: - `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 演示栈仍然有价值,但它是独立演示环境,不承诺与正式业务数据一致。