file-transfer-go Agent Skills & AI Rules

GitHub

Go/React开发的端到端webrtc的文件传输/文字传输/桌面共享,安全,隐私,数据不经过服务器。

5,152 stars TypeScript TypeScript 1 file · ~977 tokens
RAW Rules
1 Discovered Rules • ~9,611 Estimated Tokens
Loading skill content...

System Architecture & Module Boundaries

ARCHITECTURE.md project-spec ~9,611 tokens
Raw
# 文件快传(Chuan)— 系统架构与技术设计文档

> 最后更新:2025-08-02

---

## 一、项目总览

**文件快传(Chuan)** 是一个基于 **WebRTC P2P** 的文件传输应用。核心设计理念:**所有实际数据(文件、文字、桌面画面)均通过 WebRTC 点对点直传,服务器仅承担信令中继和房间管理职责**。

### 技术栈

| 层级 | 技术选型 |
|------|---------|
| 后端 | Go 1.21 · chi/v5 路由 · gorilla/websocket |
| 前端 | Next.js 15 · React 19 · TypeScript · Tailwind CSS 4 |
| 状态管理 | Zustand 5 + React useState/useRef |
| UI 组件 | shadcn/ui (Radix UI) · Lucide Icons |
| P2P 通信 | WebRTC DataChannel(文件/文字)· MediaStream(桌面共享)|
| 部署 | 双模式:Next.js 开发模式 / Go 嵌入前端静态文件 |

### 核心特性

| 功能 | 实现方式 |
|------|---------|
| 文件传输 | WebRTC DataChannel · 256KB 分块 · CRC32 校验 · ACK 确认 |
| 文本消息 | WebRTC DataChannel · 实时双向同步 · 打字状态 |
| 桌面共享 | WebRTC MediaStream · getDisplayMedia · renegotiation |
| ICE 配置 | 默认 5 个 STUN · 支持自定义 STUN/TURN · localStorage 持久化 |

---

## 二、系统架构

### 2.1 整体架构图

```
┌──────────────────────────────────────────────────────────────────────┐
│                        Go 后端 (:8080)                               │
│  ┌──────────────┐  ┌───────────────────┐  ┌─────────────────────┐  │
│  │  REST API     │  │  WebSocket 信令    │  │  静态文件服务        │  │
│  │  /api/*       │  │  /api/ws/webrtc   │  │  go:embed frontend  │  │
│  │               │  │  /ws/webrtc       │  │                     │  │
│  │ - create-room │  │                   │  │  SPA 回退 index.html │  │
│  │ - room-info   │  │  纯中继 转发消息   │  │                     │  │
│  └──────────────┘  └───────────────────┘  └─────────────────────┘  │
│                              │                                       │
│              ┌───────────────┼───────────────┐                       │
│              │   内存房间管理  (sync.RWMutex)  │                       │
│              │   map[string]*WebRTCRoom       │                       │
│              │   1小时过期 · 5分钟定时清理     │                       │
│              └───────────────────────────────┘                       │
└──────────────────────────────────────────────────────────────────────┘
        │ WebSocket                                    │ WebSocket
        │ (信令: offer/answer/ice-candidate)            │
        ▼                                              ▼
┌───────────────┐          WebRTC P2P          ┌───────────────┐
│  发送方浏览器   │ ◄══════════════════════════► │  接收方浏览器   │
│               │   DataChannel (文件/文字)      │               │
│  Next.js App  │   MediaStream (桌面共享)       │  Next.js App  │
└───────────────┘                              └───────────────┘
```

### 2.2 数据流向

```
1. 控制平面 (HTTP):  浏览器 ──── REST API ────→ Go 后端    (房间创建/查询)
2. 信令平面 (WS):    浏览器 ←──── WebSocket ──→ Go 后端    (SDP/ICE 交换)
3. 数据平面 (P2P):   浏览器 ◄═══ DataChannel ══► 浏览器     (文件/文字直传)
4. 媒体平面 (P2P):   浏览器 ◄═══ MediaStream ══► 浏览器     (桌面画面直传)
```

**关键设计:数据平面完全绕过服务器,文件和文字内容不经服务器中转。**

---

