init: FileDrop phase1 architecture and scaffold
- Rust axum signaling server with WebSocket support - Lit + TypeScript frontend with Vite - Redis session storage with TTL - WebRTC transport and crypto client stubs - Phase1 architecture plan in plans/ - Deploy directory structure prepared
This commit is contained in:
392
plans/phase1-architecture.md
Normal file
392
plans/phase1-architecture.md
Normal file
@@ -0,0 +1,392 @@
|
||||
# FileDrop - 安全无痕文件传输 Web App
|
||||
|
||||
## 产品定位
|
||||
|
||||
免费、无注册、无历史记录的安全文件传输工具。
|
||||
|
||||
- 优先局域网,兼顾公网
|
||||
- WebRTC 直连优先,TURN 兜底
|
||||
- 浏览器端端到端加密
|
||||
- 传完即销毁,不留痕迹
|
||||
|
||||
---
|
||||
|
||||
## 第一期范围
|
||||
|
||||
### 要做
|
||||
|
||||
- 扫码/短码配对
|
||||
- WebRTC DataChannel 传输
|
||||
- 浏览器端 AES-GCM 分块加密
|
||||
- TURN 回退
|
||||
- 会话超时自动清理
|
||||
- 小文件传输(<100MB)
|
||||
|
||||
### 不做
|
||||
|
||||
- 用户注册/登录
|
||||
- 历史记录
|
||||
- 大文件/断点续传
|
||||
- 付费/套餐
|
||||
- Postgres
|
||||
- WASM
|
||||
- 对象存储中转
|
||||
|
||||
---
|
||||
|
||||
## 技术栈
|
||||
|
||||
| 层 | 技术 |
|
||||
|---|---|
|
||||
| 前端 | Lit + TypeScript + Vite |
|
||||
| 后端 | Rust + axum + tokio |
|
||||
| 临时状态 | Redis |
|
||||
| NAT 穿透 | coturn |
|
||||
| 反向代理 | Caddy |
|
||||
| 部署 | Debian 12 + systemd |
|
||||
|
||||
---
|
||||
|
||||
## 系统架构
|
||||
|
||||
```
|
||||
Browser A Browser B
|
||||
| |
|
||||
|---- HTTPS / WSS -------------|
|
||||
| |
|
||||
Rust Signaling Server
|
||||
|
|
||||
Redis
|
||||
|
|
||||
coturn
|
||||
```
|
||||
|
||||
### 职责划分
|
||||
|
||||
**浏览器**
|
||||
- 选文件
|
||||
- 生成会话密钥
|
||||
- 二维码/短码配对
|
||||
- WebRTC 建连
|
||||
- 文件分块加密
|
||||
- DataChannel 发送/接收
|
||||
|
||||
**Rust 服务端**
|
||||
- 创建/加入会话
|
||||
- WebSocket 信令转发
|
||||
- 临时会话状态
|
||||
- 过期清理
|
||||
- 下发 ICE 配置
|
||||
|
||||
**Redis**
|
||||
- 会话临时状态
|
||||
- 在线状态
|
||||
- 一次性加入令牌
|
||||
- TTL 自动过期
|
||||
|
||||
**coturn**
|
||||
- STUN/TURN 服务
|
||||
|
||||
---
|
||||
|
||||
## 核心流程
|
||||
|
||||
### 1. 创建会话
|
||||
|
||||
1. 发送端选择文件
|
||||
2. 浏览器本地生成 `session_secret`
|
||||
3. 请求 `POST /api/sessions`
|
||||
4. 服务端返回 `room_id`、`join_token`、`ws_url`、`ice_servers`
|
||||
5. 前端生成分享信息:
|
||||
- 短码:`room_id`
|
||||
- 链接:`/join/{room_id}#k={session_secret}&t={join_token}`
|
||||
|
||||
> `#k=` 在 URL fragment 中,不会发给服务端
|
||||
|
||||
### 2. 加入会话
|
||||
|
||||
1. 接收端扫码进入
|
||||
2. 前端从 fragment 取出 `session_secret` 和 `join_token`
|
||||
3. 请求 `POST /api/sessions/{room_id}/join`
|
||||
4. 服务端校验会话状态和 token
|
||||
5. 成功后建立 WebSocket
|
||||
|
||||
### 3. 信令协商
|
||||
|
||||
1. 双方连上 `WS /ws`
|
||||
2. 发送端创建 offer
|
||||
3. 接收端返回 answer
|
||||
4. 双方交换 ICE candidate
|
||||
5. 建立 `RTCDataChannel`
|
||||
|
||||
### 4. 文件传输
|
||||
|
||||
1. 发送端发送 `file_manifest`
|
||||
2. 文件切块(64KB ~ 256KB)
|
||||
3. 每块独立 AES-GCM 加密
|
||||
4. 通过 DataChannel 发送
|
||||
5. 接收端逐块解密并缓存
|
||||
6. 完成后生成下载文件
|
||||
7. 双方发送完成确认并关闭会话
|
||||
|
||||
### 5. 会话销毁
|
||||
|
||||
触发条件:
|
||||
- 传输完成
|
||||
- 用户主动取消
|
||||
- 超时(15 分钟)
|
||||
- WebSocket/RTC 长时间断开
|
||||
|
||||
---
|
||||
|
||||
## 协议设计
|
||||
|
||||
### HTTP API
|
||||
|
||||
#### `POST /api/sessions`
|
||||
|
||||
创建会话
|
||||
|
||||
**请求**
|
||||
```json
|
||||
{
|
||||
"file_count": 2,
|
||||
"total_size": 1834201
|
||||
}
|
||||
```
|
||||
|
||||
**响应**
|
||||
```json
|
||||
{
|
||||
"room_id": "8F4K2P",
|
||||
"join_token": "opaque-token",
|
||||
"ws_url": "wss://app.example.com/ws",
|
||||
"expires_at": "2026-04-09T12:00:00Z",
|
||||
"ice_servers": [
|
||||
{ "urls": ["stun:turn.example.com:3478"] },
|
||||
{
|
||||
"urls": ["turn:turn.example.com:3478?transport=udp"],
|
||||
"username": "u",
|
||||
"credential": "p"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### `POST /api/sessions/{room_id}/join`
|
||||
|
||||
加入会话
|
||||
|
||||
**请求**
|
||||
```json
|
||||
{
|
||||
"join_token": "opaque-token"
|
||||
}
|
||||
```
|
||||
|
||||
**响应**
|
||||
```json
|
||||
{
|
||||
"ws_url": "wss://app.example.com/ws",
|
||||
"expires_at": "2026-04-09T12:00:00Z",
|
||||
"ice_servers": [...]
|
||||
}
|
||||
```
|
||||
|
||||
#### `GET /health`
|
||||
|
||||
健康检查
|
||||
|
||||
### WebSocket 消息
|
||||
|
||||
统一格式:
|
||||
```json
|
||||
{
|
||||
"type": "offer|answer|ice|ready|cancel|error|ping|pong",
|
||||
"room_id": "8F4K2P",
|
||||
"role": "sender|receiver",
|
||||
"payload": {}
|
||||
}
|
||||
```
|
||||
|
||||
### DataChannel 消息
|
||||
|
||||
控制消息(JSON):
|
||||
- `file_manifest` - 文件清单
|
||||
- `chunk_ack` - 分块确认
|
||||
- `transfer_complete` - 传输完成
|
||||
|
||||
数据面:`ArrayBuffer`(加密分块)
|
||||
|
||||
---
|
||||
|
||||
## 安全设计
|
||||
|
||||
1. 会话密钥只在浏览器生成和持有
|
||||
2. 文件名和文件内容都加密
|
||||
3. 服务端只看到 room_id、连接状态、粗粒度元信息
|
||||
4. 不保存文件内容
|
||||
5. 不保存传输历史
|
||||
6. Redis 全部使用 TTL
|
||||
7. 所有页面和 WS 强制 HTTPS/WSS
|
||||
8. TURN 使用临时凭证
|
||||
9. 前端在完成或取消后清空密钥和缓存
|
||||
|
||||
**加密方案**
|
||||
- 会话密钥:32 字节随机
|
||||
- 密钥派生:HKDF-SHA-256
|
||||
- 分块加密:AES-GCM
|
||||
- 摘要校验:SHA-256
|
||||
|
||||
---
|
||||
|
||||
## 前端页面
|
||||
|
||||
| 路由 | 说明 |
|
||||
|---|---|
|
||||
| `/` | 首页:发送/接收入口 |
|
||||
| `/send` | 选文件、创建会话、展示二维码 |
|
||||
| `/join/:roomId` | 扫码进入、确认接收、下载 |
|
||||
| `/expired` | 会话失效提示 |
|
||||
|
||||
---
|
||||
|
||||
## Redis 设计
|
||||
|
||||
### Key 结构
|
||||
|
||||
- `session:{room_id}` - 会话元数据
|
||||
- `presence:{room_id}:sender` - 发送端在线状态
|
||||
- `presence:{room_id}:receiver` - 接收端在线状态
|
||||
- `join_token:{room_id}` - 一次性加入令牌
|
||||
- `ws:{connection_id}` - WebSocket 连接映射
|
||||
|
||||
### TTL
|
||||
|
||||
- 会话:15 分钟
|
||||
- 已完成会话:立即删除或保留 1 分钟缓冲
|
||||
- WebSocket presence:30-60 秒心跳续期
|
||||
|
||||
---
|
||||
|
||||
## 开发里程碑
|
||||
|
||||
### P0 原型
|
||||
- 创建/加入会话
|
||||
- WS 信令
|
||||
- DataChannel 建立
|
||||
- 文本消息互通
|
||||
|
||||
### P1 小文件传输
|
||||
- 文件 manifest
|
||||
- 分块发送
|
||||
- 接收后下载
|
||||
- 进度显示
|
||||
|
||||
### P2 安全闭环
|
||||
- 会话密钥生成
|
||||
- 文件名/内容加密
|
||||
- 会话销毁
|
||||
- Redis TTL 清理
|
||||
|
||||
### P3 稳定性
|
||||
- TURN 兜底验证
|
||||
- 异常断开处理
|
||||
- 超时与取消
|
||||
- Chrome / Safari / iOS 测试
|
||||
|
||||
### P4 上线
|
||||
- Debian 部署
|
||||
- HTTPS/WSS
|
||||
- 基础限流
|
||||
- 基础日志与监控
|
||||
|
||||
---
|
||||
|
||||
## 项目结构
|
||||
|
||||
```
|
||||
filedrop/
|
||||
├── Cargo.toml
|
||||
├── plans/
|
||||
│ └── phase1-architecture.md
|
||||
├── server/
|
||||
│ ├── src/
|
||||
│ │ ├── main.rs
|
||||
│ │ ├── config.rs
|
||||
│ │ ├── api/
|
||||
│ │ │ ├── mod.rs
|
||||
│ │ │ ├── sessions.rs
|
||||
│ │ │ └── health.rs
|
||||
│ │ ├── signaling/
|
||||
│ │ │ ├── mod.rs
|
||||
│ │ │ ├── ws_handler.rs
|
||||
│ │ │ └── room.rs
|
||||
│ │ ├── session/
|
||||
│ │ │ ├── mod.rs
|
||||
│ │ │ ├── service.rs
|
||||
│ │ │ └── store.rs
|
||||
│ │ └── ice/
|
||||
│ │ ├── mod.rs
|
||||
│ │ └── config.rs
|
||||
│ └── Cargo.toml
|
||||
├── web/
|
||||
│ ├── package.json
|
||||
│ ├── tsconfig.json
|
||||
│ ├── vite.config.ts
|
||||
│ ├── index.html
|
||||
│ └── src/
|
||||
│ ├── main.ts
|
||||
│ ├── api/
|
||||
│ │ ├── session-api.ts
|
||||
│ │ └── signaling.ts
|
||||
│ ├── webrtc/
|
||||
│ │ ├── transport.ts
|
||||
│ │ └── ice.ts
|
||||
│ ├── crypto/
|
||||
│ │ └── crypto-client.ts
|
||||
│ ├── transfer/
|
||||
│ │ ├── file-transfer.ts
|
||||
│ │ └── state.ts
|
||||
│ └── ui/
|
||||
│ ├── pages/
|
||||
│ │ ├── home-page.ts
|
||||
│ │ ├── send-page.ts
|
||||
│ │ ├── join-page.ts
|
||||
│ │ └── expired-page.ts
|
||||
│ └── components/
|
||||
│ ├── qr-display.ts
|
||||
│ ├── file-picker.ts
|
||||
│ ├── progress-bar.ts
|
||||
│ └── status-indicator.ts
|
||||
├── deploy/
|
||||
│ ├── docker-compose.yml
|
||||
│ ├── Caddyfile
|
||||
│ ├── coturn.conf
|
||||
│ └── filedrop.service
|
||||
└── README.md
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 部署拓扑
|
||||
|
||||
最小部署:
|
||||
- Caddy/Nginx
|
||||
- axum app
|
||||
- Redis
|
||||
- coturn
|
||||
|
||||
域名建议:
|
||||
- `app.example.com`:Web App + API + WS
|
||||
- `turn.example.com`:TURN 服务
|
||||
|
||||
---
|
||||
|
||||
## 风险与注意事项
|
||||
|
||||
1. Safari 对 WebRTC/DataChannel、后台标签页较敏感
|
||||
2. TURN 成本需监控,大文件应尽快切应用层密文中转
|
||||
3. 浏览器内存占用,大文件必须按块处理
|
||||
4. 断点续传复杂度不适合首版
|
||||
5. 端到端加密需覆盖文件名和文件列表
|
||||
Reference in New Issue
Block a user