Files
raisfast/docs/cloud-backup-design.md
T

393 lines
14 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.
# 云备份服务设计
> raisfast Cloud Backup — 私有化数据的安全网
>
> 定位:**零配置、端到端加密、增量备份的云端备份服务**
---
## 1. 为什么需要这个功能
| 场景 | 后果 | 用户心理 |
|---|---|---|
| 硬盘损坏 | 所有客户数据丢失 | "我用私有化就是为了安全,结果数据反而丢了?" |
| 误删数据 | 重要商机/联系人丢失 | "有没有办法恢复?" |
| 勒索病毒 | 数据被加密,无法恢复 | "我愿意花钱恢复数据" |
| 服务器故障 | 业务中断 | "备份在哪里?" |
| 迁移新机器 | 需要手动复制数据 | "能不能一键迁移?" |
**私有化用户最大的焦虑 = 数据丢失没有兜底。** 解决这个焦虑 = 核心卖点 + 商业化入口。
---
## 2. 设计目标
| 目标 | 说明 |
|---|---|
| **零配置** | 注册账号 → 自动备份,不需要配置 S3/Azure |
| **端到端加密** | 数据在客户端加密,云端只存密文,服务端无法解密 |
| **增量备份** | 只传输变更部分,节省带宽和时间 |
| **版本历史** | 保留 N 个历史版本,支持回滚到任意时间点 |
| **自动调度** | 每小时/每天自动备份,无需手动操作 |
| **跨平台** | 桌面应用 + 服务器都支持 |
| **一键恢复** | 选一个版本 → 一键恢复到任意设备 |
---
## 3. 技术架构
### 3.1 整体流程
```
┌─────────────────────────────────────────────────────────────┐
│ 用户设备(客户端) │
│ │
│ SQLite DB → 变更检测 → 增量计算 → AES-256加密 → 压缩 │
│ ↓ │
│ 上传到备份服务器 │
│ ↓ │
│ 备份记录(本地) ← 备份元数据 ← 备份服务器确认 │
└─────────────────────────────────────────────────────────────┘
↓ 加密数据上传
┌─────────────────────────────────────────────────────────────┐
│ 备份服务器(云端) │
│ │
│ 接收加密块 → 存储到对象存储(S3/R2)→ 记录元数据 │
│ │
│ 存储内容: │
│ ├── 用户账号信息(邮箱、设备列表) │
│ ├── 备份元数据(时间、大小、版本号) │
│ └── 加密数据块(AES-256-GCM,服务端无法解密) │
└─────────────────────────────────────────────────────────────┘
```
### 3.2 端到端加密方案
```
密钥层级:
用户密码
↓ Argon2id
主密钥 (Master Key)
↓ HKDF-SHA256
├── 数据加密密钥 (DEK) → 加密备份数据
├── 密钥加密密钥 (KEK) → 加密存储 DEK
└── 文件名加密密钥 (FEK) → 加密备份文件名
流程:
1. 注册时:用户密码 → Argon2id → 主密钥
2. 备份时:主密钥 → HKDF → DEK → AES-256-GCM 加密数据
3. 上传时:只传密文,服务端看不到明文
4. 恢复时:密码 → 重新派生主密钥 → 解密
安全保证:
- 服务端被黑客入侵 → 无法解密(没有用户密码)
- 备份文件泄露 → 无法解密(AES-256-GCM
- 用户忘记密码 → 无法恢复(零知识架构)
```
### 3.3 增量备份方案
```
SQLite WAL 模式:
首次备份(全量):
SQLite 文件 → 分块(4MB/块)→ 每块计算 SHA-256 → 加密 → 上传
后续备份(增量):
1. 对比当前块 SHA-256 与上次备份的块列表
2. 只上传 SHA 变化的块
3. 生成新的快照清单(指向块列表)
快照 A(全量): [block-1, block-2, block-3, block-4]
快照 B(增量): [block-1, block-2, block-3-new, block-4]
↑ 只有这个变了
存储开销:每次增量只存变化的块(通常 < 1MB)
```
### 3.4 SQLite 备份具体实现
```
方案选择:SQLite Backup APIsqlite3_backup_init
Step 1: 创建临时 SQLite 副本
→ 使用 SQLite Backup API 将热数据库复制到临时文件
→ 确保数据一致性(不锁库)
Step 2: 计算块哈希
→ 临时文件按 4MB 分块
→ 计算每块 SHA-256
Step 3: 增量对比
→ 与上次备份的块列表对比
→ 识别变化的块
Step 4: 加密 + 压缩
→ 变化的块 → zstd 压缩 → AES-256-GCM 加密
Step 5: 上传
→ 加密块上传到备份服务器
→ 生成新快照清单
Step 6: 清理
→ 删除临时文件
→ 保留本地备份元数据
```
---
## 4. 备份服务端设计
### 4.1 技术选型
| 组件 | 选型 | 说明 |
|---|---|---|
| 备份服务器 | Rust (Axum) | 与 raisfast 同技术栈,可独立部署 |
| 对象存储 | Cloudflare R2 | 免出站流量,S3 兼容 |
| 用户认证 | JWT | 简单可靠 |
| 数据库 | PostgreSQL | 存储用户/元数据 |
| 支付 | Stripe | 国际通用 |
### 4.2 API 设计
```
认证:
POST /api/v1/auth/register # 注册(邮箱+密码)
POST /api/v1/auth/login # 登录
POST /api/v1/auth/token # 刷新 Token
设备管理:
GET /api/v1/devices # 列出设备
POST /api/v1/devices # 注册设备
DELETE /api/v1/devices/:id # 移除设备
备份:
POST /api/v1/backups/upload # 上传备份块
POST /api/v1/backups/snapshot # 创建快照
GET /api/v1/backups/snapshots # 列出快照
GET /api/v1/backups/snapshots/:id # 获取快照详情
DELETE /api/v1/backups/snapshots/:id # 删除快照
恢复:
POST /api/v1/backups/restore/:id # 获取恢复所需的块列表
GET /api/v1/backups/blocks/:hash # 下载指定块
订阅:
GET /api/v1/subscription # 当前订阅状态
POST /api/v1/subscription/checkout # Stripe Checkout
POST /api/v1/subscription/webhook # Stripe Webhook
```
### 4.3 数据模型
```sql
-- 用户
CREATE TABLE users (
id UUID PRIMARY KEY,
email TEXT UNIQUE NOT NULL,
password_hash TEXT NOT NULL, -- Argon2id
created_at TIMESTAMPTZ NOT NULL
);
-- 设备
CREATE TABLE devices (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
name TEXT NOT NULL, -- "MacBook Pro" / "Server-01"
device_key TEXT NOT NULL, -- 设备公钥指纹
created_at TIMESTAMPTZ NOT NULL
);
-- 备份快照
CREATE TABLE snapshots (
id UUID PRIMARY KEY,
device_id UUID REFERENCES devices(id),
snapshot_no BIGINT NOT NULL, -- 快照序号
size_bytes BIGINT NOT NULL, -- 原始大小
block_count INT NOT NULL, -- 块数量
created_at TIMESTAMPTZ NOT NULL
);
-- 备份块(去重)
CREATE TABLE blocks (
hash TEXT PRIMARY KEY, -- SHA-256
size_bytes INT NOT NULL,
storage_key TEXT NOT NULL, -- S3/R2 对象键
created_at TIMESTAMPTZ NOT NULL
);
-- 快照 → 块映射
CREATE TABLE snapshot_blocks (
snapshot_id UUID REFERENCES snapshots(id),
block_hash TEXT REFERENCES blocks(hash),
block_index INT NOT NULL, -- 块序号
PRIMARY KEY (snapshot_id, block_index)
);
-- 订阅
CREATE TABLE subscriptions (
id UUID PRIMARY KEY,
user_id UUID REFERENCES users(id),
plan TEXT NOT NULL, -- free/pro/enterprise
storage_limit BIGINT NOT NULL, -- 存储上限(字节)
stripe_id TEXT, -- Stripe 订阅 ID
status TEXT NOT NULL, -- active/canceled
created_at TIMESTAMPTZ NOT NULL
);
```
---
## 5. 客户端集成
### 5.1 桌面应用集成
```
设置页面:
┌─────────────────────────────────────────────┐
│ 云备份设置 │
│ │
│ 账号:chris@example.com [登录/注册] │
│ 状态:✅ 已连接 上次备份:3 分钟前 │
│ │
│ 备份频率: │
│ ○ 每小时 ● 每天 ○ 每周 │
│ │
│ 备份历史: │
│ ┌─────────────────────────────────────────┐ │
│ │ 2024-04-23 14:30 12.5 MB [恢复] [删除]│ │
│ │ 2024-04-22 14:30 12.3 MB [恢复] [删除]│ │
│ │ 2024-04-21 14:30 11.8 MB [恢复] [删除]│ │
│ └─────────────────────────────────────────┘ │
│ │
│ 恢复到其他设备: │
│ [选择快照] → [生成恢复码] → 在新设备输入恢复码 │
│ │
│ [立即备份] │
└─────────────────────────────────────────────┘
```
### 5.2 CLI 集成
```bash
# 登录
rust-backup login --email chris@example.com
# 手动备份
rust-backup backup --project ./my-project
# 查看备份历史
rust-backup snapshots --project ./my-project
# 恢复到指定版本
rust-backup restore --snapshot abc-123 --output ./restored-project
# 恢复到新设备(使用恢复码)
rust-backup restore --code ABCD-1234-EFGH-5678 --output ./my-project
# 生成恢复码(在其他设备上使用)
rust-backup recovery-code --snapshot abc-123
```
### 5.3 Plugin API(可在插件中触发备份)
```javascript
// 在插件中触发备份(如创建重要数据后)
Plugin.on_content_created = function(input) {
const data = parseBody(input);
if (data.content_type === "deal" && data.stage === "closed_won") {
// 赢单后自动备份
Host.backup();
}
return ok(data);
};
```
---
## 6. 定价
| 套餐 | 存储空间 | 保留版本 | 设备数 | 价格 |
|---|---|---|---|---|
| **免费** | 1 GB | 7 天 | 1 台 | $0 |
| **Pro** | 50 GB | 90 天 | 5 台 | **$9.9/月** |
| **Team** | 500 GB | 365 天 | 无限 | **$29/月** |
| **Enterprise** | 无限 | 无限 | 无限 | **$99/月** |
### 6.1 成本估算
| 项目 | 单价 | 1000 个 Pro 用户成本 |
|---|---|---|
| Cloudflare R2 存储 | $0.015/GB/月 | $75050GB × 1000 × $0.015 |
| R2 上传(Class A 操作) | $4.50/百万次 | ~$50 |
| R2 下载(恢复) | 免费(R2 无出站费) | $0 |
| 服务器(1 台) | $20/月 | $20 |
| **总成本** | | **~$820/月** |
| **收入** | $9.9 × 1000 | **$9,900/月** |
| **利润率** | | **~92%** |
---
## 7. 开发路线
### Phase 1 — 核心备份(1-2 月)
| 任务 | 说明 |
|---|---|
| 备份服务端 | Rust + Axum,用户注册/登录/上传/下载 |
| 端到端加密 | AES-256-GCM + Argon2id 密钥派生 |
| 增量备份引擎 | SQLite 分块 + SHA-256 对比 + zstd 压缩 |
| 桌面应用集成 | 设置页面 + 自动调度 + 状态显示 |
| CLI 命令 | `rust-backup login/backup/restore` |
### Phase 2 — 恢复体验(2-3 月)
| 任务 | 说明 |
|---|---|
| 一键恢复 | 选择快照 → 一键恢复 |
| 恢复码 | 跨设备恢复(类似恢复短语) |
| 版本对比 | 对比两个快照的差异 |
| 选择性恢复 | 只恢复特定表/数据 |
### Phase 3 — 高级功能(3-6 月)
| 任务 | 说明 |
|---|---|
| 团队功能 | 多设备管理、团队共享备份 |
| Point-in-time | 恢复到精确时间点 |
| 备份监控 | 备份失败告警、异常检测 |
| 合规报告 | 备份报告生成(企业审计用) |
---
## 8. 商业价值
### 8.1 为什么备份服务是最佳商业化入口
| 优势 | 说明 |
|---|---|
| **刚需** | 每个私有化用户都需要备份 |
| **持续付费** | 按月/年订阅,不是一次性购买 |
| **高粘性** | 备份数据在云端,用户不会轻易离开 |
| **高利润率** | 存储成本极低(~8%),利润率 90%+ |
| **信任入口** | 用户把数据交给你备份 → 建立信任 → 后续卖更多服务 |
| **天然获客** | 桌面应用内提示"开启云备份" → 零成本转化 |
### 8.2 与主产品的协同
```
raisfast 开源版(免费)
↓ 用户使用,积累数据
↓ 看到备份提示
rust-backup 云备份(付费)
↓ 用户付费备份
↓ 建立信任
↓ 推荐其他付费服务
raisfast 企业插件包(付费)
raisfast 行业模板包(付费)
raisfast 技术支持(付费)
```
**备份是整个商业化飞轮的起点。**