## 三、后端设计

### 3.1 目录结构

```
cmd/
├── main.go          # 入口:参数解析 → 配置 → 路由 → 启动服务器
├── config.go        # 配置管理:命令行 > 环境变量 > .chuan.env > 默认值
├── router.go        # chi 路由注册 + 中间件 + 前端静态服务
└── server.go        # HTTP Server 封装 + 优雅关闭 (SIGINT/SIGTERM)

internal/
├── handlers/
│   └── handlers.go  # HTTP/WebSocket 请求处理(薄层,委托给 service)
├── models/
│   └── models.go    # 数据模型:WebRTCRoom、WebRTCClient、RoomStatus
├── services/
│   └── webrtc_service.go  # 核心信令服务:房间管理 + WebSocket 消息转发
└── web/
    └── frontend.go  # go:embed 嵌入前端 + SPA 回退
```

### 3.2 API 端点

| 方法 | 路径 | 功能 | 请求/响应 |
|------|------|------|-----------|
| `POST` | `/api/create-room` | 创建房间 | `{}` → `{success, code, message}` |
| `GET` | `/api/room-info?code=XXX` | 查询房间状态 | → `{success, status: RoomStatus}` |
| `GET` | `/api/webrtc-room-status?code=XXX` | 同上(别名)| 同上 |
| `WS` | `/api/ws/webrtc?code=&role=` | WebRTC 信令 | WebSocket 双向 |
| `WS` | `/ws/webrtc?code=&role=` | 同上(兼容路径)| 同上 |
| `GET` | `/*` | 前端静态文件 | SPA 回退 |

### 3.3 房间管理

- **房间代码**:6 位,字符集 `123456789ABCDEFGHIJKLMNPQRSTUVWXYZ`(排除 0 和 O,避免混淆)
- **房间容量**:最多 2 人(1 sender + 1 receiver)
- **过期策略**:创建后 1 小时过期,每 5 分钟后台清理
- **存储方式**:纯内存 `map[string]*WebRTCRoom` + `sync.RWMutex`,无数据库

### 3.4 WebSocket 信令协议

#### 连接 URL
```
ws[s]://host/api/ws/webrtc?code=ROOM_CODE&role=sender|receiver&channel=shared
```

#### 服务端 → 客户端

| type | payload | 触发时机 |
|------|---------|---------|
| `peer-joined` | `{ role }` | 对方加入房间 |
| `disconnection` | `{ role, message }` | 对方断开连接 |
| `error` | `{ message }` | 房间不存在 / 已满 / 参数无效 |

#### 客户端 ↔ 客户端(经服务端纯转发)

| type | payload | 说明 |
|------|---------|------|
| `offer` | `RTCSessionDescription` | SDP Offer |
| `answer` | `RTCSessionDescription` | SDP Answer |
| `ice-candidate` | `RTCIceCandidate` | ICE 候选地址 |

**服务端对 offer/answer/ice-candidate 消息不做任何解析,仅中继转发给房间内另一方。**

---

## 四、前端设计(重点)

### 4.1 目录结构

