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

144 lines
6.2 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` 三个 volume。本地服务器部署使用 `docker-compose.local.yml`,数据写入 `local_postgres_data``local_redis_data``local_server_data` 三个独立 volume。
这意味着本地服务器和云服务器默认是两套数据。本地服务器新增或修改产品、项目、版本、任务、测试用例、Bug 后,云服务器不会自动更新。
用户确认本地和云端都会做备份,因此在线业务不需要两套独立主库。备份用于灾备,在线写入源应保持唯一。
## 目标
- 云服务器 PostgreSQL 作为唯一业务主数据源。
- 本地服务器访问和修改的业务数据与云服务器一致。
- 本地服务器上的写入立即落到云端主库,云端打开后能看到同一份数据。
- 保留本地服务器备份能力,但默认不把本地 PostgreSQL 当作业务写入库。
- 避免双主同步、定时合并、手工覆盖等高风险机制。
## 非目标
- 不做本地数据库和云端数据库的双向同步。
- 不支持离线写入后再自动合并到云端。
- 不把云端 PostgreSQL 裸露到公网。
- 不在本次改动中迁移 AI Provider 配置文件到数据库。
## 推荐方案
采用“云端单主库 + 本地服务器连接云端主库”的模式。
本地服务器仍可运行 `web``server``redis``nginx`,但 `server``DATABASE_URL` 指向云端主库。业务数据通过 NestJS `DataModule` 写入云端 PostgreSQL 的 `app_data` 表。前端仍走同域 `/api/v1`,由本地 Nginx 转发到本地 server本地 server 再连接云端数据库。
```text
本地浏览器
-> 本地 Nginx /api
-> 本地 NestJS server
-> 云端 PostgreSQL 主库
云端浏览器
-> 云端 Nginx /api
-> 云端 NestJS server
-> 云端 PostgreSQL 主库
```
## 部署配置设计
### 1. 新增本地共享主库 env 示例
新增 `.env.local-server.cloud-data.example`,用于说明本地服务器连接云端主库时应填写的配置。
关键字段:
- `DATABASE_URL`:云端 PostgreSQL 连接串,推荐通过 VPN、专线、内网穿透或 SSH tunnel 暴露给本地服务器。
- `NEXT_PUBLIC_API_URL=/api/v1`:本地浏览器仍访问本地 Nginx。
- `NEXTAUTH_URL`:本地服务器访问地址,例如 `http://192.168.1.50:8080`
- `REDIS_URL=redis://redis:6379`Redis 可继续本地运行,当前业务主数据不依赖 Redis 持久化同步。
### 2. 调整本地 Compose 的数据库依赖
`docker-compose.local.yml` 当前强制启动并依赖本地 `postgres`。这会让本地部署天然变成另一套业务数据。
调整后:
- `postgres` 改为可选 profile例如 `profiles: ['local-db']`
- `server.depends_on` 不再强依赖 `postgres`,只保留对 `redis` 的健康依赖。
- 默认本地服务器部署要求显式提供 `DATABASE_URL`
- 如果确实要跑完全离线的本地演示库,可以通过 profile 启动本地 Postgres。
预期命令:
```bash
# 本地服务器连接云端主库
docker compose --env-file .env.local-server.cloud-data -f docker-compose.local.yml up -d
# 完全本地演示库
docker compose --env-file .env.local-server -f docker-compose.local.yml --profile local-db up -d
```
### 3. 更新部署文档
`docs/deployment.md` 增加“本地服务器连接云端主库”章节,明确:
- 推荐模式是云端单主库。
- 本地服务器默认不应使用独立业务库。
- 本地和云端备份是灾备,不是双主同步。
- 云端数据库连接必须走安全通道。
`docs/architecture.md` 更新生产部署边界,说明本地服务器可作为云端主库的第二个应用入口。
`docs/decisions.md` 增加设计决策:在线业务数据采用单主数据源,不做双主同步。
`docs/workflow.md` 更新部署流程,避免用户把本地服务器误当成独立生产库。
## 安全边界
推荐连接方式按优先级排序:
1. VPN 或组网工具,使本地服务器通过私网访问云端数据库。
2. SSH tunnel只在本地服务器和云服务器之间建立转发。
3. 云安全组只允许本地服务器固定公网 IP 访问数据库端口。
不推荐:
- PostgreSQL `5432` 对公网开放给任意来源。
- 多台服务器共享同一个弱密码数据库账户。
- 在 Git 中提交真实 `DATABASE_URL`、数据库密码或 AI Key。
## 备份策略
备份保留两地:
- 云端定期备份主库。
- 本地定期拉取云端主库备份或保存导出的快照。
备份文件不参与实时业务写入。发生故障时由人工选择某个备份恢复为新的主库,恢复前先停止其他写入入口,避免恢复期间出现新的分叉。
## 数据一致性与冲突
因为只有云端 PostgreSQL 是主库,本地和云端不会出现数据库级双写冲突。
应用层仍保留现有 `app_data` 乐观锁:
- 多个用户同时修改同一个业务文档时,后端返回 `409 APP_DATA_CONFLICT`
- 前端不自动覆盖服务端新版本。
该机制继续处理多人同时编辑,而不是处理两套数据库同步。
## 验证方式
完成实现后需要验证:
1. 本地服务器启动后,`curl http://localhost:8080/api/v1/config/ai` 返回 200。
2. 本地服务器创建一条业务数据,例如产品或任务。
3. 云服务器页面刷新后能看到同一条数据。
4. 云服务器修改同一条数据后,本地服务器刷新能看到变化。
5. 停止本地 `postgres` 容器后,本地服务器仍能读写业务数据。
6. `pnpm deploy:verify` 通过。
7. 前端和后端类型检查通过。
## 风险与处理
- 云端主库不可达时,本地服务器不能写业务数据。这是单主设计的预期行为,错误信息应通过现有 API 失败提示暴露。
- 如果本地仍误启独立 Postgres 且 `DATABASE_URL` 指向它,会再次形成两套数据。文档和 env 示例必须把默认推荐改成云端主库。
- AI Provider 配置当前仍是 server volume 文件。本地和云端 AI 配置可能不一致;这是本次非目标。后续如果需要,也应把 AI 配置迁入数据库或明确只在云端维护。