```
chuan-next/src/
├── app/                            # Next.js App Router
│   ├── page.tsx                    # 入口页(SSG)
│   ├── layout.tsx                  # 根布局(字体、Toast、Umami 统计)
│   ├── globals.css                 # 全局样式 + 动画定义
│   ├── HomePage.tsx                # 主页面组件(Tab 管理 + WebRTC 状态)
│   ├── HomePageWrapper.tsx         # Suspense 包裹(SSR 兼容)
│   └── api/                        # Next.js API Routes(开发模式代理)
│       ├── create-room/route.ts    # → GO_BACKEND_URL/api/create-room
│       ├── room-info/route.ts      # → GO_BACKEND_URL/api/room-info
│       ├── room-status/route.ts    # → GO_BACKEND_URL/api/room-status
│       ├── create-text-room/route.ts
│       ├── get-text-content/route.ts
│       └── update-files/route.ts
│
├── components/                     # UI 组件
│   ├── WebRTCFileTransfer.tsx      # 文件传输整合组件(659行)
│   ├── WebRTCTextImageTransfer.tsx # 文字+图片传输整合组件
│   ├── DesktopShare.tsx            # 桌面共享整合组件
│   ├── Hero.tsx                    # 页头(标题、GitHub 链接)
│   ├── webrtc/                     # WebRTC 功能子组件
│   │   ├── WebRTCFileUpload.tsx    # 文件发送 UI
│   │   ├── WebRTCFileReceive.tsx   # 文件接收 UI
│   │   ├── WebRTCTextSender.tsx    # 文字发送 UI
│   │   ├── WebRTCTextReceiver.tsx  # 文字接收 UI
│   │   ├── WebRTCDesktopSender.tsx # 桌面共享发送 UI
│   │   ├── WebRTCDesktopReceiver.tsx # 桌面共享接收 UI
│   │   └── WebRTCSettings.tsx      # ICE 服务器配置 UI
│   └── ui/                         # 基础 UI 组件 (shadcn/ui)
│       ├── button.tsx              # 按钮(6种 variant)
│       ├── tabs.tsx                # 标签页(Radix Tabs)
│       ├── input.tsx               # 输入框
│       ├── card.tsx                # 卡片
│       ├── dialog.tsx              # 对话框
│       ├── progress.tsx            # 进度条
│       ├── textarea.tsx            # 文本域
│       ├── toast.tsx               # Toast (Radix)
│       ├── toast-simple.tsx        # 轻量 Toast (自定义)
│       ├── toaster.tsx             # Toast 渲染
│       └── confirm-dialog.tsx      # 确认对话框
│
├── hooks/                          # 自定义 Hooks(核心逻辑层)
│   ├── index.ts                    # 统一导出
│   ├── connection/                 # WebRTC 连接管理
│   │   ├── useSharedWebRTCManager.ts      # 整合入口
│   │   ├── useWebRTCConnectionCore.ts     # 核心连接(WS + PeerConnection)
│   │   ├── useWebRTCDataChannelManager.ts # DataChannel 管理 + 消息路由
│   │   ├── useWebRTCTrackManager.ts       # MediaStream 轨道管理
│   │   ├── useWebRTCStateManager.ts       # Zustand store 封装
│   │   ├── useRoomConnection.ts           # 加入房间逻辑
│   │   ├── useConnectionState.ts          # 连接状态变化处理
│   │   └── useWebRTCSupport.ts            # 浏览器 WebRTC 检测
│   ├── file-transfer/              # 文件传输业务
│   │   ├── useFileTransferBusiness.ts     # 核心:分块传输 + CRC32 + ACK
│   │   ├── useFileListSync.ts             # 文件列表实时同步
│   │   └── useFileStateManager.ts         # 文件状态(选中/下载/进度)
│   ├── text-transfer/              # 文字传输业务
│   │   └── useTextTransferBusiness.ts     # 实时文字同步 + 打字状态
│   ├── desktop-share/              # 桌面共享业务
│   │   └── useDesktopShareBusiness.ts     # getDisplayMedia + 轨道管理
│   ├── settings/                   # 设置
│   │   ├── useIceServersConfig.ts         # ICE 服务器配置 + localStorage
│   │   └── useWebRTCConfigSync.ts         # 配置变更监听
│   └── ui/                         # UI 相关
│       ├── webRTCStore.ts                 # Zustand 全局状态
│       ├── useURLHandler.ts               # URL 参数管理
│       ├── useTabNavigation.ts            # Tab 切换管理
│       └── useConfirmDialog.ts            # 确认对话框 Hook
│
├── lib/                            # 工具库
│   ├── config.ts                   # 环境配置(API URL / WS URL 动态计算)
│   ├── utils.ts                    # cn() 样式合并
│   ├── client-api.ts               # ClientAPI 封装
│   ├── api-utils.ts                # apiFetch() 统一请求
│   └── webrtc-support.ts           # WebRTC 支持检测
│
└── types/
    └── index.ts                    # 全局类型定义
```

### 4.2 组件层次结构

```
RootLayout (layout.tsx)
  └─ ToastProvider (toast-simple.tsx)
       └─ HomePageWrapper (Suspense)
            └─ HomePage
                 ├─ Hero                           # 页头
                 ├─ WebRTCUnsupportedModal          # WebRTC 不支持提示
                 ├─ ConfirmDialog                   # Tab 切换确认
                 └─ Tabs (5个标签)
                      │
                      ├─ [Tab: webrtc] WebRTCFileTransfer
                      │    ├─ 模式切换 (发送/接收)
                      │    ├─ [发送模式] WebRTCFileUpload
                      │    │    ├─ 拖拽上传区域 / 文件列表
                      │    │    ├─ RoomInfoDisplay (取件码 + 二维码 + 复制)
                      │    │    ├─ ConnectionStatus (连接状态)
                      │    │    └─ 传输进度条
                      │    └─ [接收模式] WebRTCFileReceive
                      │         ├─ 取件码输入 (6位)
                      │         ├─ ConnectionStatus (连接状态)
                      │         └─ 文件列表 + 下载按钮
                      │
                      ├─ [Tab: message] WebRTCTextImageTransfer
                      │    ├─ 模式切换 (发送/接收)
                      │    ├─ [发送模式] WebRTCTextSender
                      │    │    ├─ Textarea 编辑器
                      │    │    ├─ 图片发送按钮
                      │    │    └─ RoomInfoDisplay
                      │    └─ [接收模式] WebRTCTextReceiver
                      │         ├─ 取件码输入
                      │         └─ 实时文字显示 + 图片接收
                      │
                      ├─ [Tab: desktop] DesktopShare
                      │    ├─ 模式切换 (共享/查看)
                      │    ├─ [共享模式] WebRTCDesktopSender
                      │    │    └─ 选择屏幕 → 开始共享
                      │    └─ [查看模式] WebRTCDesktopReceiver
                      │         └─ DesktopViewer (video 标签)
                      │
                      ├─ [Tab: wechat] WeChatGroup
                      │    └─ 微信群二维码
                      │
                      └─ [Tab: settings] WebRTCSettings
                           └─ ICE 服务器配置表单
```

### 4.3 Hooks 架构(核心设计)

前端逻辑层采用 **分层 Hooks 架构**,自底向上组合:

```
                    ┌─────────────────────────────┐
                    │    Zustand Store             │  ← 最底层:全局状态
                    │    (webRTCStore.ts)           │
                    │    isConnected, isConnecting  │
                    │    currentRoom, error         │
                    └──────────────┬──────────────┘
                                   │
            ┌──────────────────────┼──────────────────────┐
            │                      │                      │
   ┌────────▼────────┐  ┌─────────▼──────────┐  ┌───────▼─────────┐
   │ StateManager     │  │ DataChannelManager  │  │ TrackManager     │
   │ (Zustand 封装)    │  │ (消息路由 + 收发)    │  │ (媒体轨道管理)    │
   └────────┬────────┘  └─────────┬──────────┘  └───────┬─────────┘
            │                      │                      │
            └──────────────────────┼──────────────────────┘
                                   │
                    ┌──────────────▼──────────────┐
                    │ ConnectionCore               │  ← WebSocket + PeerConnection
                    │ (useWebRTCConnectionCore)     │     信令处理 + ICE 交换
                    └──────────────┬──────────────┘
                                   │
                    ┌──────────────▼──────────────┐
                    │ SharedWebRTCManager          │  ← 整合入口(4合1)
                    │ (useSharedWebRTCManager)      │     返回统一 WebRTCConnection
                    └──────────────┬──────────────┘
                                   │
          ┌────────────────────────┼────────────────────────┐
          │                        │                        │
 ┌────────▼────────┐   ┌──────────▼──────────┐   ┌────────▼────────┐
 │ FileTransfer     │   │ TextTransfer         │   │ DesktopShare     │
 │ Business         │   │ Business             │   │ Business         │
 │ (文件分块+校验)    │   │ (实时同步文字)         │   │ (屏幕采集+推流)    │
 └────────┬────────┘   └──────────┬──────────┘   └────────┬────────┘
          │                        │                        │
          └────────────────────────┼────────────────────────┘
                                   │
                    ┌──────────────▼──────────────┐
                    │ 业务级组件 (TSX)              │
                    │ WebRTCFileTransfer.tsx 等     │
                    └─────────────────────────────┘
```

#### 4.3.1 连接层 — `useWebRTCConnectionCore`

**职责**:管理 WebSocket 信令连接 + RTCPeerConnection 生命周期。

```
connect(code, role)
    │
    ├─ 1. WebSocket 连接到 ws://host/api/ws/webrtc?code=XXX&role=YYY
    │
    ├─ 2. 收到 "peer-joined" → 创建 RTCPeerConnection
    │      └─ ICE 服务器从 getIceServersConfig() 获取
    │
    ├─ 3. Sender 创建 DataChannel → 创建 Offer
    │      Receiver 等待 ondatachannel
    │
    ├─ 4. offer/answer/ice-candidate 通过 WebSocket 交换
    │
    ├─ 5. ICE 连接建立 → DataChannel open → 连接完成
    │
    └─ 6. 断开时:发送 disconnection → 清理 PeerConnection
```

#### 4.3.2 数据通道层 — `useWebRTCDataChannelManager`

**职责**:管理 DataChannel 消息的路由分发。

**核心设计:多 channel 路由**

所有 JSON 消息通过 `channel` 字段路由到不同的业务处理器:

```typescript
// 消息格式
{
  channel: "file-transfer" | "text-transfer" | "desktop-share",
  type: "具体消息类型",
  payload: { ... }
}
```

```
DataChannel.onmessage
    │
    ├─ typeof data === 'string' → JSON.parse
    │   ├─ channel === 'file-transfer' → fileTransferHandler
    │   ├─ channel === 'text-transfer' → textTransferHandler
    │   └─ channel === 'desktop-share' → desktopShareHandler
    │
    └─ typeof data === ArrayBuffer → 二进制数据
        └─ 优先路由到 'file-transfer' handler(文件块数据)
```

**注意**:三个功能(文件/文字/桌面)共享同一个 DataChannel(`shared-channel`),而非各自创建独立通道。

#### 4.3.3 轨道管理层 — `useWebRTCTrackManager`

**职责**:管理 MediaStream 轨道(仅桌面共享使用)。

- `addTrack(track, stream)` — 添加视频/音频轨道
- `removeTrack(sender)` — 移除轨道
- `createOfferNow()` — 添加轨道后触发重协商
- `onTrack` 回调 — 接收远程媒体流

### 4.4 状态管理设计

采用 **三层状态模型**:

| 层级 | 技术 | 用途 | 示例 |
|------|------|------|------|
| 全局共享状态 | Zustand | WebRTC 连接状态 | `isConnected`, `currentRoom`, `error` |
| 组件级状态 | React useState | UI 交互状态 | `selectedFiles`, `mode`, `progress` |
| 引用状态 | React useRef | 回调/缓冲区/定时器 | `receiveBufferRef`, `messageHandlers`, 防抖 timer |

#### Zustand Store 结构

```typescript
interface WebRTCState {
  // 连接状态
  isConnected: boolean;       // 总体连接状态
  isConnecting: boolean;      // 正在连接中
  isWebSocketConnected: boolean;  // WebSocket 层连接
  isPeerConnected: boolean;   // PeerConnection 层连接

  // 错误状态
  error: string | null;
  canRetry: boolean;

  // 房间信息
  currentRoom: { code: string; role: 'sender' | 'receiver' } | null;

  // Actions
  updateState(partial: Partial<WebRTCState>): void;
  setCurrentRoom(room: { code: string; role: string } | null): void;
  resetToInitial(): void;
}
```

### 4.5 文件传输详细设计

#### 4.5.1 DataChannel 消息协议

**file-transfer channel 消息类型:**

| 方向 | type | payload | 说明 |
|------|------|---------|------|
| S→R | `file-list` | `FileInfo[]` | 发送方文件列表(实时同步) |
| R→S | `file-request` | `{ fileId, fileName }` | 接收方请求下载 |
| S→R | `file-metadata` | `{ id, name, size, type }` | 文件元信息 |
| S→R | `file-chunk-info` | `{ fileId, chunkIndex, totalChunks, checksum }` | 块元信息(含 CRC32) |
| S→R | *(二进制)* | `ArrayBuffer` (≤256KB) | 文件块数据 |
| R→S | `file-chunk-ack` | `{ fileId, chunkIndex, success, checksum }` | 块确认(含校验结果) |
| S→R | `file-complete` | `{ fileId }` | 文件传输完成 |

#### 4.5.2 传输参数

| 参数 | 值 | 说明 |
|------|-----|------|
| 块大小 | 256 KB | 每个 chunk 的最大字节数 |
| 最大重试 | 5 次 | 单个 chunk 校验失败后的重试上限 |
| 退避策略 | 指数退避 | 重试间隔指数增长 |
| 校验算法 | CRC32 | 每个 chunk 独立校验 |
| 流控 | 自适应 | 根据平均传输速度动态调整发送间隔 |
| DataChannel 配置 | `ordered: true, maxRetransmits: 3` | 有序可靠传输 |

#### 4.5.3 完整传输时序

```
发送方                              信令服务器                           接收方
  │                                     │                                 │
  │  1. POST /api/create-room           │                                 │
  │ ──────────────────────────────────► │                                 │
  │ ◄── {success: true, code: "AB12CD"} │                                 │
  │                                     │                                 │
  │  2. WS connect (role=sender)        │                                 │
  │ ═══════════════════════════════════► │                                 │
  │                                     │                                 │
  │                                     │  3. GET /api/room-info?code=... │
  │                                     │ ◄────────────────────────────── │
  │                                     │ ── {success, status} ─────────► │
  │                                     │                                 │
  │                                     │  4. WS connect (role=receiver)  │
  │                                     │ ◄═══════════════════════════════ │
  │                                     │                                 │
  │  5. ◄── peer-joined ──             │  ── peer-joined ──►             │
  │                                     │                                 │
  │  6. 创建 PeerConnection + DataChannel                                 │
  │  7. 创建 SDP Offer                                                    │
  │  ═══ offer ═══════════════════════► │ ═══ offer ═══════════════════► │
  │                                     │                                 │
  │                                     │  8. 创建 PeerConnection         │
  │                                     │  9. 设置 Remote/Local SDP       │
  │  ◄═══ answer ══════════════════════ │ ◄═══ answer ═════════════════ │
  │                                     │                                 │
  │  10. ICE Candidate 交换 ◄═══════════╋══════════════════════════════► │
  │                                     │                                 │
  │  ══════════ DataChannel OPEN ═══════╪══════════════════════════════ │
  │                                     │                                 │
  │  11. file-list ─────────────────────┼────────────────────────────────► │
  │      [{id, name, size, type}, ...]  │                                 │
  │                                     │                                 │
  │  ◄─────────────────────────────────┼──── 12. file-request ────────── │
  │      {fileId, fileName}             │                                 │
  │                                     │                                 │
  │  13. file-metadata ────────────────┼────────────────────────────────► │
  │      {id, name, size, type}         │                                 │
  │                                     │                                 │
  │  ╔══ 循环每个 chunk ═══════════════╪════════════════════════════════╗ │
  │  ║  14. file-chunk-info ───────────┼──────────────────────────────► ║ │
  │  ║      {fileId, chunkIndex,        │                              ║ │
  │  ║       totalChunks, checksum}     │                              ║ │
  │  ║                                  │                              ║ │
  │  ║  15. [ArrayBuffer 256KB] ───────┼──────────────────────────────► ║ │
  │  ║                                  │                              ║ │
  │  ║  ◄───────────────────────────────┼── 16. file-chunk-ack ─────── ║ │
  │  ║      {fileId, chunkIndex,        │      success, checksum}      ║ │
  │  ║                                  │                              ║ │
  │  ║  [如果 success=false, 指数退避重试,最多5次]                      ║ │
  │  ╚══════════════════════════════════╪══════════════════════════════╝ │
  │                                     │                                 │
  │  17. file-complete ────────────────┼────────────────────────────────► │
  │      {fileId}                       │                                 │
  │                                     │                   18. 组装 Blob  │
  │                                     │                       → File   │
  │                                     │                       → 下载    │
```

#### 4.5.4 文件列表同步机制

发送方选择文件后,文件列表会 **实时同步** 到接收方:

```
发送方文件变更 → useFileListSync hook
    │
    ├─ 150ms 防抖(debounce)
    │
    ├─ 比对新旧文件列表(避免无变更的冗余同步)
    │
    └─ 通过 DataChannel 发送 file-list 消息
        │
        └─ 接收方更新 UI 展示文件列表
```

### 4.6 文字传输详细设计

#### 4.6.1 DataChannel 消息协议

**text-transfer channel 消息类型:**

| 方向 | type | payload | 说明 |
|------|------|---------|------|
| S→R | `text-sync` | `{ text: string }` | 实时文本内容同步 |
| 双向 | `text-typing` | `{ typing: boolean }` | 打字状态指示 |

#### 4.6.2 实时同步机制

```
发送方 Textarea 输入
    │
    ├─ onChange → handleTextInputChange()
    │
    ├─ 调用 textTransfer.sendTextSync(text)
    │   └─ DataChannel 发送 { channel: "text-transfer", type: "text-sync", payload: { text } }
    │
    ├─ 同时发送 text-typing: { typing: true }
    │   └─ 1秒后自动发送 text-typing: { typing: false }
    │
    └─ 接收方
        ├─ onTextSync 回调 → 更新显示文本
        └─ onTyping 回调 → 显示 "对方正在输入..." 动画
```

**图片传输**:复用文件传输的 `file-transfer` channel,图片作为文件发送。

### 4.7 桌面共享详细设计

```
发送方
    │
    ├─ navigator.mediaDevices.getDisplayMedia({ video: true, audio: true })
    │
    ├─ 获取 MediaStream → 提取 video/audio Track
    │
    ├─ trackManager.addTrack(track, stream)
    │   └─ peerConnection.addTrack()
    │
    ├─ trackManager.createOfferNow()  ← 触发 SDP 重协商
    │   └─ 新 Offer → WebSocket → 对方 → Answer → WebSocket → 本方
    │
    └─ 接收方
        ├─ ontrack 事件 → 获取远程 MediaStream
        └─ <video> 元素播放 → DesktopViewer 组件
```

### 4.8 ICE 配置设计

#### 默认 STUN 服务器

```typescript
const DEFAULT_ICE_SERVERS = [
  { urls: 'stun:stun.easyvoip.com:3478' },
  { urls: 'stun:stun.miwifi.com:3478' },
  { urls: 'stun:stun.l.google.com:19302' },
  { urls: 'stun:stun1.l.google.com:19302' },
  { urls: 'stun:global.stun.twilio.com:3478' },
];
```

#### 用户自定义

- 通过 WebRTCSettings 界面添加 STUN/TURN 服务器
- 配置持久化到 `localStorage`(key: `webrtc-ice-config`)
- `getIceServersConfig()` 导出给非 React 代码使用
- 配置变更后提示用户断开重连

### 4.9 环境适配设计

```
                    ┌─────────────────────────────────────────┐
                    │           config.ts 环境判断              │
                    │                                         │
                    │  isStaticMode = Next.js 构建输出判断      │
                    │  isDev = NODE_ENV === 'development'      │
                    └────────────────┬────────────────────────┘
                                     │
                   ┌─────────────────┼─────────────────┐
                   │                 │                 │
          ┌────────▼────────┐ ┌─────▼──────┐ ┌───────▼───────┐
          │ 开发模式          │ │ SSG 静态模式 │ │ 嵌入 Go 模式   │
          │ (yarn dev)       │ │ (build:ssg) │ │ (go run)      │
          │                  │ │             │ │               │
          │ API:             │ │ API:        │ │ API:          │
          │ /api/* → Next.js │ │ 直连 Go     │ │ 同源 /api/*   │
          │ API Route 代理   │ │ 后端地址     │ │               │
          │                  │ │             │ │               │
          │ WS:              │ │ WS:         │ │ WS:           │
          │ ws://localhost   │ │ 当前域名     │ │ 当前域名       │
          │ :8080/...        │ │ ws[s]://    │ │ ws[s]://      │
          └──────────────────┘ └─────────────┘ └───────────────┘
```

#### URL 动态计算逻辑

```typescript
// API URL
getApiUrl() → 
  开发模式 ? '/api'  (通过 Next.js API Route 代理到 GO_BACKEND_URL)
  静态模式 ? getDirectBackendUrl() + '/api'  (直连 Go 后端)

// WebSocket URL
getWsUrl() →
  开发模式 ? 'ws://localhost:8080'  (直连 Go 后端 WS)
  静态模式 ? 'ws[s]://' + window.location.host  (同源)
```

### 4.10 URL 路由设计

前端通过 URL 参数控制功能和模式:

```
/?type=webrtc&mode=send            → 文件传输-发送模式
/?type=webrtc&mode=receive         → 文件传输-接收模式
/?type=webrtc&mode=receive&code=ABC123  → 自动填入取件码
/?type=message&mode=send           → 文字消息-发送模式
/?type=desktop&mode=send           → 桌面共享-共享模式
```

**URL 参数映射**:
- `type`: `webrtc` | `message` | `desktop` | `wechat` | `settings` → 对应 Tab
- `mode`: `send` | `receive` → 发送/接收子模式
- `code`: 6 位取件码 → 自动填入并可自动加入房间

---

## 五、关键设计决策

### 5.1 为什么用纯 P2P?

- **隐私**:文件内容不经服务器,用户数据零留存
- **成本**:服务器仅做信令,带宽成本极低
- **性能**:局域网环境下可接近网卡极限速度

### 5.2 为什么共享单个 DataChannel?

三个功能(文件/文字/桌面控制)共享同一个 `shared-channel`,通过 JSON `channel` 字段路由:

- **简化连接管理**:只需建立一次 P2P 连接
- **减少信令开销**:无需为每个功能单独协商
- **统一状态管理**:连接/断开状态全局一致

### 5.3 为什么用 CRC32 + ACK?

WebRTC DataChannel 本身基于 SCTP(配置为 `ordered: true, maxRetransmits: 3`),已有一定可靠性。额外的 CRC32 + ACK 是 **应用层二次保障**:

- 防止极端情况下 SCTP 重传仍失败后的数据损坏
- 提供块级粒度的错误恢复(只重传失败的块,无需从头开始)
- 接收方独立验证数据完整性

### 5.4 为什么 256KB 块大小?

- WebRTC DataChannel 最大消息大小约 256KB(不同浏览器有差异)
- 小块有利于进度反馈的精度
- 小块降低单次重传的代价

### 5.5 为什么关闭 React Strict Mode?

```typescript
// next.config.ts
reactStrictMode: false
```

React 18+ Strict Mode 会在开发环境 **双重调用** effect,导致:
- WebSocket 连接被创建两次
- PeerConnection 生命周期混乱
- DataChannel 状态不一致

---

## 六、部署模式

### 模式一:开发模式

```bash
# 终端 1:Go 后端
go run cmd/main.go          # :8080

# 终端 2:Next.js 前端
cd chuan-next && yarn dev   # :3000 (turbopack)
```

前端 API 调用链:`浏览器 → localhost:3000/api/* → Next.js API Route → localhost:8080/api/*`

### 模式二:生产模式(Go 嵌入前端)

```bash
cd chuan-next && yarn build:ssg    # 输出到 out/
cp -r out/ ../internal/web/frontend/  # 复制到 Go embed 目录
go build -o chuan cmd/*.go         # 编译
./chuan                            # :8080 同时提供 API + 前端
```

所有请求都由 Go 单进程处理,前端静态文件通过 `go:embed` 嵌入二进制。

---

## 七、安全与限制

| 维度 | 现状 |
|------|------|
| 加密 | WebRTC 自带 DTLS 加密,P2P 数据全程加密传输 |
| 认证 | 无用户认证,仅靠 6 位取件码 |
| 房间安全 | 取件码空间 34^6 ≈ 15 亿,暴力破解概率低 |
| 数据留存 | 服务器零数据留存,所有文件仅在浏览器内存/临时存储 |
| 并发限制 | 每房间最多 2 人,单进程内存管理 |
| 文件大小 | 受限于浏览器内存(大文件需接收方有足够内存组装 Blob)|
| NAT 穿透 | 依赖 STUN 服务器,对称 NAT 需 TURN 服务器(默认未配置)|