{"owner":"MatrixSeven","repo":"file-transfer-go","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":9611,"categories":["project-spec"],"hasMcp":false,"mcpConfig":null,"found":["ARCHITECTURE.md"],"skills":{"ARCHITECTURE.md":"# 文件快传（Chuan）— 系统架构与技术设计文档\n\n> 最后更新：2025-08-02\n\n---\n\n## 一、项目总览\n\n**文件快传（Chuan）** 是一个基于 **WebRTC P2P** 的文件传输应用。核心设计理念：**所有实际数据（文件、文字、桌面画面）均通过 WebRTC 点对点直传，服务器仅承担信令中继和房间管理职责**。\n\n### 技术栈\n\n| 层级 | 技术选型 |\n|------|---------|\n| 后端 | Go 1.21 · chi/v5 路由 · gorilla/websocket |\n| 前端 | Next.js 15 · React 19 · TypeScript · Tailwind CSS 4 |\n| 状态管理 | Zustand 5 + React useState/useRef |\n| UI 组件 | shadcn/ui (Radix UI) · Lucide Icons |\n| P2P 通信 | WebRTC DataChannel（文件/文字）· MediaStream（桌面共享）|\n| 部署 | 双模式：Next.js 开发模式 / Go 嵌入前端静态文件 |\n\n### 核心特性\n\n| 功能 | 实现方式 |\n|------|---------|\n| 文件传输 | WebRTC DataChannel · 256KB 分块 · CRC32 校验 · ACK 确认 |\n| 文本消息 | WebRTC DataChannel · 实时双向同步 · 打字状态 |\n| 桌面共享 | WebRTC MediaStream · getDisplayMedia · renegotiation |\n| ICE 配置 | 默认 5 个 STUN · 支持自定义 STUN/TURN · localStorage 持久化 |\n\n---\n\n## 二、系统架构\n\n### 2.1 整体架构图\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│                        Go 后端 (:8080)                               │\n│  ┌──────────────┐  ┌───────────────────┐  ┌─────────────────────┐  │\n│  │  REST API     │  │  WebSocket 信令    │  │  静态文件服务        │  │\n│  │  /api/*       │  │  /api/ws/webrtc   │  │  go:embed frontend  │  │\n│  │               │  │  /ws/webrtc       │  │                     │  │\n│  │ - create-room │  │                   │  │  SPA 回退 index.html │  │\n│  │ - room-info   │  │  纯中继 转发消息   │  │                     │  │\n│  └──────────────┘  └───────────────────┘  └─────────────────────┘  │\n│                              │                                       │\n│              ┌───────────────┼───────────────┐                       │\n│              │   内存房间管理  (sync.RWMutex)  │                       │\n│              │   map[string]*WebRTCRoom       │                       │\n│              │   1小时过期 · 5分钟定时清理     │                       │\n│              └───────────────────────────────┘                       │\n└──────────────────────────────────────────────────────────────────────┘\n        │ WebSocket                                    │ WebSocket\n        │ (信令: offer/answer/ice-candidate)            │\n        ▼                                              ▼\n┌───────────────┐          WebRTC P2P          ┌───────────────┐\n│  发送方浏览器   │ ◄══════════════════════════► │  接收方浏览器   │\n│               │   DataChannel (文件/文字)      │               │\n│  Next.js App  │   MediaStream (桌面共享)       │  Next.js App  │\n└───────────────┘                              └───────────────┘\n```\n\n### 2.2 数据流向\n\n```\n1. 控制平面 (HTTP):  浏览器 ──── REST API ────→ Go 后端    (房间创建/查询)\n2. 信令平面 (WS):    浏览器 ←──── WebSocket ──→ Go 后端    (SDP/ICE 交换)\n3. 数据平面 (P2P):   浏览器 ◄═══ DataChannel ══► 浏览器     (文件/文字直传)\n4. 媒体平面 (P2P):   浏览器 ◄═══ MediaStream ══► 浏览器     (桌面画面直传)\n```\n\n**关键设计：数据平面完全绕过服务器，文件和文字内容不经服务器中转。**\n\n---\n\n## 三、后端设计\n\n### 3.1 目录结构\n\n```\ncmd/\n├── main.go          # 入口：参数解析 → 配置 → 路由 → 启动服务器\n├── config.go        # 配置管理：命令行 > 环境变量 > .chuan.env > 默认值\n├── router.go        # chi 路由注册 + 中间件 + 前端静态服务\n└── server.go        # HTTP Server 封装 + 优雅关闭 (SIGINT/SIGTERM)\n\ninternal/\n├── handlers/\n│   └── handlers.go  # HTTP/WebSocket 请求处理（薄层，委托给 service）\n├── models/\n│   └── models.go    # 数据模型：WebRTCRoom、WebRTCClient、RoomStatus\n├── services/\n│   └── webrtc_service.go  # 核心信令服务：房间管理 + WebSocket 消息转发\n└── web/\n    └── frontend.go  # go:embed 嵌入前端 + SPA 回退\n```\n\n### 3.2 API 端点\n\n| 方法 | 路径 | 功能 | 请求/响应 |\n|------|------|------|-----------|\n| `POST` | `/api/create-room` | 创建房间 | `{}` → `{success, code, message}` |\n| `GET` | `/api/room-info?code=XXX` | 查询房间状态 | → `{success, status: RoomStatus}` |\n| `GET` | `/api/webrtc-room-status?code=XXX` | 同上（别名）| 同上 |\n| `WS` | `/api/ws/webrtc?code=&role=` | WebRTC 信令 | WebSocket 双向 |\n| `WS` | `/ws/webrtc?code=&role=` | 同上（兼容路径）| 同上 |\n| `GET` | `/*` | 前端静态文件 | SPA 回退 |\n\n### 3.3 房间管理\n\n- **房间代码**：6 位，字符集 `123456789ABCDEFGHIJKLMNPQRSTUVWXYZ`（排除 0 和 O，避免混淆）\n- **房间容量**：最多 2 人（1 sender + 1 receiver）\n- **过期策略**：创建后 1 小时过期，每 5 分钟后台清理\n- **存储方式**：纯内存 `map[string]*WebRTCRoom` + `sync.RWMutex`，无数据库\n\n### 3.4 WebSocket 信令协议\n\n#### 连接 URL\n```\nws[s]://host/api/ws/webrtc?code=ROOM_CODE&role=sender|receiver&channel=shared\n```\n\n#### 服务端 → 客户端\n\n| type | payload | 触发时机 |\n|------|---------|---------|\n| `peer-joined` | `{ role }` | 对方加入房间 |\n| `disconnection` | `{ role, message }` | 对方断开连接 |\n| `error` | `{ message }` | 房间不存在 / 已满 / 参数无效 |\n\n#### 客户端 ↔ 客户端（经服务端纯转发）\n\n| type | payload | 说明 |\n|------|---------|------|\n| `offer` | `RTCSessionDescription` | SDP Offer |\n| `answer` | `RTCSessionDescription` | SDP Answer |\n| `ice-candidate` | `RTCIceCandidate` | ICE 候选地址 |\n\n**服务端对 offer/answer/ice-candidate 消息不做任何解析，仅中继转发给房间内另一方。**\n\n---\n\n## 四、前端设计（重点）\n\n### 4.1 目录结构\n\n```\nchuan-next/src/\n├── app/                            # Next.js App Router\n│   ├── page.tsx                    # 入口页（SSG）\n│   ├── layout.tsx                  # 根布局（字体、Toast、Umami 统计）\n│   ├── globals.css                 # 全局样式 + 动画定义\n│   ├── HomePage.tsx                # 主页面组件（Tab 管理 + WebRTC 状态）\n│   ├── HomePageWrapper.tsx         # Suspense 包裹（SSR 兼容）\n│   └── api/                        # Next.js API Routes（开发模式代理）\n│       ├── create-room/route.ts    # → GO_BACKEND_URL/api/create-room\n│       ├── room-info/route.ts      # → GO_BACKEND_URL/api/room-info\n│       ├── room-status/route.ts    # → GO_BACKEND_URL/api/room-status\n│       ├── create-text-room/route.ts\n│       ├── get-text-content/route.ts\n│       └── update-files/route.ts\n│\n├── components/                     # UI 组件\n│   ├── WebRTCFileTransfer.tsx      # 文件传输整合组件（659行）\n│   ├── WebRTCTextImageTransfer.tsx # 文字+图片传输整合组件\n│   ├── DesktopShare.tsx            # 桌面共享整合组件\n│   ├── Hero.tsx                    # 页头（标题、GitHub 链接）\n│   ├── webrtc/                     # WebRTC 功能子组件\n│   │   ├── WebRTCFileUpload.tsx    # 文件发送 UI\n│   │   ├── WebRTCFileReceive.tsx   # 文件接收 UI\n│   │   ├── WebRTCTextSender.tsx    # 文字发送 UI\n│   │   ├── WebRTCTextReceiver.tsx  # 文字接收 UI\n│   │   ├── WebRTCDesktopSender.tsx # 桌面共享发送 UI\n│   │   ├── WebRTCDesktopReceiver.tsx # 桌面共享接收 UI\n│   │   └── WebRTCSettings.tsx      # ICE 服务器配置 UI\n│   └── ui/                         # 基础 UI 组件 (shadcn/ui)\n│       ├── button.tsx              # 按钮（6种 variant）\n│       ├── tabs.tsx                # 标签页（Radix Tabs）\n│       ├── input.tsx               # 输入框\n│       ├── card.tsx                # 卡片\n│       ├── dialog.tsx              # 对话框\n│       ├── progress.tsx            # 进度条\n│       ├── textarea.tsx            # 文本域\n│       ├── toast.tsx               # Toast (Radix)\n│       ├── toast-simple.tsx        # 轻量 Toast (自定义)\n│       ├── toaster.tsx             # Toast 渲染\n│       └── confirm-dialog.tsx      # 确认对话框\n│\n├── hooks/                          # 自定义 Hooks（核心逻辑层）\n│   ├── index.ts                    # 统一导出\n│   ├── connection/                 # WebRTC 连接管理\n│   │   ├── useSharedWebRTCManager.ts      # 整合入口\n│   │   ├── useWebRTCConnectionCore.ts     # 核心连接（WS + PeerConnection）\n│   │   ├── useWebRTCDataChannelManager.ts # DataChannel 管理 + 消息路由\n│   │   ├── useWebRTCTrackManager.ts       # MediaStream 轨道管理\n│   │   ├── useWebRTCStateManager.ts       # Zustand store 封装\n│   │   ├── useRoomConnection.ts           # 加入房间逻辑\n│   │   ├── useConnectionState.ts          # 连接状态变化处理\n│   │   └── useWebRTCSupport.ts            # 浏览器 WebRTC 检测\n│   ├── file-transfer/              # 文件传输业务\n│   │   ├── useFileTransferBusiness.ts     # 核心：分块传输 + CRC32 + ACK\n│   │   ├── useFileListSync.ts             # 文件列表实时同步\n│   │   └── useFileStateManager.ts         # 文件状态（选中/下载/进度）\n│   ├── text-transfer/              # 文字传输业务\n│   │   └── useTextTransferBusiness.ts     # 实时文字同步 + 打字状态\n│   ├── desktop-share/              # 桌面共享业务\n│   │   └── useDesktopShareBusiness.ts     # getDisplayMedia + 轨道管理\n│   ├── settings/                   # 设置\n│   │   ├── useIceServersConfig.ts         # ICE 服务器配置 + localStorage\n│   │   └── useWebRTCConfigSync.ts         # 配置变更监听\n│   └── ui/                         # UI 相关\n│       ├── webRTCStore.ts                 # Zustand 全局状态\n│       ├── useURLHandler.ts               # URL 参数管理\n│       ├── useTabNavigation.ts            # Tab 切换管理\n│       └── useConfirmDialog.ts            # 确认对话框 Hook\n│\n├── lib/                            # 工具库\n│   ├── config.ts                   # 环境配置（API URL / WS URL 动态计算）\n│   ├── utils.ts                    # cn() 样式合并\n│   ├── client-api.ts               # ClientAPI 封装\n│   ├── api-utils.ts                # apiFetch() 统一请求\n│   └── webrtc-support.ts           # WebRTC 支持检测\n│\n└── types/\n    └── index.ts                    # 全局类型定义\n```\n\n### 4.2 组件层次结构\n\n```\nRootLayout (layout.tsx)\n  └─ ToastProvider (toast-simple.tsx)\n       └─ HomePageWrapper (Suspense)\n            └─ HomePage\n                 ├─ Hero                           # 页头\n                 ├─ WebRTCUnsupportedModal          # WebRTC 不支持提示\n                 ├─ ConfirmDialog                   # Tab 切换确认\n                 └─ Tabs (5个标签)\n                      │\n                      ├─ [Tab: webrtc] WebRTCFileTransfer\n                      │    ├─ 模式切换 (发送/接收)\n                      │    ├─ [发送模式] WebRTCFileUpload\n                      │    │    ├─ 拖拽上传区域 / 文件列表\n                      │    │    ├─ RoomInfoDisplay (取件码 + 二维码 + 复制)\n                      │    │    ├─ ConnectionStatus (连接状态)\n                      │    │    └─ 传输进度条\n                      │    └─ [接收模式] WebRTCFileReceive\n                      │         ├─ 取件码输入 (6位)\n                      │         ├─ ConnectionStatus (连接状态)\n                      │         └─ 文件列表 + 下载按钮\n                      │\n                      ├─ [Tab: message] WebRTCTextImageTransfer\n                      │    ├─ 模式切换 (发送/接收)\n                      │    ├─ [发送模式] WebRTCTextSender\n                      │    │    ├─ Textarea 编辑器\n                      │    │    ├─ 图片发送按钮\n                      │    │    └─ RoomInfoDisplay\n                      │    └─ [接收模式] WebRTCTextReceiver\n                      │         ├─ 取件码输入\n                      │         └─ 实时文字显示 + 图片接收\n                      │\n                      ├─ [Tab: desktop] DesktopShare\n                      │    ├─ 模式切换 (共享/查看)\n                      │    ├─ [共享模式] WebRTCDesktopSender\n                      │    │    └─ 选择屏幕 → 开始共享\n                      │    └─ [查看模式] WebRTCDesktopReceiver\n                      │         └─ DesktopViewer (video 标签)\n                      │\n                      ├─ [Tab: wechat] WeChatGroup\n                      │    └─ 微信群二维码\n                      │\n                      └─ [Tab: settings] WebRTCSettings\n                           └─ ICE 服务器配置表单\n```\n\n### 4.3 Hooks 架构（核心设计）\n\n前端逻辑层采用 **分层 Hooks 架构**，自底向上组合：\n\n```\n                    ┌─────────────────────────────┐\n                    │    Zustand Store             │  ← 最底层：全局状态\n                    │    (webRTCStore.ts)           │\n                    │    isConnected, isConnecting  │\n                    │    currentRoom, error         │\n                    └──────────────┬──────────────┘\n                                   │\n            ┌──────────────────────┼──────────────────────┐\n            │                      │                      │\n   ┌────────▼────────┐  ┌─────────▼──────────┐  ┌───────▼─────────┐\n   │ StateManager     │  │ DataChannelManager  │  │ TrackManager     │\n   │ (Zustand 封装)    │  │ (消息路由 + 收发)    │  │ (媒体轨道管理)    │\n   └────────┬────────┘  └─────────┬──────────┘  └───────┬─────────┘\n            │                      │                      │\n            └──────────────────────┼──────────────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ ConnectionCore               │  ← WebSocket + PeerConnection\n                    │ (useWebRTCConnectionCore)     │     信令处理 + ICE 交换\n                    └──────────────┬──────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ SharedWebRTCManager          │  ← 整合入口（4合1）\n                    │ (useSharedWebRTCManager)      │     返回统一 WebRTCConnection\n                    └──────────────┬──────────────┘\n                                   │\n          ┌────────────────────────┼────────────────────────┐\n          │                        │                        │\n ┌────────▼────────┐   ┌──────────▼──────────┐   ┌────────▼────────┐\n │ FileTransfer     │   │ TextTransfer         │   │ DesktopShare     │\n │ Business         │   │ Business             │   │ Business         │\n │ (文件分块+校验)    │   │ (实时同步文字)         │   │ (屏幕采集+推流)    │\n └────────┬────────┘   └──────────┬──────────┘   └────────┬────────┘\n          │                        │                        │\n          └────────────────────────┼────────────────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ 业务级组件 (TSX)              │\n                    │ WebRTCFileTransfer.tsx 等     │\n                    └─────────────────────────────┘\n```\n\n#### 4.3.1 连接层 — `useWebRTCConnectionCore`\n\n**职责**：管理 WebSocket 信令连接 + RTCPeerConnection 生命周期。\n\n```\nconnect(code, role)\n    │\n    ├─ 1. WebSocket 连接到 ws://host/api/ws/webrtc?code=XXX&role=YYY\n    │\n    ├─ 2. 收到 \"peer-joined\" → 创建 RTCPeerConnection\n    │      └─ ICE 服务器从 getIceServersConfig() 获取\n    │\n    ├─ 3. Sender 创建 DataChannel → 创建 Offer\n    │      Receiver 等待 ondatachannel\n    │\n    ├─ 4. offer/answer/ice-candidate 通过 WebSocket 交换\n    │\n    ├─ 5. ICE 连接建立 → DataChannel open → 连接完成\n    │\n    └─ 6. 断开时：发送 disconnection → 清理 PeerConnection\n```\n\n#### 4.3.2 数据通道层 — `useWebRTCDataChannelManager`\n\n**职责**：管理 DataChannel 消息的路由分发。\n\n**核心设计：多 channel 路由**\n\n所有 JSON 消息通过 `channel` 字段路由到不同的业务处理器：\n\n```typescript\n// 消息格式\n{\n  channel: \"file-transfer\" | \"text-transfer\" | \"desktop-share\",\n  type: \"具体消息类型\",\n  payload: { ... }\n}\n```\n\n```\nDataChannel.onmessage\n    │\n    ├─ typeof data === 'string' → JSON.parse\n    │   ├─ channel === 'file-transfer' → fileTransferHandler\n    │   ├─ channel === 'text-transfer' → textTransferHandler\n    │   └─ channel === 'desktop-share' → desktopShareHandler\n    │\n    └─ typeof data === ArrayBuffer → 二进制数据\n        └─ 优先路由到 'file-transfer' handler（文件块数据）\n```\n\n**注意**：三个功能（文件/文字/桌面）共享同一个 DataChannel（`shared-channel`），而非各自创建独立通道。\n\n#### 4.3.3 轨道管理层 — `useWebRTCTrackManager`\n\n**职责**：管理 MediaStream 轨道（仅桌面共享使用）。\n\n- `addTrack(track, stream)` — 添加视频/音频轨道\n- `removeTrack(sender)` — 移除轨道\n- `createOfferNow()` — 添加轨道后触发重协商\n- `onTrack` 回调 — 接收远程媒体流\n\n### 4.4 状态管理设计\n\n采用 **三层状态模型**：\n\n| 层级 | 技术 | 用途 | 示例 |\n|------|------|------|------|\n| 全局共享状态 | Zustand | WebRTC 连接状态 | `isConnected`, `currentRoom`, `error` |\n| 组件级状态 | React useState | UI 交互状态 | `selectedFiles`, `mode`, `progress` |\n| 引用状态 | React useRef | 回调/缓冲区/定时器 | `receiveBufferRef`, `messageHandlers`, 防抖 timer |\n\n#### Zustand Store 结构\n\n```typescript\ninterface WebRTCState {\n  // 连接状态\n  isConnected: boolean;       // 总体连接状态\n  isConnecting: boolean;      // 正在连接中\n  isWebSocketConnected: boolean;  // WebSocket 层连接\n  isPeerConnected: boolean;   // PeerConnection 层连接\n\n  // 错误状态\n  error: string | null;\n  canRetry: boolean;\n\n  // 房间信息\n  currentRoom: { code: string; role: 'sender' | 'receiver' } | null;\n\n  // Actions\n  updateState(partial: Partial<WebRTCState>): void;\n  setCurrentRoom(room: { code: string; role: string } | null): void;\n  resetToInitial(): void;\n}\n```\n\n### 4.5 文件传输详细设计\n\n#### 4.5.1 DataChannel 消息协议\n\n**file-transfer channel 消息类型：**\n\n| 方向 | type | payload | 说明 |\n|------|------|---------|------|\n| S→R | `file-list` | `FileInfo[]` | 发送方文件列表（实时同步） |\n| R→S | `file-request` | `{ fileId, fileName }` | 接收方请求下载 |\n| S→R | `file-metadata` | `{ id, name, size, type }` | 文件元信息 |\n| S→R | `file-chunk-info` | `{ fileId, chunkIndex, totalChunks, checksum }` | 块元信息（含 CRC32） |\n| S→R | *(二进制)* | `ArrayBuffer` (≤256KB) | 文件块数据 |\n| R→S | `file-chunk-ack` | `{ fileId, chunkIndex, success, checksum }` | 块确认（含校验结果） |\n| S→R | `file-complete` | `{ fileId }` | 文件传输完成 |\n\n#### 4.5.2 传输参数\n\n| 参数 | 值 | 说明 |\n|------|-----|------|\n| 块大小 | 256 KB | 每个 chunk 的最大字节数 |\n| 最大重试 | 5 次 | 单个 chunk 校验失败后的重试上限 |\n| 退避策略 | 指数退避 | 重试间隔指数增长 |\n| 校验算法 | CRC32 | 每个 chunk 独立校验 |\n| 流控 | 自适应 | 根据平均传输速度动态调整发送间隔 |\n| DataChannel 配置 | `ordered: true, maxRetransmits: 3` | 有序可靠传输 |\n\n#### 4.5.3 完整传输时序\n\n```\n发送方                              信令服务器                           接收方\n  │                                     │                                 │\n  │  1. POST /api/create-room           │                                 │\n  │ ──────────────────────────────────► │                                 │\n  │ ◄── {success: true, code: \"AB12CD\"} │                                 │\n  │                                     │                                 │\n  │  2. WS connect (role=sender)        │                                 │\n  │ ═══════════════════════════════════► │                                 │\n  │                                     │                                 │\n  │                                     │  3. GET /api/room-info?code=... │\n  │                                     │ ◄────────────────────────────── │\n  │                                     │ ── {success, status} ─────────► │\n  │                                     │                                 │\n  │                                     │  4. WS connect (role=receiver)  │\n  │                                     │ ◄═══════════════════════════════ │\n  │                                     │                                 │\n  │  5. ◄── peer-joined ──             │  ── peer-joined ──►             │\n  │                                     │                                 │\n  │  6. 创建 PeerConnection + DataChannel                                 │\n  │  7. 创建 SDP Offer                                                    │\n  │  ═══ offer ═══════════════════════► │ ═══ offer ═══════════════════► │\n  │                                     │                                 │\n  │                                     │  8. 创建 PeerConnection         │\n  │                                     │  9. 设置 Remote/Local SDP       │\n  │  ◄═══ answer ══════════════════════ │ ◄═══ answer ═════════════════ │\n  │                                     │                                 │\n  │  10. ICE Candidate 交换 ◄═══════════╋══════════════════════════════► │\n  │                                     │                                 │\n  │  ══════════ DataChannel OPEN ═══════╪══════════════════════════════ │\n  │                                     │                                 │\n  │  11. file-list ─────────────────────┼────────────────────────────────► │\n  │      [{id, name, size, type}, ...]  │                                 │\n  │                                     │                                 │\n  │  ◄─────────────────────────────────┼──── 12. file-request ────────── │\n  │      {fileId, fileName}             │                                 │\n  │                                     │                                 │\n  │  13. file-metadata ────────────────┼────────────────────────────────► │\n  │      {id, name, size, type}         │                                 │\n  │                                     │                                 │\n  │  ╔══ 循环每个 chunk ═══════════════╪════════════════════════════════╗ │\n  │  ║  14. file-chunk-info ───────────┼──────────────────────────────► ║ │\n  │  ║      {fileId, chunkIndex,        │                              ║ │\n  │  ║       totalChunks, checksum}     │                              ║ │\n  │  ║                                  │                              ║ │\n  │  ║  15. [ArrayBuffer 256KB] ───────┼──────────────────────────────► ║ │\n  │  ║                                  │                              ║ │\n  │  ║  ◄───────────────────────────────┼── 16. file-chunk-ack ─────── ║ │\n  │  ║      {fileId, chunkIndex,        │      success, checksum}      ║ │\n  │  ║                                  │                              ║ │\n  │  ║  [如果 success=false, 指数退避重试，最多5次]                      ║ │\n  │  ╚══════════════════════════════════╪══════════════════════════════╝ │\n  │                                     │                                 │\n  │  17. file-complete ────────────────┼────────────────────────────────► │\n  │      {fileId}                       │                                 │\n  │                                     │                   18. 组装 Blob  │\n  │                                     │                       → File   │\n  │                                     │                       → 下载    │\n```\n\n#### 4.5.4 文件列表同步机制\n\n发送方选择文件后，文件列表会 **实时同步** 到接收方：\n\n```\n发送方文件变更 → useFileListSync hook\n    │\n    ├─ 150ms 防抖（debounce）\n    │\n    ├─ 比对新旧文件列表（避免无变更的冗余同步）\n    │\n    └─ 通过 DataChannel 发送 file-list 消息\n        │\n        └─ 接收方更新 UI 展示文件列表\n```\n\n### 4.6 文字传输详细设计\n\n#### 4.6.1 DataChannel 消息协议\n\n**text-transfer channel 消息类型：**\n\n| 方向 | type | payload | 说明 |\n|------|------|---------|------|\n| S→R | `text-sync` | `{ text: string }` | 实时文本内容同步 |\n| 双向 | `text-typing` | `{ typing: boolean }` | 打字状态指示 |\n\n#### 4.6.2 实时同步机制\n\n```\n发送方 Textarea 输入\n    │\n    ├─ onChange → handleTextInputChange()\n    │\n    ├─ 调用 textTransfer.sendTextSync(text)\n    │   └─ DataChannel 发送 { channel: \"text-transfer\", type: \"text-sync\", payload: { text } }\n    │\n    ├─ 同时发送 text-typing: { typing: true }\n    │   └─ 1秒后自动发送 text-typing: { typing: false }\n    │\n    └─ 接收方\n        ├─ onTextSync 回调 → 更新显示文本\n        └─ onTyping 回调 → 显示 \"对方正在输入...\" 动画\n```\n\n**图片传输**：复用文件传输的 `file-transfer` channel，图片作为文件发送。\n\n### 4.7 桌面共享详细设计\n\n```\n发送方\n    │\n    ├─ navigator.mediaDevices.getDisplayMedia({ video: true, audio: true })\n    │\n    ├─ 获取 MediaStream → 提取 video/audio Track\n    │\n    ├─ trackManager.addTrack(track, stream)\n    │   └─ peerConnection.addTrack()\n    │\n    ├─ trackManager.createOfferNow()  ← 触发 SDP 重协商\n    │   └─ 新 Offer → WebSocket → 对方 → Answer → WebSocket → 本方\n    │\n    └─ 接收方\n        ├─ ontrack 事件 → 获取远程 MediaStream\n        └─ <video> 元素播放 → DesktopViewer 组件\n```\n\n### 4.8 ICE 配置设计\n\n#### 默认 STUN 服务器\n\n```typescript\nconst DEFAULT_ICE_SERVERS = [\n  { urls: 'stun:stun.easyvoip.com:3478' },\n  { urls: 'stun:stun.miwifi.com:3478' },\n  { urls: 'stun:stun.l.google.com:19302' },\n  { urls: 'stun:stun1.l.google.com:19302' },\n  { urls: 'stun:global.stun.twilio.com:3478' },\n];\n```\n\n#### 用户自定义\n\n- 通过 WebRTCSettings 界面添加 STUN/TURN 服务器\n- 配置持久化到 `localStorage`（key: `webrtc-ice-config`）\n- `getIceServersConfig()` 导出给非 React 代码使用\n- 配置变更后提示用户断开重连\n\n### 4.9 环境适配设计\n\n```\n                    ┌─────────────────────────────────────────┐\n                    │           config.ts 环境判断              │\n                    │                                         │\n                    │  isStaticMode = Next.js 构建输出判断      │\n                    │  isDev = NODE_ENV === 'development'      │\n                    └────────────────┬────────────────────────┘\n                                     │\n                   ┌─────────────────┼─────────────────┐\n                   │                 │                 │\n          ┌────────▼────────┐ ┌─────▼──────┐ ┌───────▼───────┐\n          │ 开发模式          │ │ SSG 静态模式 │ │ 嵌入 Go 模式   │\n          │ (yarn dev)       │ │ (build:ssg) │ │ (go run)      │\n          │                  │ │             │ │               │\n          │ API:             │ │ API:        │ │ API:          │\n          │ /api/* → Next.js │ │ 直连 Go     │ │ 同源 /api/*   │\n          │ API Route 代理   │ │ 后端地址     │ │               │\n          │                  │ │             │ │               │\n          │ WS:              │ │ WS:         │ │ WS:           │\n          │ ws://localhost   │ │ 当前域名     │ │ 当前域名       │\n          │ :8080/...        │ │ ws[s]://    │ │ ws[s]://      │\n          └──────────────────┘ └─────────────┘ └───────────────┘\n```\n\n#### URL 动态计算逻辑\n\n```typescript\n// API URL\ngetApiUrl() → \n  开发模式 ? '/api'  (通过 Next.js API Route 代理到 GO_BACKEND_URL)\n  静态模式 ? getDirectBackendUrl() + '/api'  (直连 Go 后端)\n\n// WebSocket URL\ngetWsUrl() →\n  开发模式 ? 'ws://localhost:8080'  (直连 Go 后端 WS)\n  静态模式 ? 'ws[s]://' + window.location.host  (同源)\n```\n\n### 4.10 URL 路由设计\n\n前端通过 URL 参数控制功能和模式：\n\n```\n/?type=webrtc&mode=send            → 文件传输-发送模式\n/?type=webrtc&mode=receive         → 文件传输-接收模式\n/?type=webrtc&mode=receive&code=ABC123  → 自动填入取件码\n/?type=message&mode=send           → 文字消息-发送模式\n/?type=desktop&mode=send           → 桌面共享-共享模式\n```\n\n**URL 参数映射**：\n- `type`: `webrtc` | `message` | `desktop` | `wechat` | `settings` → 对应 Tab\n- `mode`: `send` | `receive` → 发送/接收子模式\n- `code`: 6 位取件码 → 自动填入并可自动加入房间\n\n---\n\n## 五、关键设计决策\n\n### 5.1 为什么用纯 P2P？\n\n- **隐私**：文件内容不经服务器，用户数据零留存\n- **成本**：服务器仅做信令，带宽成本极低\n- **性能**：局域网环境下可接近网卡极限速度\n\n### 5.2 为什么共享单个 DataChannel？\n\n三个功能（文件/文字/桌面控制）共享同一个 `shared-channel`，通过 JSON `channel` 字段路由：\n\n- **简化连接管理**：只需建立一次 P2P 连接\n- **减少信令开销**：无需为每个功能单独协商\n- **统一状态管理**：连接/断开状态全局一致\n\n### 5.3 为什么用 CRC32 + ACK？\n\nWebRTC DataChannel 本身基于 SCTP（配置为 `ordered: true, maxRetransmits: 3`），已有一定可靠性。额外的 CRC32 + ACK 是 **应用层二次保障**：\n\n- 防止极端情况下 SCTP 重传仍失败后的数据损坏\n- 提供块级粒度的错误恢复（只重传失败的块，无需从头开始）\n- 接收方独立验证数据完整性\n\n### 5.4 为什么 256KB 块大小？\n\n- WebRTC DataChannel 最大消息大小约 256KB（不同浏览器有差异）\n- 小块有利于进度反馈的精度\n- 小块降低单次重传的代价\n\n### 5.5 为什么关闭 React Strict Mode？\n\n```typescript\n// next.config.ts\nreactStrictMode: false\n```\n\nReact 18+ Strict Mode 会在开发环境 **双重调用** effect，导致：\n- WebSocket 连接被创建两次\n- PeerConnection 生命周期混乱\n- DataChannel 状态不一致\n\n---\n\n## 六、部署模式\n\n### 模式一：开发模式\n\n```bash\n# 终端 1：Go 后端\ngo run cmd/main.go          # :8080\n\n# 终端 2：Next.js 前端\ncd chuan-next && yarn dev   # :3000 (turbopack)\n```\n\n前端 API 调用链：`浏览器 → localhost:3000/api/* → Next.js API Route → localhost:8080/api/*`\n\n### 模式二：生产模式（Go 嵌入前端）\n\n```bash\ncd chuan-next && yarn build:ssg    # 输出到 out/\ncp -r out/ ../internal/web/frontend/  # 复制到 Go embed 目录\ngo build -o chuan cmd/*.go         # 编译\n./chuan                            # :8080 同时提供 API + 前端\n```\n\n所有请求都由 Go 单进程处理，前端静态文件通过 `go:embed` 嵌入二进制。\n\n---\n\n## 七、安全与限制\n\n| 维度 | 现状 |\n|------|------|\n| 加密 | WebRTC 自带 DTLS 加密，P2P 数据全程加密传输 |\n| 认证 | 无用户认证，仅靠 6 位取件码 |\n| 房间安全 | 取件码空间 34^6 ≈ 15 亿，暴力破解概率低 |\n| 数据留存 | 服务器零数据留存，所有文件仅在浏览器内存/临时存储 |\n| 并发限制 | 每房间最多 2 人，单进程内存管理 |\n| 文件大小 | 受限于浏览器内存（大文件需接收方有足够内存组装 Blob）|\n| NAT 穿透 | 依赖 STUN 服务器，对称 NAT 需 TURN 服务器（默认未配置）|\n"},"files":{"ARCHITECTURE.md":"# 文件快传（Chuan）— 系统架构与技术设计文档\n\n> 最后更新：2025-08-02\n\n---\n\n## 一、项目总览\n\n**文件快传（Chuan）** 是一个基于 **WebRTC P2P** 的文件传输应用。核心设计理念：**所有实际数据（文件、文字、桌面画面）均通过 WebRTC 点对点直传，服务器仅承担信令中继和房间管理职责**。\n\n### 技术栈\n\n| 层级 | 技术选型 |\n|------|---------|\n| 后端 | Go 1.21 · chi/v5 路由 · gorilla/websocket |\n| 前端 | Next.js 15 · React 19 · TypeScript · Tailwind CSS 4 |\n| 状态管理 | Zustand 5 + React useState/useRef |\n| UI 组件 | shadcn/ui (Radix UI) · Lucide Icons |\n| P2P 通信 | WebRTC DataChannel（文件/文字）· MediaStream（桌面共享）|\n| 部署 | 双模式：Next.js 开发模式 / Go 嵌入前端静态文件 |\n\n### 核心特性\n\n| 功能 | 实现方式 |\n|------|---------|\n| 文件传输 | WebRTC DataChannel · 256KB 分块 · CRC32 校验 · ACK 确认 |\n| 文本消息 | WebRTC DataChannel · 实时双向同步 · 打字状态 |\n| 桌面共享 | WebRTC MediaStream · getDisplayMedia · renegotiation |\n| ICE 配置 | 默认 5 个 STUN · 支持自定义 STUN/TURN · localStorage 持久化 |\n\n---\n\n## 二、系统架构\n\n### 2.1 整体架构图\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│                        Go 后端 (:8080)                               │\n│  ┌──────────────┐  ┌───────────────────┐  ┌─────────────────────┐  │\n│  │  REST API     │  │  WebSocket 信令    │  │  静态文件服务        │  │\n│  │  /api/*       │  │  /api/ws/webrtc   │  │  go:embed frontend  │  │\n│  │               │  │  /ws/webrtc       │  │                     │  │\n│  │ - create-room │  │                   │  │  SPA 回退 index.html │  │\n│  │ - room-info   │  │  纯中继 转发消息   │  │                     │  │\n│  └──────────────┘  └───────────────────┘  └─────────────────────┘  │\n│                              │                                       │\n│              ┌───────────────┼───────────────┐                       │\n│              │   内存房间管理  (sync.RWMutex)  │                       │\n│              │   map[string]*WebRTCRoom       │                       │\n│              │   1小时过期 · 5分钟定时清理     │                       │\n│              └───────────────────────────────┘                       │\n└──────────────────────────────────────────────────────────────────────┘\n        │ WebSocket                                    │ WebSocket\n        │ (信令: offer/answer/ice-candidate)            │\n        ▼                                              ▼\n┌───────────────┐          WebRTC P2P          ┌───────────────┐\n│  发送方浏览器   │ ◄══════════════════════════► │  接收方浏览器   │\n│               │   DataChannel (文件/文字)      │               │\n│  Next.js App  │   MediaStream (桌面共享)       │  Next.js App  │\n└───────────────┘                              └───────────────┘\n```\n\n### 2.2 数据流向\n\n```\n1. 控制平面 (HTTP):  浏览器 ──── REST API ────→ Go 后端    (房间创建/查询)\n2. 信令平面 (WS):    浏览器 ←──── WebSocket ──→ Go 后端    (SDP/ICE 交换)\n3. 数据平面 (P2P):   浏览器 ◄═══ DataChannel ══► 浏览器     (文件/文字直传)\n4. 媒体平面 (P2P):   浏览器 ◄═══ MediaStream ══► 浏览器     (桌面画面直传)\n```\n\n**关键设计：数据平面完全绕过服务器，文件和文字内容不经服务器中转。**\n\n---\n\n## 三、后端设计\n\n### 3.1 目录结构\n\n```\ncmd/\n├── main.go          # 入口：参数解析 → 配置 → 路由 → 启动服务器\n├── config.go        # 配置管理：命令行 > 环境变量 > .chuan.env > 默认值\n├── router.go        # chi 路由注册 + 中间件 + 前端静态服务\n└── server.go        # HTTP Server 封装 + 优雅关闭 (SIGINT/SIGTERM)\n\ninternal/\n├── handlers/\n│   └── handlers.go  # HTTP/WebSocket 请求处理（薄层，委托给 service）\n├── models/\n│   └── models.go    # 数据模型：WebRTCRoom、WebRTCClient、RoomStatus\n├── services/\n│   └── webrtc_service.go  # 核心信令服务：房间管理 + WebSocket 消息转发\n└── web/\n    └── frontend.go  # go:embed 嵌入前端 + SPA 回退\n```\n\n### 3.2 API 端点\n\n| 方法 | 路径 | 功能 | 请求/响应 |\n|------|------|------|-----------|\n| `POST` | `/api/create-room` | 创建房间 | `{}` → `{success, code, message}` |\n| `GET` | `/api/room-info?code=XXX` | 查询房间状态 | → `{success, status: RoomStatus}` |\n| `GET` | `/api/webrtc-room-status?code=XXX` | 同上（别名）| 同上 |\n| `WS` | `/api/ws/webrtc?code=&role=` | WebRTC 信令 | WebSocket 双向 |\n| `WS` | `/ws/webrtc?code=&role=` | 同上（兼容路径）| 同上 |\n| `GET` | `/*` | 前端静态文件 | SPA 回退 |\n\n### 3.3 房间管理\n\n- **房间代码**：6 位，字符集 `123456789ABCDEFGHIJKLMNPQRSTUVWXYZ`（排除 0 和 O，避免混淆）\n- **房间容量**：最多 2 人（1 sender + 1 receiver）\n- **过期策略**：创建后 1 小时过期，每 5 分钟后台清理\n- **存储方式**：纯内存 `map[string]*WebRTCRoom` + `sync.RWMutex`，无数据库\n\n### 3.4 WebSocket 信令协议\n\n#### 连接 URL\n```\nws[s]://host/api/ws/webrtc?code=ROOM_CODE&role=sender|receiver&channel=shared\n```\n\n#### 服务端 → 客户端\n\n| type | payload | 触发时机 |\n|------|---------|---------|\n| `peer-joined` | `{ role }` | 对方加入房间 |\n| `disconnection` | `{ role, message }` | 对方断开连接 |\n| `error` | `{ message }` | 房间不存在 / 已满 / 参数无效 |\n\n#### 客户端 ↔ 客户端（经服务端纯转发）\n\n| type | payload | 说明 |\n|------|---------|------|\n| `offer` | `RTCSessionDescription` | SDP Offer |\n| `answer` | `RTCSessionDescription` | SDP Answer |\n| `ice-candidate` | `RTCIceCandidate` | ICE 候选地址 |\n\n**服务端对 offer/answer/ice-candidate 消息不做任何解析，仅中继转发给房间内另一方。**\n\n---\n\n## 四、前端设计（重点）\n\n### 4.1 目录结构\n\n```\nchuan-next/src/\n├── app/                            # Next.js App Router\n│   ├── page.tsx                    # 入口页（SSG）\n│   ├── layout.tsx                  # 根布局（字体、Toast、Umami 统计）\n│   ├── globals.css                 # 全局样式 + 动画定义\n│   ├── HomePage.tsx                # 主页面组件（Tab 管理 + WebRTC 状态）\n│   ├── HomePageWrapper.tsx         # Suspense 包裹（SSR 兼容）\n│   └── api/                        # Next.js API Routes（开发模式代理）\n│       ├── create-room/route.ts    # → GO_BACKEND_URL/api/create-room\n│       ├── room-info/route.ts      # → GO_BACKEND_URL/api/room-info\n│       ├── room-status/route.ts    # → GO_BACKEND_URL/api/room-status\n│       ├── create-text-room/route.ts\n│       ├── get-text-content/route.ts\n│       └── update-files/route.ts\n│\n├── components/                     # UI 组件\n│   ├── WebRTCFileTransfer.tsx      # 文件传输整合组件（659行）\n│   ├── WebRTCTextImageTransfer.tsx # 文字+图片传输整合组件\n│   ├── DesktopShare.tsx            # 桌面共享整合组件\n│   ├── Hero.tsx                    # 页头（标题、GitHub 链接）\n│   ├── webrtc/                     # WebRTC 功能子组件\n│   │   ├── WebRTCFileUpload.tsx    # 文件发送 UI\n│   │   ├── WebRTCFileReceive.tsx   # 文件接收 UI\n│   │   ├── WebRTCTextSender.tsx    # 文字发送 UI\n│   │   ├── WebRTCTextReceiver.tsx  # 文字接收 UI\n│   │   ├── WebRTCDesktopSender.tsx # 桌面共享发送 UI\n│   │   ├── WebRTCDesktopReceiver.tsx # 桌面共享接收 UI\n│   │   └── WebRTCSettings.tsx      # ICE 服务器配置 UI\n│   └── ui/                         # 基础 UI 组件 (shadcn/ui)\n│       ├── button.tsx              # 按钮（6种 variant）\n│       ├── tabs.tsx                # 标签页（Radix Tabs）\n│       ├── input.tsx               # 输入框\n│       ├── card.tsx                # 卡片\n│       ├── dialog.tsx              # 对话框\n│       ├── progress.tsx            # 进度条\n│       ├── textarea.tsx            # 文本域\n│       ├── toast.tsx               # Toast (Radix)\n│       ├── toast-simple.tsx        # 轻量 Toast (自定义)\n│       ├── toaster.tsx             # Toast 渲染\n│       └── confirm-dialog.tsx      # 确认对话框\n│\n├── hooks/                          # 自定义 Hooks（核心逻辑层）\n│   ├── index.ts                    # 统一导出\n│   ├── connection/                 # WebRTC 连接管理\n│   │   ├── useSharedWebRTCManager.ts      # 整合入口\n│   │   ├── useWebRTCConnectionCore.ts     # 核心连接（WS + PeerConnection）\n│   │   ├── useWebRTCDataChannelManager.ts # DataChannel 管理 + 消息路由\n│   │   ├── useWebRTCTrackManager.ts       # MediaStream 轨道管理\n│   │   ├── useWebRTCStateManager.ts       # Zustand store 封装\n│   │   ├── useRoomConnection.ts           # 加入房间逻辑\n│   │   ├── useConnectionState.ts          # 连接状态变化处理\n│   │   └── useWebRTCSupport.ts            # 浏览器 WebRTC 检测\n│   ├── file-transfer/              # 文件传输业务\n│   │   ├── useFileTransferBusiness.ts     # 核心：分块传输 + CRC32 + ACK\n│   │   ├── useFileListSync.ts             # 文件列表实时同步\n│   │   └── useFileStateManager.ts         # 文件状态（选中/下载/进度）\n│   ├── text-transfer/              # 文字传输业务\n│   │   └── useTextTransferBusiness.ts     # 实时文字同步 + 打字状态\n│   ├── desktop-share/              # 桌面共享业务\n│   │   └── useDesktopShareBusiness.ts     # getDisplayMedia + 轨道管理\n│   ├── settings/                   # 设置\n│   │   ├── useIceServersConfig.ts         # ICE 服务器配置 + localStorage\n│   │   └── useWebRTCConfigSync.ts         # 配置变更监听\n│   └── ui/                         # UI 相关\n│       ├── webRTCStore.ts                 # Zustand 全局状态\n│       ├── useURLHandler.ts               # URL 参数管理\n│       ├── useTabNavigation.ts            # Tab 切换管理\n│       └── useConfirmDialog.ts            # 确认对话框 Hook\n│\n├── lib/                            # 工具库\n│   ├── config.ts                   # 环境配置（API URL / WS URL 动态计算）\n│   ├── utils.ts                    # cn() 样式合并\n│   ├── client-api.ts               # ClientAPI 封装\n│   ├── api-utils.ts                # apiFetch() 统一请求\n│   └── webrtc-support.ts           # WebRTC 支持检测\n│\n└── types/\n    └── index.ts                    # 全局类型定义\n```\n\n### 4.2 组件层次结构\n\n```\nRootLayout (layout.tsx)\n  └─ ToastProvider (toast-simple.tsx)\n       └─ HomePageWrapper (Suspense)\n            └─ HomePage\n                 ├─ Hero                           # 页头\n                 ├─ WebRTCUnsupportedModal          # WebRTC 不支持提示\n                 ├─ ConfirmDialog                   # Tab 切换确认\n                 └─ Tabs (5个标签)\n                      │\n                      ├─ [Tab: webrtc] WebRTCFileTransfer\n                      │    ├─ 模式切换 (发送/接收)\n                      │    ├─ [发送模式] WebRTCFileUpload\n                      │    │    ├─ 拖拽上传区域 / 文件列表\n                      │    │    ├─ RoomInfoDisplay (取件码 + 二维码 + 复制)\n                      │    │    ├─ ConnectionStatus (连接状态)\n                      │    │    └─ 传输进度条\n                      │    └─ [接收模式] WebRTCFileReceive\n                      │         ├─ 取件码输入 (6位)\n                      │         ├─ ConnectionStatus (连接状态)\n                      │         └─ 文件列表 + 下载按钮\n                      │\n                      ├─ [Tab: message] WebRTCTextImageTransfer\n                      │    ├─ 模式切换 (发送/接收)\n                      │    ├─ [发送模式] WebRTCTextSender\n                      │    │    ├─ Textarea 编辑器\n                      │    │    ├─ 图片发送按钮\n                      │    │    └─ RoomInfoDisplay\n                      │    └─ [接收模式] WebRTCTextReceiver\n                      │         ├─ 取件码输入\n                      │         └─ 实时文字显示 + 图片接收\n                      │\n                      ├─ [Tab: desktop] DesktopShare\n                      │    ├─ 模式切换 (共享/查看)\n                      │    ├─ [共享模式] WebRTCDesktopSender\n                      │    │    └─ 选择屏幕 → 开始共享\n                      │    └─ [查看模式] WebRTCDesktopReceiver\n                      │         └─ DesktopViewer (video 标签)\n                      │\n                      ├─ [Tab: wechat] WeChatGroup\n                      │    └─ 微信群二维码\n                      │\n                      └─ [Tab: settings] WebRTCSettings\n                           └─ ICE 服务器配置表单\n```\n\n### 4.3 Hooks 架构（核心设计）\n\n前端逻辑层采用 **分层 Hooks 架构**，自底向上组合：\n\n```\n                    ┌─────────────────────────────┐\n                    │    Zustand Store             │  ← 最底层：全局状态\n                    │    (webRTCStore.ts)           │\n                    │    isConnected, isConnecting  │\n                    │    currentRoom, error         │\n                    └──────────────┬──────────────┘\n                                   │\n            ┌──────────────────────┼──────────────────────┐\n            │                      │                      │\n   ┌────────▼────────┐  ┌─────────▼──────────┐  ┌───────▼─────────┐\n   │ StateManager     │  │ DataChannelManager  │  │ TrackManager     │\n   │ (Zustand 封装)    │  │ (消息路由 + 收发)    │  │ (媒体轨道管理)    │\n   └────────┬────────┘  └─────────┬──────────┘  └───────┬─────────┘\n            │                      │                      │\n            └──────────────────────┼──────────────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ ConnectionCore               │  ← WebSocket + PeerConnection\n                    │ (useWebRTCConnectionCore)     │     信令处理 + ICE 交换\n                    └──────────────┬──────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ SharedWebRTCManager          │  ← 整合入口（4合1）\n                    │ (useSharedWebRTCManager)      │     返回统一 WebRTCConnection\n                    └──────────────┬──────────────┘\n                                   │\n          ┌────────────────────────┼────────────────────────┐\n          │                        │                        │\n ┌────────▼────────┐   ┌──────────▼──────────┐   ┌────────▼────────┐\n │ FileTransfer     │   │ TextTransfer         │   │ DesktopShare     │\n │ Business         │   │ Business             │   │ Business         │\n │ (文件分块+校验)    │   │ (实时同步文字)         │   │ (屏幕采集+推流)    │\n └────────┬────────┘   └──────────┬──────────┘   └────────┬────────┘\n          │                        │                        │\n          └────────────────────────┼────────────────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ 业务级组件 (TSX)              │\n                    │ WebRTCFileTransfer.tsx 等     │\n                    └─────────────────────────────┘\n```\n\n#### 4.3.1 连接层 — `useWebRTCConnectionCore`\n\n**职责**：管理 WebSocket 信令连接 + RTCPeerConnection 生命周期。\n\n```\nconnect(code, role)\n    │\n    ├─ 1. WebSocket 连接到 ws://host/api/ws/webrtc?code=XXX&role=YYY\n    │\n    ├─ 2. 收到 \"peer-joined\" → 创建 RTCPeerConnection\n    │      └─ ICE 服务器从 getIceServersConfig() 获取\n    │\n    ├─ 3. Sender 创建 DataChannel → 创建 Offer\n    │      Receiver 等待 ondatachannel\n    │\n    ├─ 4. offer/answer/ice-candidate 通过 WebSocket 交换\n    │\n    ├─ 5. ICE 连接建立 → DataChannel open → 连接完成\n    │\n    └─ 6. 断开时：发送 disconnection → 清理 PeerConnection\n```\n\n#### 4.3.2 数据通道层 — `useWebRTCDataChannelManager`\n\n**职责**：管理 DataChannel 消息的路由分发。\n\n**核心设计：多 channel 路由**\n\n所有 JSON 消息通过 `channel` 字段路由到不同的业务处理器：\n\n```typescript\n// 消息格式\n{\n  channel: \"file-transfer\" | \"text-transfer\" | \"desktop-share\",\n  type: \"具体消息类型\",\n  payload: { ... }\n}\n```\n\n```\nDataChannel.onmessage\n    │\n    ├─ typeof data === 'string' → JSON.parse\n    │   ├─ channel === 'file-transfer' → fileTransferHandler\n    │   ├─ channel === 'text-transfer' → textTransferHandler\n    │   └─ channel === 'desktop-share' → desktopShareHandler\n    │\n    └─ typeof data === ArrayBuffer → 二进制数据\n        └─ 优先路由到 'file-transfer' handler（文件块数据）\n```\n\n**注意**：三个功能（文件/文字/桌面）共享同一个 DataChannel（`shared-channel`），而非各自创建独立通道。\n\n#### 4.3.3 轨道管理层 — `useWebRTCTrackManager`\n\n**职责**：管理 MediaStream 轨道（仅桌面共享使用）。\n\n- `addTrack(track, stream)` — 添加视频/音频轨道\n- `removeTrack(sender)` — 移除轨道\n- `createOfferNow()` — 添加轨道后触发重协商\n- `onTrack` 回调 — 接收远程媒体流\n\n### 4.4 状态管理设计\n\n采用 **三层状态模型**：\n\n| 层级 | 技术 | 用途 | 示例 |\n|------|------|------|------|\n| 全局共享状态 | Zustand | WebRTC 连接状态 | `isConnected`, `currentRoom`, `error` |\n| 组件级状态 | React useState | UI 交互状态 | `selectedFiles`, `mode`, `progress` |\n| 引用状态 | React useRef | 回调/缓冲区/定时器 | `receiveBufferRef`, `messageHandlers`, 防抖 timer |\n\n#### Zustand Store 结构\n\n```typescript\ninterface WebRTCState {\n  // 连接状态\n  isConnected: boolean;       // 总体连接状态\n  isConnecting: boolean;      // 正在连接中\n  isWebSocketConnected: boolean;  // WebSocket 层连接\n  isPeerConnected: boolean;   // PeerConnection 层连接\n\n  // 错误状态\n  error: string | null;\n  canRetry: boolean;\n\n  // 房间信息\n  currentRoom: { code: string; role: 'sender' | 'receiver' } | null;\n\n  // Actions\n  updateState(partial: Partial<WebRTCState>): void;\n  setCurrentRoom(room: { code: string; role: string } | null): void;\n  resetToInitial(): void;\n}\n```\n\n### 4.5 文件传输详细设计\n\n#### 4.5.1 DataChannel 消息协议\n\n**file-transfer channel 消息类型：**\n\n| 方向 | type | payload | 说明 |\n|------|------|---------|------|\n| S→R | `file-list` | `FileInfo[]` | 发送方文件列表（实时同步） |\n| R→S | `file-request` | `{ fileId, fileName }` | 接收方请求下载 |\n| S→R | `file-metadata` | `{ id, name, size, type }` | 文件元信息 |\n| S→R | `file-chunk-info` | `{ fileId, chunkIndex, totalChunks, checksum }` | 块元信息（含 CRC32） |\n| S→R | *(二进制)* | `ArrayBuffer` (≤256KB) | 文件块数据 |\n| R→S | `file-chunk-ack` | `{ fileId, chunkIndex, success, checksum }` | 块确认（含校验结果） |\n| S→R | `file-complete` | `{ fileId }` | 文件传输完成 |\n\n#### 4.5.2 传输参数\n\n| 参数 | 值 | 说明 |\n|------|-----|------|\n| 块大小 | 256 KB | 每个 chunk 的最大字节数 |\n| 最大重试 | 5 次 | 单个 chunk 校验失败后的重试上限 |\n| 退避策略 | 指数退避 | 重试间隔指数增长 |\n| 校验算法 | CRC32 | 每个 chunk 独立校验 |\n| 流控 | 自适应 | 根据平均传输速度动态调整发送间隔 |\n| DataChannel 配置 | `ordered: true, maxRetransmits: 3` | 有序可靠传输 |\n\n#### 4.5.3 完整传输时序\n\n```\n发送方                              信令服务器                           接收方\n  │                                     │                                 │\n  │  1. POST /api/create-room           │                                 │\n  │ ──────────────────────────────────► │                                 │\n  │ ◄── {success: true, code: \"AB12CD\"} │                                 │\n  │                                     │                                 │\n  │  2. WS connect (role=sender)        │                                 │\n  │ ═══════════════════════════════════► │                                 │\n  │                                     │                                 │\n  │                                     │  3. GET /api/room-info?code=... │\n  │                                     │ ◄────────────────────────────── │\n  │                                     │ ── {success, status} ─────────► │\n  │                                     │                                 │\n  │                                     │  4. WS connect (role=receiver)  │\n  │                                     │ ◄═══════════════════════════════ │\n  │                                     │                                 │\n  │  5. ◄── peer-joined ──             │  ── peer-joined ──►             │\n  │                                     │                                 │\n  │  6. 创建 PeerConnection + DataChannel                                 │\n  │  7. 创建 SDP Offer                                                    │\n  │  ═══ offer ═══════════════════════► │ ═══ offer ═══════════════════► │\n  │                                     │                                 │\n  │                                     │  8. 创建 PeerConnection         │\n  │                                     │  9. 设置 Remote/Local SDP       │\n  │  ◄═══ answer ══════════════════════ │ ◄═══ answer ═════════════════ │\n  │                                     │                                 │\n  │  10. ICE Candidate 交换 ◄═══════════╋══════════════════════════════► │\n  │                                     │                                 │\n  │  ══════════ DataChannel OPEN ═══════╪══════════════════════════════ │\n  │                                     │                                 │\n  │  11. file-list ─────────────────────┼────────────────────────────────► │\n  │      [{id, name, size, type}, ...]  │                                 │\n  │                                     │                                 │\n  │  ◄─────────────────────────────────┼──── 12. file-request ────────── │\n  │      {fileId, fileName}             │                                 │\n  │                                     │                                 │\n  │  13. file-metadata ────────────────┼────────────────────────────────► │\n  │      {id, name, size, type}         │                                 │\n  │                                     │                                 │\n  │  ╔══ 循环每个 chunk ═══════════════╪════════════════════════════════╗ │\n  │  ║  14. file-chunk-info ───────────┼──────────────────────────────► ║ │\n  │  ║      {fileId, chunkIndex,        │                              ║ │\n  │  ║       totalChunks, checksum}     │                              ║ │\n  │  ║                                  │                              ║ │\n  │  ║  15. [ArrayBuffer 256KB] ───────┼──────────────────────────────► ║ │\n  │  ║                                  │                              ║ │\n  │  ║  ◄───────────────────────────────┼── 16. file-chunk-ack ─────── ║ │\n  │  ║      {fileId, chunkIndex,        │      success, checksum}      ║ │\n  │  ║                                  │                              ║ │\n  │  ║  [如果 success=false, 指数退避重试，最多5次]                      ║ │\n  │  ╚══════════════════════════════════╪══════════════════════════════╝ │\n  │                                     │                                 │\n  │  17. file-complete ────────────────┼────────────────────────────────► │\n  │      {fileId}                       │                                 │\n  │                                     │                   18. 组装 Blob  │\n  │                                     │                       → File   │\n  │                                     │                       → 下载    │\n```\n\n#### 4.5.4 文件列表同步机制\n\n发送方选择文件后，文件列表会 **实时同步** 到接收方：\n\n```\n发送方文件变更 → useFileListSync hook\n    │\n    ├─ 150ms 防抖（debounce）\n    │\n    ├─ 比对新旧文件列表（避免无变更的冗余同步）\n    │\n    └─ 通过 DataChannel 发送 file-list 消息\n        │\n        └─ 接收方更新 UI 展示文件列表\n```\n\n### 4.6 文字传输详细设计\n\n#### 4.6.1 DataChannel 消息协议\n\n**text-transfer channel 消息类型：**\n\n| 方向 | type | payload | 说明 |\n|------|------|---------|------|\n| S→R | `text-sync` | `{ text: string }` | 实时文本内容同步 |\n| 双向 | `text-typing` | `{ typing: boolean }` | 打字状态指示 |\n\n#### 4.6.2 实时同步机制\n\n```\n发送方 Textarea 输入\n    │\n    ├─ onChange → handleTextInputChange()\n    │\n    ├─ 调用 textTransfer.sendTextSync(text)\n    │   └─ DataChannel 发送 { channel: \"text-transfer\", type: \"text-sync\", payload: { text } }\n    │\n    ├─ 同时发送 text-typing: { typing: true }\n    │   └─ 1秒后自动发送 text-typing: { typing: false }\n    │\n    └─ 接收方\n        ├─ onTextSync 回调 → 更新显示文本\n        └─ onTyping 回调 → 显示 \"对方正在输入...\" 动画\n```\n\n**图片传输**：复用文件传输的 `file-transfer` channel，图片作为文件发送。\n\n### 4.7 桌面共享详细设计\n\n```\n发送方\n    │\n    ├─ navigator.mediaDevices.getDisplayMedia({ video: true, audio: true })\n    │\n    ├─ 获取 MediaStream → 提取 video/audio Track\n    │\n    ├─ trackManager.addTrack(track, stream)\n    │   └─ peerConnection.addTrack()\n    │\n    ├─ trackManager.createOfferNow()  ← 触发 SDP 重协商\n    │   └─ 新 Offer → WebSocket → 对方 → Answer → WebSocket → 本方\n    │\n    └─ 接收方\n        ├─ ontrack 事件 → 获取远程 MediaStream\n        └─ <video> 元素播放 → DesktopViewer 组件\n```\n\n### 4.8 ICE 配置设计\n\n#### 默认 STUN 服务器\n\n```typescript\nconst DEFAULT_ICE_SERVERS = [\n  { urls: 'stun:stun.easyvoip.com:3478' },\n  { urls: 'stun:stun.miwifi.com:3478' },\n  { urls: 'stun:stun.l.google.com:19302' },\n  { urls: 'stun:stun1.l.google.com:19302' },\n  { urls: 'stun:global.stun.twilio.com:3478' },\n];\n```\n\n#### 用户自定义\n\n- 通过 WebRTCSettings 界面添加 STUN/TURN 服务器\n- 配置持久化到 `localStorage`（key: `webrtc-ice-config`）\n- `getIceServersConfig()` 导出给非 React 代码使用\n- 配置变更后提示用户断开重连\n\n### 4.9 环境适配设计\n\n```\n                    ┌─────────────────────────────────────────┐\n                    │           config.ts 环境判断              │\n                    │                                         │\n                    │  isStaticMode = Next.js 构建输出判断      │\n                    │  isDev = NODE_ENV === 'development'      │\n                    └────────────────┬────────────────────────┘\n                                     │\n                   ┌─────────────────┼─────────────────┐\n                   │                 │                 │\n          ┌────────▼────────┐ ┌─────▼──────┐ ┌───────▼───────┐\n          │ 开发模式          │ │ SSG 静态模式 │ │ 嵌入 Go 模式   │\n          │ (yarn dev)       │ │ (build:ssg) │ │ (go run)      │\n          │                  │ │             │ │               │\n          │ API:             │ │ API:        │ │ API:          │\n          │ /api/* → Next.js │ │ 直连 Go     │ │ 同源 /api/*   │\n          │ API Route 代理   │ │ 后端地址     │ │               │\n          │                  │ │             │ │               │\n          │ WS:              │ │ WS:         │ │ WS:           │\n          │ ws://localhost   │ │ 当前域名     │ │ 当前域名       │\n          │ :8080/...        │ │ ws[s]://    │ │ ws[s]://      │\n          └──────────────────┘ └─────────────┘ └───────────────┘\n```\n\n#### URL 动态计算逻辑\n\n```typescript\n// API URL\ngetApiUrl() → \n  开发模式 ? '/api'  (通过 Next.js API Route 代理到 GO_BACKEND_URL)\n  静态模式 ? getDirectBackendUrl() + '/api'  (直连 Go 后端)\n\n// WebSocket URL\ngetWsUrl() →\n  开发模式 ? 'ws://localhost:8080'  (直连 Go 后端 WS)\n  静态模式 ? 'ws[s]://' + window.location.host  (同源)\n```\n\n### 4.10 URL 路由设计\n\n前端通过 URL 参数控制功能和模式：\n\n```\n/?type=webrtc&mode=send            → 文件传输-发送模式\n/?type=webrtc&mode=receive         → 文件传输-接收模式\n/?type=webrtc&mode=receive&code=ABC123  → 自动填入取件码\n/?type=message&mode=send           → 文字消息-发送模式\n/?type=desktop&mode=send           → 桌面共享-共享模式\n```\n\n**URL 参数映射**：\n- `type`: `webrtc` | `message` | `desktop` | `wechat` | `settings` → 对应 Tab\n- `mode`: `send` | `receive` → 发送/接收子模式\n- `code`: 6 位取件码 → 自动填入并可自动加入房间\n\n---\n\n## 五、关键设计决策\n\n### 5.1 为什么用纯 P2P？\n\n- **隐私**：文件内容不经服务器，用户数据零留存\n- **成本**：服务器仅做信令，带宽成本极低\n- **性能**：局域网环境下可接近网卡极限速度\n\n### 5.2 为什么共享单个 DataChannel？\n\n三个功能（文件/文字/桌面控制）共享同一个 `shared-channel`，通过 JSON `channel` 字段路由：\n\n- **简化连接管理**：只需建立一次 P2P 连接\n- **减少信令开销**：无需为每个功能单独协商\n- **统一状态管理**：连接/断开状态全局一致\n\n### 5.3 为什么用 CRC32 + ACK？\n\nWebRTC DataChannel 本身基于 SCTP（配置为 `ordered: true, maxRetransmits: 3`），已有一定可靠性。额外的 CRC32 + ACK 是 **应用层二次保障**：\n\n- 防止极端情况下 SCTP 重传仍失败后的数据损坏\n- 提供块级粒度的错误恢复（只重传失败的块，无需从头开始）\n- 接收方独立验证数据完整性\n\n### 5.4 为什么 256KB 块大小？\n\n- WebRTC DataChannel 最大消息大小约 256KB（不同浏览器有差异）\n- 小块有利于进度反馈的精度\n- 小块降低单次重传的代价\n\n### 5.5 为什么关闭 React Strict Mode？\n\n```typescript\n// next.config.ts\nreactStrictMode: false\n```\n\nReact 18+ Strict Mode 会在开发环境 **双重调用** effect，导致：\n- WebSocket 连接被创建两次\n- PeerConnection 生命周期混乱\n- DataChannel 状态不一致\n\n---\n\n## 六、部署模式\n\n### 模式一：开发模式\n\n```bash\n# 终端 1：Go 后端\ngo run cmd/main.go          # :8080\n\n# 终端 2：Next.js 前端\ncd chuan-next && yarn dev   # :3000 (turbopack)\n```\n\n前端 API 调用链：`浏览器 → localhost:3000/api/* → Next.js API Route → localhost:8080/api/*`\n\n### 模式二：生产模式（Go 嵌入前端）\n\n```bash\ncd chuan-next && yarn build:ssg    # 输出到 out/\ncp -r out/ ../internal/web/frontend/  # 复制到 Go embed 目录\ngo build -o chuan cmd/*.go         # 编译\n./chuan                            # :8080 同时提供 API + 前端\n```\n\n所有请求都由 Go 单进程处理，前端静态文件通过 `go:embed` 嵌入二进制。\n\n---\n\n## 七、安全与限制\n\n| 维度 | 现状 |\n|------|------|\n| 加密 | WebRTC 自带 DTLS 加密，P2P 数据全程加密传输 |\n| 认证 | 无用户认证，仅靠 6 位取件码 |\n| 房间安全 | 取件码空间 34^6 ≈ 15 亿，暴力破解概率低 |\n| 数据留存 | 服务器零数据留存，所有文件仅在浏览器内存/临时存储 |\n| 并发限制 | 每房间最多 2 人，单进程内存管理 |\n| 文件大小 | 受限于浏览器内存（大文件需接收方有足够内存组装 Blob）|\n| NAT 穿透 | 依赖 STUN 服务器，对称 NAT 需 TURN 服务器（默认未配置）|\n"},"items":[{"name":"ARCHITECTURE.md","path":"ARCHITECTURE.md","rawUrl":"https://raw.githubusercontent.com/MatrixSeven/file-transfer-go/HEAD/ARCHITECTURE.md","title":"System Architecture & Module Boundaries","category":"project-spec","format":"markdown","content":"# 文件快传（Chuan）— 系统架构与技术设计文档\n\n> 最后更新：2025-08-02\n\n---\n\n## 一、项目总览\n\n**文件快传（Chuan）** 是一个基于 **WebRTC P2P** 的文件传输应用。核心设计理念：**所有实际数据（文件、文字、桌面画面）均通过 WebRTC 点对点直传，服务器仅承担信令中继和房间管理职责**。\n\n### 技术栈\n\n| 层级 | 技术选型 |\n|------|---------|\n| 后端 | Go 1.21 · chi/v5 路由 · gorilla/websocket |\n| 前端 | Next.js 15 · React 19 · TypeScript · Tailwind CSS 4 |\n| 状态管理 | Zustand 5 + React useState/useRef |\n| UI 组件 | shadcn/ui (Radix UI) · Lucide Icons |\n| P2P 通信 | WebRTC DataChannel（文件/文字）· MediaStream（桌面共享）|\n| 部署 | 双模式：Next.js 开发模式 / Go 嵌入前端静态文件 |\n\n### 核心特性\n\n| 功能 | 实现方式 |\n|------|---------|\n| 文件传输 | WebRTC DataChannel · 256KB 分块 · CRC32 校验 · ACK 确认 |\n| 文本消息 | WebRTC DataChannel · 实时双向同步 · 打字状态 |\n| 桌面共享 | WebRTC MediaStream · getDisplayMedia · renegotiation |\n| ICE 配置 | 默认 5 个 STUN · 支持自定义 STUN/TURN · localStorage 持久化 |\n\n---\n\n## 二、系统架构\n\n### 2.1 整体架构图\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│                        Go 后端 (:8080)                               │\n│  ┌──────────────┐  ┌───────────────────┐  ┌─────────────────────┐  │\n│  │  REST API     │  │  WebSocket 信令    │  │  静态文件服务        │  │\n│  │  /api/*       │  │  /api/ws/webrtc   │  │  go:embed frontend  │  │\n│  │               │  │  /ws/webrtc       │  │                     │  │\n│  │ - create-room │  │                   │  │  SPA 回退 index.html │  │\n│  │ - room-info   │  │  纯中继 转发消息   │  │                     │  │\n│  └──────────────┘  └───────────────────┘  └─────────────────────┘  │\n│                              │                                       │\n│              ┌───────────────┼───────────────┐                       │\n│              │   内存房间管理  (sync.RWMutex)  │                       │\n│              │   map[string]*WebRTCRoom       │                       │\n│              │   1小时过期 · 5分钟定时清理     │                       │\n│              └───────────────────────────────┘                       │\n└──────────────────────────────────────────────────────────────────────┘\n        │ WebSocket                                    │ WebSocket\n        │ (信令: offer/answer/ice-candidate)            │\n        ▼                                              ▼\n┌───────────────┐          WebRTC P2P          ┌───────────────┐\n│  发送方浏览器   │ ◄══════════════════════════► │  接收方浏览器   │\n│               │   DataChannel (文件/文字)      │               │\n│  Next.js App  │   MediaStream (桌面共享)       │  Next.js App  │\n└───────────────┘                              └───────────────┘\n```\n\n### 2.2 数据流向\n\n```\n1. 控制平面 (HTTP):  浏览器 ──── REST API ────→ Go 后端    (房间创建/查询)\n2. 信令平面 (WS):    浏览器 ←──── WebSocket ──→ Go 后端    (SDP/ICE 交换)\n3. 数据平面 (P2P):   浏览器 ◄═══ DataChannel ══► 浏览器     (文件/文字直传)\n4. 媒体平面 (P2P):   浏览器 ◄═══ MediaStream ══► 浏览器     (桌面画面直传)\n```\n\n**关键设计：数据平面完全绕过服务器，文件和文字内容不经服务器中转。**\n\n---\n\n## 三、后端设计\n\n### 3.1 目录结构\n\n```\ncmd/\n├── main.go          # 入口：参数解析 → 配置 → 路由 → 启动服务器\n├── config.go        # 配置管理：命令行 > 环境变量 > .chuan.env > 默认值\n├── router.go        # chi 路由注册 + 中间件 + 前端静态服务\n└── server.go        # HTTP Server 封装 + 优雅关闭 (SIGINT/SIGTERM)\n\ninternal/\n├── handlers/\n│   └── handlers.go  # HTTP/WebSocket 请求处理（薄层，委托给 service）\n├── models/\n│   └── models.go    # 数据模型：WebRTCRoom、WebRTCClient、RoomStatus\n├── services/\n│   └── webrtc_service.go  # 核心信令服务：房间管理 + WebSocket 消息转发\n└── web/\n    └── frontend.go  # go:embed 嵌入前端 + SPA 回退\n```\n\n### 3.2 API 端点\n\n| 方法 | 路径 | 功能 | 请求/响应 |\n|------|------|------|-----------|\n| `POST` | `/api/create-room` | 创建房间 | `{}` → `{success, code, message}` |\n| `GET` | `/api/room-info?code=XXX` | 查询房间状态 | → `{success, status: RoomStatus}` |\n| `GET` | `/api/webrtc-room-status?code=XXX` | 同上（别名）| 同上 |\n| `WS` | `/api/ws/webrtc?code=&role=` | WebRTC 信令 | WebSocket 双向 |\n| `WS` | `/ws/webrtc?code=&role=` | 同上（兼容路径）| 同上 |\n| `GET` | `/*` | 前端静态文件 | SPA 回退 |\n\n### 3.3 房间管理\n\n- **房间代码**：6 位，字符集 `123456789ABCDEFGHIJKLMNPQRSTUVWXYZ`（排除 0 和 O，避免混淆）\n- **房间容量**：最多 2 人（1 sender + 1 receiver）\n- **过期策略**：创建后 1 小时过期，每 5 分钟后台清理\n- **存储方式**：纯内存 `map[string]*WebRTCRoom` + `sync.RWMutex`，无数据库\n\n### 3.4 WebSocket 信令协议\n\n#### 连接 URL\n```\nws[s]://host/api/ws/webrtc?code=ROOM_CODE&role=sender|receiver&channel=shared\n```\n\n#### 服务端 → 客户端\n\n| type | payload | 触发时机 |\n|------|---------|---------|\n| `peer-joined` | `{ role }` | 对方加入房间 |\n| `disconnection` | `{ role, message }` | 对方断开连接 |\n| `error` | `{ message }` | 房间不存在 / 已满 / 参数无效 |\n\n#### 客户端 ↔ 客户端（经服务端纯转发）\n\n| type | payload | 说明 |\n|------|---------|------|\n| `offer` | `RTCSessionDescription` | SDP Offer |\n| `answer` | `RTCSessionDescription` | SDP Answer |\n| `ice-candidate` | `RTCIceCandidate` | ICE 候选地址 |\n\n**服务端对 offer/answer/ice-candidate 消息不做任何解析，仅中继转发给房间内另一方。**\n\n---\n\n## 四、前端设计（重点）\n\n### 4.1 目录结构\n\n```\nchuan-next/src/\n├── app/                            # Next.js App Router\n│   ├── page.tsx                    # 入口页（SSG）\n│   ├── layout.tsx                  # 根布局（字体、Toast、Umami 统计）\n│   ├── globals.css                 # 全局样式 + 动画定义\n│   ├── HomePage.tsx                # 主页面组件（Tab 管理 + WebRTC 状态）\n│   ├── HomePageWrapper.tsx         # Suspense 包裹（SSR 兼容）\n│   └── api/                        # Next.js API Routes（开发模式代理）\n│       ├── create-room/route.ts    # → GO_BACKEND_URL/api/create-room\n│       ├── room-info/route.ts      # → GO_BACKEND_URL/api/room-info\n│       ├── room-status/route.ts    # → GO_BACKEND_URL/api/room-status\n│       ├── create-text-room/route.ts\n│       ├── get-text-content/route.ts\n│       └── update-files/route.ts\n│\n├── components/                     # UI 组件\n│   ├── WebRTCFileTransfer.tsx      # 文件传输整合组件（659行）\n│   ├── WebRTCTextImageTransfer.tsx # 文字+图片传输整合组件\n│   ├── DesktopShare.tsx            # 桌面共享整合组件\n│   ├── Hero.tsx                    # 页头（标题、GitHub 链接）\n│   ├── webrtc/                     # WebRTC 功能子组件\n│   │   ├── WebRTCFileUpload.tsx    # 文件发送 UI\n│   │   ├── WebRTCFileReceive.tsx   # 文件接收 UI\n│   │   ├── WebRTCTextSender.tsx    # 文字发送 UI\n│   │   ├── WebRTCTextReceiver.tsx  # 文字接收 UI\n│   │   ├── WebRTCDesktopSender.tsx # 桌面共享发送 UI\n│   │   ├── WebRTCDesktopReceiver.tsx # 桌面共享接收 UI\n│   │   └── WebRTCSettings.tsx      # ICE 服务器配置 UI\n│   └── ui/                         # 基础 UI 组件 (shadcn/ui)\n│       ├── button.tsx              # 按钮（6种 variant）\n│       ├── tabs.tsx                # 标签页（Radix Tabs）\n│       ├── input.tsx               # 输入框\n│       ├── card.tsx                # 卡片\n│       ├── dialog.tsx              # 对话框\n│       ├── progress.tsx            # 进度条\n│       ├── textarea.tsx            # 文本域\n│       ├── toast.tsx               # Toast (Radix)\n│       ├── toast-simple.tsx        # 轻量 Toast (自定义)\n│       ├── toaster.tsx             # Toast 渲染\n│       └── confirm-dialog.tsx      # 确认对话框\n│\n├── hooks/                          # 自定义 Hooks（核心逻辑层）\n│   ├── index.ts                    # 统一导出\n│   ├── connection/                 # WebRTC 连接管理\n│   │   ├── useSharedWebRTCManager.ts      # 整合入口\n│   │   ├── useWebRTCConnectionCore.ts     # 核心连接（WS + PeerConnection）\n│   │   ├── useWebRTCDataChannelManager.ts # DataChannel 管理 + 消息路由\n│   │   ├── useWebRTCTrackManager.ts       # MediaStream 轨道管理\n│   │   ├── useWebRTCStateManager.ts       # Zustand store 封装\n│   │   ├── useRoomConnection.ts           # 加入房间逻辑\n│   │   ├── useConnectionState.ts          # 连接状态变化处理\n│   │   └── useWebRTCSupport.ts            # 浏览器 WebRTC 检测\n│   ├── file-transfer/              # 文件传输业务\n│   │   ├── useFileTransferBusiness.ts     # 核心：分块传输 + CRC32 + ACK\n│   │   ├── useFileListSync.ts             # 文件列表实时同步\n│   │   └── useFileStateManager.ts         # 文件状态（选中/下载/进度）\n│   ├── text-transfer/              # 文字传输业务\n│   │   └── useTextTransferBusiness.ts     # 实时文字同步 + 打字状态\n│   ├── desktop-share/              # 桌面共享业务\n│   │   └── useDesktopShareBusiness.ts     # getDisplayMedia + 轨道管理\n│   ├── settings/                   # 设置\n│   │   ├── useIceServersConfig.ts         # ICE 服务器配置 + localStorage\n│   │   └── useWebRTCConfigSync.ts         # 配置变更监听\n│   └── ui/                         # UI 相关\n│       ├── webRTCStore.ts                 # Zustand 全局状态\n│       ├── useURLHandler.ts               # URL 参数管理\n│       ├── useTabNavigation.ts            # Tab 切换管理\n│       └── useConfirmDialog.ts            # 确认对话框 Hook\n│\n├── lib/                            # 工具库\n│   ├── config.ts                   # 环境配置（API URL / WS URL 动态计算）\n│   ├── utils.ts                    # cn() 样式合并\n│   ├── client-api.ts               # ClientAPI 封装\n│   ├── api-utils.ts                # apiFetch() 统一请求\n│   └── webrtc-support.ts           # WebRTC 支持检测\n│\n└── types/\n    └── index.ts                    # 全局类型定义\n```\n\n### 4.2 组件层次结构\n\n```\nRootLayout (layout.tsx)\n  └─ ToastProvider (toast-simple.tsx)\n       └─ HomePageWrapper (Suspense)\n            └─ HomePage\n                 ├─ Hero                           # 页头\n                 ├─ WebRTCUnsupportedModal          # WebRTC 不支持提示\n                 ├─ ConfirmDialog                   # Tab 切换确认\n                 └─ Tabs (5个标签)\n                      │\n                      ├─ [Tab: webrtc] WebRTCFileTransfer\n                      │    ├─ 模式切换 (发送/接收)\n                      │    ├─ [发送模式] WebRTCFileUpload\n                      │    │    ├─ 拖拽上传区域 / 文件列表\n                      │    │    ├─ RoomInfoDisplay (取件码 + 二维码 + 复制)\n                      │    │    ├─ ConnectionStatus (连接状态)\n                      │    │    └─ 传输进度条\n                      │    └─ [接收模式] WebRTCFileReceive\n                      │         ├─ 取件码输入 (6位)\n                      │         ├─ ConnectionStatus (连接状态)\n                      │         └─ 文件列表 + 下载按钮\n                      │\n                      ├─ [Tab: message] WebRTCTextImageTransfer\n                      │    ├─ 模式切换 (发送/接收)\n                      │    ├─ [发送模式] WebRTCTextSender\n                      │    │    ├─ Textarea 编辑器\n                      │    │    ├─ 图片发送按钮\n                      │    │    └─ RoomInfoDisplay\n                      │    └─ [接收模式] WebRTCTextReceiver\n                      │         ├─ 取件码输入\n                      │         └─ 实时文字显示 + 图片接收\n                      │\n                      ├─ [Tab: desktop] DesktopShare\n                      │    ├─ 模式切换 (共享/查看)\n                      │    ├─ [共享模式] WebRTCDesktopSender\n                      │    │    └─ 选择屏幕 → 开始共享\n                      │    └─ [查看模式] WebRTCDesktopReceiver\n                      │         └─ DesktopViewer (video 标签)\n                      │\n                      ├─ [Tab: wechat] WeChatGroup\n                      │    └─ 微信群二维码\n                      │\n                      └─ [Tab: settings] WebRTCSettings\n                           └─ ICE 服务器配置表单\n```\n\n### 4.3 Hooks 架构（核心设计）\n\n前端逻辑层采用 **分层 Hooks 架构**，自底向上组合：\n\n```\n                    ┌─────────────────────────────┐\n                    │    Zustand Store             │  ← 最底层：全局状态\n                    │    (webRTCStore.ts)           │\n                    │    isConnected, isConnecting  │\n                    │    currentRoom, error         │\n                    └──────────────┬──────────────┘\n                                   │\n            ┌──────────────────────┼──────────────────────┐\n            │                      │                      │\n   ┌────────▼────────┐  ┌─────────▼──────────┐  ┌───────▼─────────┐\n   │ StateManager     │  │ DataChannelManager  │  │ TrackManager     │\n   │ (Zustand 封装)    │  │ (消息路由 + 收发)    │  │ (媒体轨道管理)    │\n   └────────┬────────┘  └─────────┬──────────┘  └───────┬─────────┘\n            │                      │                      │\n            └──────────────────────┼──────────────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ ConnectionCore               │  ← WebSocket + PeerConnection\n                    │ (useWebRTCConnectionCore)     │     信令处理 + ICE 交换\n                    └──────────────┬──────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ SharedWebRTCManager          │  ← 整合入口（4合1）\n                    │ (useSharedWebRTCManager)      │     返回统一 WebRTCConnection\n                    └──────────────┬──────────────┘\n                                   │\n          ┌────────────────────────┼────────────────────────┐\n          │                        │                        │\n ┌────────▼────────┐   ┌──────────▼──────────┐   ┌────────▼────────┐\n │ FileTransfer     │   │ TextTransfer         │   │ DesktopShare     │\n │ Business         │   │ Business             │   │ Business         │\n │ (文件分块+校验)    │   │ (实时同步文字)         │   │ (屏幕采集+推流)    │\n └────────┬────────┘   └──────────┬──────────┘   └────────┬────────┘\n          │                        │                        │\n          └────────────────────────┼────────────────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ 业务级组件 (TSX)              │\n                    │ WebRTCFileTransfer.tsx 等     │\n                    └─────────────────────────────┘\n```\n\n#### 4.3.1 连接层 — `useWebRTCConnectionCore`\n\n**职责**：管理 WebSocket 信令连接 + RTCPeerConnection 生命周期。\n\n```\nconnect(code, role)\n    │\n    ├─ 1. WebSocket 连接到 ws://host/api/ws/webrtc?code=XXX&role=YYY\n    │\n    ├─ 2. 收到 \"peer-joined\" → 创建 RTCPeerConnection\n    │      └─ ICE 服务器从 getIceServersConfig() 获取\n    │\n    ├─ 3. Sender 创建 DataChannel → 创建 Offer\n    │      Receiver 等待 ondatachannel\n    │\n    ├─ 4. offer/answer/ice-candidate 通过 WebSocket 交换\n    │\n    ├─ 5. ICE 连接建立 → DataChannel open → 连接完成\n    │\n    └─ 6. 断开时：发送 disconnection → 清理 PeerConnection\n```\n\n#### 4.3.2 数据通道层 — `useWebRTCDataChannelManager`\n\n**职责**：管理 DataChannel 消息的路由分发。\n\n**核心设计：多 channel 路由**\n\n所有 JSON 消息通过 `channel` 字段路由到不同的业务处理器：\n\n```typescript\n// 消息格式\n{\n  channel: \"file-transfer\" | \"text-transfer\" | \"desktop-share\",\n  type: \"具体消息类型\",\n  payload: { ... }\n}\n```\n\n```\nDataChannel.onmessage\n    │\n    ├─ typeof data === 'string' → JSON.parse\n    │   ├─ channel === 'file-transfer' → fileTransferHandler\n    │   ├─ channel === 'text-transfer' → textTransferHandler\n    │   └─ channel === 'desktop-share' → desktopShareHandler\n    │\n    └─ typeof data === ArrayBuffer → 二进制数据\n        └─ 优先路由到 'file-transfer' handler（文件块数据）\n```\n\n**注意**：三个功能（文件/文字/桌面）共享同一个 DataChannel（`shared-channel`），而非各自创建独立通道。\n\n#### 4.3.3 轨道管理层 — `useWebRTCTrackManager`\n\n**职责**：管理 MediaStream 轨道（仅桌面共享使用）。\n\n- `addTrack(track, stream)` — 添加视频/音频轨道\n- `removeTrack(sender)` — 移除轨道\n- `createOfferNow()` — 添加轨道后触发重协商\n- `onTrack` 回调 — 接收远程媒体流\n\n### 4.4 状态管理设计\n\n采用 **三层状态模型**：\n\n| 层级 | 技术 | 用途 | 示例 |\n|------|------|------|------|\n| 全局共享状态 | Zustand | WebRTC 连接状态 | `isConnected`, `currentRoom`, `error` |\n| 组件级状态 | React useState | UI 交互状态 | `selectedFiles`, `mode`, `progress` |\n| 引用状态 | React useRef | 回调/缓冲区/定时器 | `receiveBufferRef`, `messageHandlers`, 防抖 timer |\n\n#### Zustand Store 结构\n\n```typescript\ninterface WebRTCState {\n  // 连接状态\n  isConnected: boolean;       // 总体连接状态\n  isConnecting: boolean;      // 正在连接中\n  isWebSocketConnected: boolean;  // WebSocket 层连接\n  isPeerConnected: boolean;   // PeerConnection 层连接\n\n  // 错误状态\n  error: string | null;\n  canRetry: boolean;\n\n  // 房间信息\n  currentRoom: { code: string; role: 'sender' | 'receiver' } | null;\n\n  // Actions\n  updateState(partial: Partial<WebRTCState>): void;\n  setCurrentRoom(room: { code: string; role: string } | null): void;\n  resetToInitial(): void;\n}\n```\n\n### 4.5 文件传输详细设计\n\n#### 4.5.1 DataChannel 消息协议\n\n**file-transfer channel 消息类型：**\n\n| 方向 | type | payload | 说明 |\n|------|------|---------|------|\n| S→R | `file-list` | `FileInfo[]` | 发送方文件列表（实时同步） |\n| R→S | `file-request` | `{ fileId, fileName }` | 接收方请求下载 |\n| S→R | `file-metadata` | `{ id, name, size, type }` | 文件元信息 |\n| S→R | `file-chunk-info` | `{ fileId, chunkIndex, totalChunks, checksum }` | 块元信息（含 CRC32） |\n| S→R | *(二进制)* | `ArrayBuffer` (≤256KB) | 文件块数据 |\n| R→S | `file-chunk-ack` | `{ fileId, chunkIndex, success, checksum }` | 块确认（含校验结果） |\n| S→R | `file-complete` | `{ fileId }` | 文件传输完成 |\n\n#### 4.5.2 传输参数\n\n| 参数 | 值 | 说明 |\n|------|-----|------|\n| 块大小 | 256 KB | 每个 chunk 的最大字节数 |\n| 最大重试 | 5 次 | 单个 chunk 校验失败后的重试上限 |\n| 退避策略 | 指数退避 | 重试间隔指数增长 |\n| 校验算法 | CRC32 | 每个 chunk 独立校验 |\n| 流控 | 自适应 | 根据平均传输速度动态调整发送间隔 |\n| DataChannel 配置 | `ordered: true, maxRetransmits: 3` | 有序可靠传输 |\n\n#### 4.5.3 完整传输时序\n\n```\n发送方                              信令服务器                           接收方\n  │                                     │                                 │\n  │  1. POST /api/create-room           │                                 │\n  │ ──────────────────────────────────► │                                 │\n  │ ◄── {success: true, code: \"AB12CD\"} │                                 │\n  │                                     │                                 │\n  │  2. WS connect (role=sender)        │                                 │\n  │ ═══════════════════════════════════► │                                 │\n  │                                     │                                 │\n  │                                     │  3. GET /api/room-info?code=... │\n  │                                     │ ◄────────────────────────────── │\n  │                                     │ ── {success, status} ─────────► │\n  │                                     │                                 │\n  │                                     │  4. WS connect (role=receiver)  │\n  │                                     │ ◄═══════════════════════════════ │\n  │                                     │                                 │\n  │  5. ◄── peer-joined ──             │  ── peer-joined ──►             │\n  │                                     │                                 │\n  │  6. 创建 PeerConnection + DataChannel                                 │\n  │  7. 创建 SDP Offer                                                    │\n  │  ═══ offer ═══════════════════════► │ ═══ offer ═══════════════════► │\n  │                                     │                                 │\n  │                                     │  8. 创建 PeerConnection         │\n  │                                     │  9. 设置 Remote/Local SDP       │\n  │  ◄═══ answer ══════════════════════ │ ◄═══ answer ═════════════════ │\n  │                                     │                                 │\n  │  10. ICE Candidate 交换 ◄═══════════╋══════════════════════════════► │\n  │                                     │                                 │\n  │  ══════════ DataChannel OPEN ═══════╪══════════════════════════════ │\n  │                                     │                                 │\n  │  11. file-list ─────────────────────┼────────────────────────────────► │\n  │      [{id, name, size, type}, ...]  │                                 │\n  │                                     │                                 │\n  │  ◄─────────────────────────────────┼──── 12. file-request ────────── │\n  │      {fileId, fileName}             │                                 │\n  │                                     │                                 │\n  │  13. file-metadata ────────────────┼────────────────────────────────► │\n  │      {id, name, size, type}         │                                 │\n  │                                     │                                 │\n  │  ╔══ 循环每个 chunk ═══════════════╪════════════════════════════════╗ │\n  │  ║  14. file-chunk-info ───────────┼──────────────────────────────► ║ │\n  │  ║      {fileId, chunkIndex,        │                              ║ │\n  │  ║       totalChunks, checksum}     │                              ║ │\n  │  ║                                  │                              ║ │\n  │  ║  15. [ArrayBuffer 256KB] ───────┼──────────────────────────────► ║ │\n  │  ║                                  │                              ║ │\n  │  ║  ◄───────────────────────────────┼── 16. file-chunk-ack ─────── ║ │\n  │  ║      {fileId, chunkIndex,        │      success, checksum}      ║ │\n  │  ║                                  │                              ║ │\n  │  ║  [如果 success=false, 指数退避重试，最多5次]                      ║ │\n  │  ╚══════════════════════════════════╪══════════════════════════════╝ │\n  │                                     │                                 │\n  │  17. file-complete ────────────────┼────────────────────────────────► │\n  │      {fileId}                       │                                 │\n  │                                     │                   18. 组装 Blob  │\n  │                                     │                       → File   │\n  │                                     │                       → 下载    │\n```\n\n#### 4.5.4 文件列表同步机制\n\n发送方选择文件后，文件列表会 **实时同步** 到接收方：\n\n```\n发送方文件变更 → useFileListSync hook\n    │\n    ├─ 150ms 防抖（debounce）\n    │\n    ├─ 比对新旧文件列表（避免无变更的冗余同步）\n    │\n    └─ 通过 DataChannel 发送 file-list 消息\n        │\n        └─ 接收方更新 UI 展示文件列表\n```\n\n### 4.6 文字传输详细设计\n\n#### 4.6.1 DataChannel 消息协议\n\n**text-transfer channel 消息类型：**\n\n| 方向 | type | payload | 说明 |\n|------|------|---------|------|\n| S→R | `text-sync` | `{ text: string }` | 实时文本内容同步 |\n| 双向 | `text-typing` | `{ typing: boolean }` | 打字状态指示 |\n\n#### 4.6.2 实时同步机制\n\n```\n发送方 Textarea 输入\n    │\n    ├─ onChange → handleTextInputChange()\n    │\n    ├─ 调用 textTransfer.sendTextSync(text)\n    │   └─ DataChannel 发送 { channel: \"text-transfer\", type: \"text-sync\", payload: { text } }\n    │\n    ├─ 同时发送 text-typing: { typing: true }\n    │   └─ 1秒后自动发送 text-typing: { typing: false }\n    │\n    └─ 接收方\n        ├─ onTextSync 回调 → 更新显示文本\n        └─ onTyping 回调 → 显示 \"对方正在输入...\" 动画\n```\n\n**图片传输**：复用文件传输的 `file-transfer` channel，图片作为文件发送。\n\n### 4.7 桌面共享详细设计\n\n```\n发送方\n    │\n    ├─ navigator.mediaDevices.getDisplayMedia({ video: true, audio: true })\n    │\n    ├─ 获取 MediaStream → 提取 video/audio Track\n    │\n    ├─ trackManager.addTrack(track, stream)\n    │   └─ peerConnection.addTrack()\n    │\n    ├─ trackManager.createOfferNow()  ← 触发 SDP 重协商\n    │   └─ 新 Offer → WebSocket → 对方 → Answer → WebSocket → 本方\n    │\n    └─ 接收方\n        ├─ ontrack 事件 → 获取远程 MediaStream\n        └─ <video> 元素播放 → DesktopViewer 组件\n```\n\n### 4.8 ICE 配置设计\n\n#### 默认 STUN 服务器\n\n```typescript\nconst DEFAULT_ICE_SERVERS = [\n  { urls: 'stun:stun.easyvoip.com:3478' },\n  { urls: 'stun:stun.miwifi.com:3478' },\n  { urls: 'stun:stun.l.google.com:19302' },\n  { urls: 'stun:stun1.l.google.com:19302' },\n  { urls: 'stun:global.stun.twilio.com:3478' },\n];\n```\n\n#### 用户自定义\n\n- 通过 WebRTCSettings 界面添加 STUN/TURN 服务器\n- 配置持久化到 `localStorage`（key: `webrtc-ice-config`）\n- `getIceServersConfig()` 导出给非 React 代码使用\n- 配置变更后提示用户断开重连\n\n### 4.9 环境适配设计\n\n```\n                    ┌─────────────────────────────────────────┐\n                    │           config.ts 环境判断              │\n                    │                                         │\n                    │  isStaticMode = Next.js 构建输出判断      │\n                    │  isDev = NODE_ENV === 'development'      │\n                    └────────────────┬────────────────────────┘\n                                     │\n                   ┌─────────────────┼─────────────────┐\n                   │                 │                 │\n          ┌────────▼────────┐ ┌─────▼──────┐ ┌───────▼───────┐\n          │ 开发模式          │ │ SSG 静态模式 │ │ 嵌入 Go 模式   │\n          │ (yarn dev)       │ │ (build:ssg) │ │ (go run)      │\n          │                  │ │             │ │               │\n          │ API:             │ │ API:        │ │ API:          │\n          │ /api/* → Next.js │ │ 直连 Go     │ │ 同源 /api/*   │\n          │ API Route 代理   │ │ 后端地址     │ │               │\n          │                  │ │             │ │               │\n          │ WS:              │ │ WS:         │ │ WS:           │\n          │ ws://localhost   │ │ 当前域名     │ │ 当前域名       │\n          │ :8080/...        │ │ ws[s]://    │ │ ws[s]://      │\n          └──────────────────┘ └─────────────┘ └───────────────┘\n```\n\n#### URL 动态计算逻辑\n\n```typescript\n// API URL\ngetApiUrl() → \n  开发模式 ? '/api'  (通过 Next.js API Route 代理到 GO_BACKEND_URL)\n  静态模式 ? getDirectBackendUrl() + '/api'  (直连 Go 后端)\n\n// WebSocket URL\ngetWsUrl() →\n  开发模式 ? 'ws://localhost:8080'  (直连 Go 后端 WS)\n  静态模式 ? 'ws[s]://' + window.location.host  (同源)\n```\n\n### 4.10 URL 路由设计\n\n前端通过 URL 参数控制功能和模式：\n\n```\n/?type=webrtc&mode=send            → 文件传输-发送模式\n/?type=webrtc&mode=receive         → 文件传输-接收模式\n/?type=webrtc&mode=receive&code=ABC123  → 自动填入取件码\n/?type=message&mode=send           → 文字消息-发送模式\n/?type=desktop&mode=send           → 桌面共享-共享模式\n```\n\n**URL 参数映射**：\n- `type`: `webrtc` | `message` | `desktop` | `wechat` | `settings` → 对应 Tab\n- `mode`: `send` | `receive` → 发送/接收子模式\n- `code`: 6 位取件码 → 自动填入并可自动加入房间\n\n---\n\n## 五、关键设计决策\n\n### 5.1 为什么用纯 P2P？\n\n- **隐私**：文件内容不经服务器，用户数据零留存\n- **成本**：服务器仅做信令，带宽成本极低\n- **性能**：局域网环境下可接近网卡极限速度\n\n### 5.2 为什么共享单个 DataChannel？\n\n三个功能（文件/文字/桌面控制）共享同一个 `shared-channel`，通过 JSON `channel` 字段路由：\n\n- **简化连接管理**：只需建立一次 P2P 连接\n- **减少信令开销**：无需为每个功能单独协商\n- **统一状态管理**：连接/断开状态全局一致\n\n### 5.3 为什么用 CRC32 + ACK？\n\nWebRTC DataChannel 本身基于 SCTP（配置为 `ordered: true, maxRetransmits: 3`），已有一定可靠性。额外的 CRC32 + ACK 是 **应用层二次保障**：\n\n- 防止极端情况下 SCTP 重传仍失败后的数据损坏\n- 提供块级粒度的错误恢复（只重传失败的块，无需从头开始）\n- 接收方独立验证数据完整性\n\n### 5.4 为什么 256KB 块大小？\n\n- WebRTC DataChannel 最大消息大小约 256KB（不同浏览器有差异）\n- 小块有利于进度反馈的精度\n- 小块降低单次重传的代价\n\n### 5.5 为什么关闭 React Strict Mode？\n\n```typescript\n// next.config.ts\nreactStrictMode: false\n```\n\nReact 18+ Strict Mode 会在开发环境 **双重调用** effect，导致：\n- WebSocket 连接被创建两次\n- PeerConnection 生命周期混乱\n- DataChannel 状态不一致\n\n---\n\n## 六、部署模式\n\n### 模式一：开发模式\n\n```bash\n# 终端 1：Go 后端\ngo run cmd/main.go          # :8080\n\n# 终端 2：Next.js 前端\ncd chuan-next && yarn dev   # :3000 (turbopack)\n```\n\n前端 API 调用链：`浏览器 → localhost:3000/api/* → Next.js API Route → localhost:8080/api/*`\n\n### 模式二：生产模式（Go 嵌入前端）\n\n```bash\ncd chuan-next && yarn build:ssg    # 输出到 out/\ncp -r out/ ../internal/web/frontend/  # 复制到 Go embed 目录\ngo build -o chuan cmd/*.go         # 编译\n./chuan                            # :8080 同时提供 API + 前端\n```\n\n所有请求都由 Go 单进程处理，前端静态文件通过 `go:embed` 嵌入二进制。\n\n---\n\n## 七、安全与限制\n\n| 维度 | 现状 |\n|------|------|\n| 加密 | WebRTC 自带 DTLS 加密，P2P 数据全程加密传输 |\n| 认证 | 无用户认证，仅靠 6 位取件码 |\n| 房间安全 | 取件码空间 34^6 ≈ 15 亿，暴力破解概率低 |\n| 数据留存 | 服务器零数据留存，所有文件仅在浏览器内存/临时存储 |\n| 并发限制 | 每房间最多 2 人，单进程内存管理 |\n| 文件大小 | 受限于浏览器内存（大文件需接收方有足够内存组装 Blob）|\n| NAT 穿透 | 依赖 STUN 服务器，对称 NAT 需 TURN 服务器（默认未配置）|\n","isInternal":false,"tokens":9611,"sizeBytes":38061}],"systemPromptSnippet":"<yakaai_skills repo=\"MatrixSeven/file-transfer-go\">\n<!-- File: ARCHITECTURE.md (Tokens: ~9611 | Category: project-spec) -->\n# 文件快传（Chuan）— 系统架构与技术设计文档\n\n> 最后更新：2025-08-02\n\n---\n\n## 一、项目总览\n\n**文件快传（Chuan）** 是一个基于 **WebRTC P2P** 的文件传输应用。核心设计理念：**所有实际数据（文件、文字、桌面画面）均通过 WebRTC 点对点直传，服务器仅承担信令中继和房间管理职责**。\n\n### 技术栈\n\n| 层级 | 技术选型 |\n|------|---------|\n| 后端 | Go 1.21 · chi/v5 路由 · gorilla/websocket |\n| 前端 | Next.js 15 · React 19 · TypeScript · Tailwind CSS 4 |\n| 状态管理 | Zustand 5 + React useState/useRef |\n| UI 组件 | shadcn/ui (Radix UI) · Lucide Icons |\n| P2P 通信 | WebRTC DataChannel（文件/文字）· MediaStream（桌面共享）|\n| 部署 | 双模式：Next.js 开发模式 / Go 嵌入前端静态文件 |\n\n### 核心特性\n\n| 功能 | 实现方式 |\n|------|---------|\n| 文件传输 | WebRTC DataChannel · 256KB 分块 · CRC32 校验 · ACK 确认 |\n| 文本消息 | WebRTC DataChannel · 实时双向同步 · 打字状态 |\n| 桌面共享 | WebRTC MediaStream · getDisplayMedia · renegotiation |\n| ICE 配置 | 默认 5 个 STUN · 支持自定义 STUN/TURN · localStorage 持久化 |\n\n---\n\n## 二、系统架构\n\n### 2.1 整体架构图\n\n```\n┌──────────────────────────────────────────────────────────────────────┐\n│                        Go 后端 (:8080)                               │\n│  ┌──────────────┐  ┌───────────────────┐  ┌─────────────────────┐  │\n│  │  REST API     │  │  WebSocket 信令    │  │  静态文件服务        │  │\n│  │  /api/*       │  │  /api/ws/webrtc   │  │  go:embed frontend  │  │\n│  │               │  │  /ws/webrtc       │  │                     │  │\n│  │ - create-room │  │                   │  │  SPA 回退 index.html │  │\n│  │ - room-info   │  │  纯中继 转发消息   │  │                     │  │\n│  └──────────────┘  └───────────────────┘  └─────────────────────┘  │\n│                              │                                       │\n│              ┌───────────────┼───────────────┐                       │\n│              │   内存房间管理  (sync.RWMutex)  │                       │\n│              │   map[string]*WebRTCRoom       │                       │\n│              │   1小时过期 · 5分钟定时清理     │                       │\n│              └───────────────────────────────┘                       │\n└──────────────────────────────────────────────────────────────────────┘\n        │ WebSocket                                    │ WebSocket\n        │ (信令: offer/answer/ice-candidate)            │\n        ▼                                              ▼\n┌───────────────┐          WebRTC P2P          ┌───────────────┐\n│  发送方浏览器   │ ◄══════════════════════════► │  接收方浏览器   │\n│               │   DataChannel (文件/文字)      │               │\n│  Next.js App  │   MediaStream (桌面共享)       │  Next.js App  │\n└───────────────┘                              └───────────────┘\n```\n\n### 2.2 数据流向\n\n```\n1. 控制平面 (HTTP):  浏览器 ──── REST API ────→ Go 后端    (房间创建/查询)\n2. 信令平面 (WS):    浏览器 ←──── WebSocket ──→ Go 后端    (SDP/ICE 交换)\n3. 数据平面 (P2P):   浏览器 ◄═══ DataChannel ══► 浏览器     (文件/文字直传)\n4. 媒体平面 (P2P):   浏览器 ◄═══ MediaStream ══► 浏览器     (桌面画面直传)\n```\n\n**关键设计：数据平面完全绕过服务器，文件和文字内容不经服务器中转。**\n\n---\n\n## 三、后端设计\n\n### 3.1 目录结构\n\n```\ncmd/\n├── main.go          # 入口：参数解析 → 配置 → 路由 → 启动服务器\n├── config.go        # 配置管理：命令行 > 环境变量 > .chuan.env > 默认值\n├── router.go        # chi 路由注册 + 中间件 + 前端静态服务\n└── server.go        # HTTP Server 封装 + 优雅关闭 (SIGINT/SIGTERM)\n\ninternal/\n├── handlers/\n│   └── handlers.go  # HTTP/WebSocket 请求处理（薄层，委托给 service）\n├── models/\n│   └── models.go    # 数据模型：WebRTCRoom、WebRTCClient、RoomStatus\n├── services/\n│   └── webrtc_service.go  # 核心信令服务：房间管理 + WebSocket 消息转发\n└── web/\n    └── frontend.go  # go:embed 嵌入前端 + SPA 回退\n```\n\n### 3.2 API 端点\n\n| 方法 | 路径 | 功能 | 请求/响应 |\n|------|------|------|-----------|\n| `POST` | `/api/create-room` | 创建房间 | `{}` → `{success, code, message}` |\n| `GET` | `/api/room-info?code=XXX` | 查询房间状态 | → `{success, status: RoomStatus}` |\n| `GET` | `/api/webrtc-room-status?code=XXX` | 同上（别名）| 同上 |\n| `WS` | `/api/ws/webrtc?code=&role=` | WebRTC 信令 | WebSocket 双向 |\n| `WS` | `/ws/webrtc?code=&role=` | 同上（兼容路径）| 同上 |\n| `GET` | `/*` | 前端静态文件 | SPA 回退 |\n\n### 3.3 房间管理\n\n- **房间代码**：6 位，字符集 `123456789ABCDEFGHIJKLMNPQRSTUVWXYZ`（排除 0 和 O，避免混淆）\n- **房间容量**：最多 2 人（1 sender + 1 receiver）\n- **过期策略**：创建后 1 小时过期，每 5 分钟后台清理\n- **存储方式**：纯内存 `map[string]*WebRTCRoom` + `sync.RWMutex`，无数据库\n\n### 3.4 WebSocket 信令协议\n\n#### 连接 URL\n```\nws[s]://host/api/ws/webrtc?code=ROOM_CODE&role=sender|receiver&channel=shared\n```\n\n#### 服务端 → 客户端\n\n| type | payload | 触发时机 |\n|------|---------|---------|\n| `peer-joined` | `{ role }` | 对方加入房间 |\n| `disconnection` | `{ role, message }` | 对方断开连接 |\n| `error` | `{ message }` | 房间不存在 / 已满 / 参数无效 |\n\n#### 客户端 ↔ 客户端（经服务端纯转发）\n\n| type | payload | 说明 |\n|------|---------|------|\n| `offer` | `RTCSessionDescription` | SDP Offer |\n| `answer` | `RTCSessionDescription` | SDP Answer |\n| `ice-candidate` | `RTCIceCandidate` | ICE 候选地址 |\n\n**服务端对 offer/answer/ice-candidate 消息不做任何解析，仅中继转发给房间内另一方。**\n\n---\n\n## 四、前端设计（重点）\n\n### 4.1 目录结构\n\n```\nchuan-next/src/\n├── app/                            # Next.js App Router\n│   ├── page.tsx                    # 入口页（SSG）\n│   ├── layout.tsx                  # 根布局（字体、Toast、Umami 统计）\n│   ├── globals.css                 # 全局样式 + 动画定义\n│   ├── HomePage.tsx                # 主页面组件（Tab 管理 + WebRTC 状态）\n│   ├── HomePageWrapper.tsx         # Suspense 包裹（SSR 兼容）\n│   └── api/                        # Next.js API Routes（开发模式代理）\n│       ├── create-room/route.ts    # → GO_BACKEND_URL/api/create-room\n│       ├── room-info/route.ts      # → GO_BACKEND_URL/api/room-info\n│       ├── room-status/route.ts    # → GO_BACKEND_URL/api/room-status\n│       ├── create-text-room/route.ts\n│       ├── get-text-content/route.ts\n│       └── update-files/route.ts\n│\n├── components/                     # UI 组件\n│   ├── WebRTCFileTransfer.tsx      # 文件传输整合组件（659行）\n│   ├── WebRTCTextImageTransfer.tsx # 文字+图片传输整合组件\n│   ├── DesktopShare.tsx            # 桌面共享整合组件\n│   ├── Hero.tsx                    # 页头（标题、GitHub 链接）\n│   ├── webrtc/                     # WebRTC 功能子组件\n│   │   ├── WebRTCFileUpload.tsx    # 文件发送 UI\n│   │   ├── WebRTCFileReceive.tsx   # 文件接收 UI\n│   │   ├── WebRTCTextSender.tsx    # 文字发送 UI\n│   │   ├── WebRTCTextReceiver.tsx  # 文字接收 UI\n│   │   ├── WebRTCDesktopSender.tsx # 桌面共享发送 UI\n│   │   ├── WebRTCDesktopReceiver.tsx # 桌面共享接收 UI\n│   │   └── WebRTCSettings.tsx      # ICE 服务器配置 UI\n│   └── ui/                         # 基础 UI 组件 (shadcn/ui)\n│       ├── button.tsx              # 按钮（6种 variant）\n│       ├── tabs.tsx                # 标签页（Radix Tabs）\n│       ├── input.tsx               # 输入框\n│       ├── card.tsx                # 卡片\n│       ├── dialog.tsx              # 对话框\n│       ├── progress.tsx            # 进度条\n│       ├── textarea.tsx            # 文本域\n│       ├── toast.tsx               # Toast (Radix)\n│       ├── toast-simple.tsx        # 轻量 Toast (自定义)\n│       ├── toaster.tsx             # Toast 渲染\n│       └── confirm-dialog.tsx      # 确认对话框\n│\n├── hooks/                          # 自定义 Hooks（核心逻辑层）\n│   ├── index.ts                    # 统一导出\n│   ├── connection/                 # WebRTC 连接管理\n│   │   ├── useSharedWebRTCManager.ts      # 整合入口\n│   │   ├── useWebRTCConnectionCore.ts     # 核心连接（WS + PeerConnection）\n│   │   ├── useWebRTCDataChannelManager.ts # DataChannel 管理 + 消息路由\n│   │   ├── useWebRTCTrackManager.ts       # MediaStream 轨道管理\n│   │   ├── useWebRTCStateManager.ts       # Zustand store 封装\n│   │   ├── useRoomConnection.ts           # 加入房间逻辑\n│   │   ├── useConnectionState.ts          # 连接状态变化处理\n│   │   └── useWebRTCSupport.ts            # 浏览器 WebRTC 检测\n│   ├── file-transfer/              # 文件传输业务\n│   │   ├── useFileTransferBusiness.ts     # 核心：分块传输 + CRC32 + ACK\n│   │   ├── useFileListSync.ts             # 文件列表实时同步\n│   │   └── useFileStateManager.ts         # 文件状态（选中/下载/进度）\n│   ├── text-transfer/              # 文字传输业务\n│   │   └── useTextTransferBusiness.ts     # 实时文字同步 + 打字状态\n│   ├── desktop-share/              # 桌面共享业务\n│   │   └── useDesktopShareBusiness.ts     # getDisplayMedia + 轨道管理\n│   ├── settings/                   # 设置\n│   │   ├── useIceServersConfig.ts         # ICE 服务器配置 + localStorage\n│   │   └── useWebRTCConfigSync.ts         # 配置变更监听\n│   └── ui/                         # UI 相关\n│       ├── webRTCStore.ts                 # Zustand 全局状态\n│       ├── useURLHandler.ts               # URL 参数管理\n│       ├── useTabNavigation.ts            # Tab 切换管理\n│       └── useConfirmDialog.ts            # 确认对话框 Hook\n│\n├── lib/                            # 工具库\n│   ├── config.ts                   # 环境配置（API URL / WS URL 动态计算）\n│   ├── utils.ts                    # cn() 样式合并\n│   ├── client-api.ts               # ClientAPI 封装\n│   ├── api-utils.ts                # apiFetch() 统一请求\n│   └── webrtc-support.ts           # WebRTC 支持检测\n│\n└── types/\n    └── index.ts                    # 全局类型定义\n```\n\n### 4.2 组件层次结构\n\n```\nRootLayout (layout.tsx)\n  └─ ToastProvider (toast-simple.tsx)\n       └─ HomePageWrapper (Suspense)\n            └─ HomePage\n                 ├─ Hero                           # 页头\n                 ├─ WebRTCUnsupportedModal          # WebRTC 不支持提示\n                 ├─ ConfirmDialog                   # Tab 切换确认\n                 └─ Tabs (5个标签)\n                      │\n                      ├─ [Tab: webrtc] WebRTCFileTransfer\n                      │    ├─ 模式切换 (发送/接收)\n                      │    ├─ [发送模式] WebRTCFileUpload\n                      │    │    ├─ 拖拽上传区域 / 文件列表\n                      │    │    ├─ RoomInfoDisplay (取件码 + 二维码 + 复制)\n                      │    │    ├─ ConnectionStatus (连接状态)\n                      │    │    └─ 传输进度条\n                      │    └─ [接收模式] WebRTCFileReceive\n                      │         ├─ 取件码输入 (6位)\n                      │         ├─ ConnectionStatus (连接状态)\n                      │         └─ 文件列表 + 下载按钮\n                      │\n                      ├─ [Tab: message] WebRTCTextImageTransfer\n                      │    ├─ 模式切换 (发送/接收)\n                      │    ├─ [发送模式] WebRTCTextSender\n                      │    │    ├─ Textarea 编辑器\n                      │    │    ├─ 图片发送按钮\n                      │    │    └─ RoomInfoDisplay\n                      │    └─ [接收模式] WebRTCTextReceiver\n                      │         ├─ 取件码输入\n                      │         └─ 实时文字显示 + 图片接收\n                      │\n                      ├─ [Tab: desktop] DesktopShare\n                      │    ├─ 模式切换 (共享/查看)\n                      │    ├─ [共享模式] WebRTCDesktopSender\n                      │    │    └─ 选择屏幕 → 开始共享\n                      │    └─ [查看模式] WebRTCDesktopReceiver\n                      │         └─ DesktopViewer (video 标签)\n                      │\n                      ├─ [Tab: wechat] WeChatGroup\n                      │    └─ 微信群二维码\n                      │\n                      └─ [Tab: settings] WebRTCSettings\n                           └─ ICE 服务器配置表单\n```\n\n### 4.3 Hooks 架构（核心设计）\n\n前端逻辑层采用 **分层 Hooks 架构**，自底向上组合：\n\n```\n                    ┌─────────────────────────────┐\n                    │    Zustand Store             │  ← 最底层：全局状态\n                    │    (webRTCStore.ts)           │\n                    │    isConnected, isConnecting  │\n                    │    currentRoom, error         │\n                    └──────────────┬──────────────┘\n                                   │\n            ┌──────────────────────┼──────────────────────┐\n            │                      │                      │\n   ┌────────▼────────┐  ┌─────────▼──────────┐  ┌───────▼─────────┐\n   │ StateManager     │  │ DataChannelManager  │  │ TrackManager     │\n   │ (Zustand 封装)    │  │ (消息路由 + 收发)    │  │ (媒体轨道管理)    │\n   └────────┬────────┘  └─────────┬──────────┘  └───────┬─────────┘\n            │                      │                      │\n            └──────────────────────┼──────────────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ ConnectionCore               │  ← WebSocket + PeerConnection\n                    │ (useWebRTCConnectionCore)     │     信令处理 + ICE 交换\n                    └──────────────┬──────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ SharedWebRTCManager          │  ← 整合入口（4合1）\n                    │ (useSharedWebRTCManager)      │     返回统一 WebRTCConnection\n                    └──────────────┬──────────────┘\n                                   │\n          ┌────────────────────────┼────────────────────────┐\n          │                        │                        │\n ┌────────▼────────┐   ┌──────────▼──────────┐   ┌────────▼────────┐\n │ FileTransfer     │   │ TextTransfer         │   │ DesktopShare     │\n │ Business         │   │ Business             │   │ Business         │\n │ (文件分块+校验)    │   │ (实时同步文字)         │   │ (屏幕采集+推流)    │\n └────────┬────────┘   └──────────┬──────────┘   └────────┬────────┘\n          │                        │                        │\n          └────────────────────────┼────────────────────────┘\n                                   │\n                    ┌──────────────▼──────────────┐\n                    │ 业务级组件 (TSX)              │\n                    │ WebRTCFileTransfer.tsx 等     │\n                    └─────────────────────────────┘\n```\n\n#### 4.3.1 连接层 — `useWebRTCConnectionCore`\n\n**职责**：管理 WebSocket 信令连接 + RTCPeerConnection 生命周期。\n\n```\nconnect(code, role)\n    │\n    ├─ 1. WebSocket 连接到 ws://host/api/ws/webrtc?code=XXX&role=YYY\n    │\n    ├─ 2. 收到 \"peer-joined\" → 创建 RTCPeerConnection\n    │      └─ ICE 服务器从 getIceServersConfig() 获取\n    │\n    ├─ 3. Sender 创建 DataChannel → 创建 Offer\n    │      Receiver 等待 ondatachannel\n    │\n    ├─ 4. offer/answer/ice-candidate 通过 WebSocket 交换\n    │\n    ├─ 5. ICE 连接建立 → DataChannel open → 连接完成\n    │\n    └─ 6. 断开时：发送 disconnection → 清理 PeerConnection\n```\n\n#### 4.3.2 数据通道层 — `useWebRTCDataChannelManager`\n\n**职责**：管理 DataChannel 消息的路由分发。\n\n**核心设计：多 channel 路由**\n\n所有 JSON 消息通过 `channel` 字段路由到不同的业务处理器：\n\n```typescript\n// 消息格式\n{\n  channel: \"file-transfer\" | \"text-transfer\" | \"desktop-share\",\n  type: \"具体消息类型\",\n  payload: { ... }\n}\n```\n\n```\nDataChannel.onmessage\n    │\n    ├─ typeof data === 'string' → JSON.parse\n    │   ├─ channel === 'file-transfer' → fileTransferHandler\n    │   ├─ channel === 'text-transfer' → textTransferHandler\n    │   └─ channel === 'desktop-share' → desktopShareHandler\n    │\n    └─ typeof data === ArrayBuffer → 二进制数据\n        └─ 优先路由到 'file-transfer' handler（文件块数据）\n```\n\n**注意**：三个功能（文件/文字/桌面）共享同一个 DataChannel（`shared-channel`），而非各自创建独立通道。\n\n#### 4.3.3 轨道管理层 — `useWebRTCTrackManager`\n\n**职责**：管理 MediaStream 轨道（仅桌面共享使用）。\n\n- `addTrack(track, stream)` — 添加视频/音频轨道\n- `removeTrack(sender)` — 移除轨道\n- `createOfferNow()` — 添加轨道后触发重协商\n- `onTrack` 回调 — 接收远程媒体流\n\n### 4.4 状态管理设计\n\n采用 **三层状态模型**：\n\n| 层级 | 技术 | 用途 | 示例 |\n|------|------|------|------|\n| 全局共享状态 | Zustand | WebRTC 连接状态 | `isConnected`, `currentRoom`, `error` |\n| 组件级状态 | React useState | UI 交互状态 | `selectedFiles`, `mode`, `progress` |\n| 引用状态 | React useRef | 回调/缓冲区/定时器 | `receiveBufferRef`, `messageHandlers`, 防抖 timer |\n\n#### Zustand Store 结构\n\n```typescript\ninterface WebRTCState {\n  // 连接状态\n  isConnected: boolean;       // 总体连接状态\n  isConnecting: boolean;      // 正在连接中\n  isWebSocketConnected: boolean;  // WebSocket 层连接\n  isPeerConnected: boolean;   // PeerConnection 层连接\n\n  // 错误状态\n  error: string | null;\n  canRetry: boolean;\n\n  // 房间信息\n  currentRoom: { code: string; role: 'sender' | 'receiver' } | null;\n\n  // Actions\n  updateState(partial: Partial<WebRTCState>): void;\n  setCurrentRoom(room: { code: string; role: string } | null): void;\n  resetToInitial(): void;\n}\n```\n\n### 4.5 文件传输详细设计\n\n#### 4.5.1 DataChannel 消息协议\n\n**file-transfer channel 消息类型：**\n\n| 方向 | type | payload | 说明 |\n|------|------|---------|------|\n| S→R | `file-list` | `FileInfo[]` | 发送方文件列表（实时同步） |\n| R→S | `file-request` | `{ fileId, fileName }` | 接收方请求下载 |\n| S→R | `file-metadata` | `{ id, name, size, type }` | 文件元信息 |\n| S→R | `file-chunk-info` | `{ fileId, chunkIndex, totalChunks, checksum }` | 块元信息（含 CRC32） |\n| S→R | *(二进制)* | `ArrayBuffer` (≤256KB) | 文件块数据 |\n| R→S | `file-chunk-ack` | `{ fileId, chunkIndex, success, checksum }` | 块确认（含校验结果） |\n| S→R | `file-complete` | `{ fileId }` | 文件传输完成 |\n\n#### 4.5.2 传输参数\n\n| 参数 | 值 | 说明 |\n|------|-----|------|\n| 块大小 | 256 KB | 每个 chunk 的最大字节数 |\n| 最大重试 | 5 次 | 单个 chunk 校验失败后的重试上限 |\n| 退避策略 | 指数退避 | 重试间隔指数增长 |\n| 校验算法 | CRC32 | 每个 chunk 独立校验 |\n| 流控 | 自适应 | 根据平均传输速度动态调整发送间隔 |\n| DataChannel 配置 | `ordered: true, maxRetransmits: 3` | 有序可靠传输 |\n\n#### 4.5.3 完整传输时序\n\n```\n发送方                              信令服务器                           接收方\n  │                                     │                                 │\n  │  1. POST /api/create-room           │                                 │\n  │ ──────────────────────────────────► │                                 │\n  │ ◄── {success: true, code: \"AB12CD\"} │                                 │\n  │                                     │                                 │\n  │  2. WS connect (role=sender)        │                                 │\n  │ ═══════════════════════════════════► │                                 │\n  │                                     │                                 │\n  │                                     │  3. GET /api/room-info?code=... │\n  │                                     │ ◄────────────────────────────── │\n  │                                     │ ── {success, status} ─────────► │\n  │                                     │                                 │\n  │                                     │  4. WS connect (role=receiver)  │\n  │                                     │ ◄═══════════════════════════════ │\n  │                                     │                                 │\n  │  5. ◄── peer-joined ──             │  ── peer-joined ──►             │\n  │                                     │                                 │\n  │  6. 创建 PeerConnection + DataChannel                                 │\n  │  7. 创建 SDP Offer                                                    │\n  │  ═══ offer ═══════════════════════► │ ═══ offer ═══════════════════► │\n  │                                     │                                 │\n  │                                     │  8. 创建 PeerConnection         │\n  │                                     │  9. 设置 Remote/Local SDP       │\n  │  ◄═══ answer ══════════════════════ │ ◄═══ answer ═════════════════ │\n  │                                     │                                 │\n  │  10. ICE Candidate 交换 ◄═══════════╋══════════════════════════════► │\n  │                                     │                                 │\n  │  ══════════ DataChannel OPEN ═══════╪══════════════════════════════ │\n  │                                     │                                 │\n  │  11. file-list ─────────────────────┼────────────────────────────────► │\n  │      [{id, name, size, type}, ...]  │                                 │\n  │                                     │                                 │\n  │  ◄─────────────────────────────────┼──── 12. file-request ────────── │\n  │      {fileId, fileName}             │                                 │\n  │                                     │                                 │\n  │  13. file-metadata ────────────────┼────────────────────────────────► │\n  │      {id, name, size, type}         │                                 │\n  │                                     │                                 │\n  │  ╔══ 循环每个 chunk ═══════════════╪════════════════════════════════╗ │\n  │  ║  14. file-chunk-info ───────────┼──────────────────────────────► ║ │\n  │  ║      {fileId, chunkIndex,        │                              ║ │\n  │  ║       totalChunks, checksum}     │                              ║ │\n  │  ║                                  │                              ║ │\n  │  ║  15. [ArrayBuffer 256KB] ───────┼──────────────────────────────► ║ │\n  │  ║                                  │                              ║ │\n  │  ║  ◄───────────────────────────────┼── 16. file-chunk-ack ─────── ║ │\n  │  ║      {fileId, chunkIndex,        │      success, checksum}      ║ │\n  │  ║                                  │                              ║ │\n  │  ║  [如果 success=false, 指数退避重试，最多5次]                      ║ │\n  │  ╚══════════════════════════════════╪══════════════════════════════╝ │\n  │                                     │                                 │\n  │  17. file-complete ────────────────┼────────────────────────────────► │\n  │      {fileId}                       │                                 │\n  │                                     │                   18. 组装 Blob  │\n  │                                     │                       → File   │\n  │                                     │                       → 下载    │\n```\n\n#### 4.5.4 文件列表同步机制\n\n发送方选择文件后，文件列表会 **实时同步** 到接收方：\n\n```\n发送方文件变更 → useFileListSync hook\n    │\n    ├─ 150ms 防抖（debounce）\n    │\n    ├─ 比对新旧文件列表（避免无变更的冗余同步）\n    │\n    └─ 通过 DataChannel 发送 file-list 消息\n        │\n        └─ 接收方更新 UI 展示文件列表\n```\n\n### 4.6 文字传输详细设计\n\n#### 4.6.1 DataChannel 消息协议\n\n**text-transfer channel 消息类型：**\n\n| 方向 | type | payload | 说明 |\n|------|------|---------|------|\n| S→R | `text-sync` | `{ text: string }` | 实时文本内容同步 |\n| 双向 | `text-typing` | `{ typing: boolean }` | 打字状态指示 |\n\n#### 4.6.2 实时同步机制\n\n```\n发送方 Textarea 输入\n    │\n    ├─ onChange → handleTextInputChange()\n    │\n    ├─ 调用 textTransfer.sendTextSync(text)\n    │   └─ DataChannel 发送 { channel: \"text-transfer\", type: \"text-sync\", payload: { text } }\n    │\n    ├─ 同时发送 text-typing: { typing: true }\n    │   └─ 1秒后自动发送 text-typing: { typing: false }\n    │\n    └─ 接收方\n        ├─ onTextSync 回调 → 更新显示文本\n        └─ onTyping 回调 → 显示 \"对方正在输入...\" 动画\n```\n\n**图片传输**：复用文件传输的 `file-transfer` channel，图片作为文件发送。\n\n### 4.7 桌面共享详细设计\n\n```\n发送方\n    │\n    ├─ navigator.mediaDevices.getDisplayMedia({ video: true, audio: true })\n    │\n    ├─ 获取 MediaStream → 提取 video/audio Track\n    │\n    ├─ trackManager.addTrack(track, stream)\n    │   └─ peerConnection.addTrack()\n    │\n    ├─ trackManager.createOfferNow()  ← 触发 SDP 重协商\n    │   └─ 新 Offer → WebSocket → 对方 → Answer → WebSocket → 本方\n    │\n    └─ 接收方\n        ├─ ontrack 事件 → 获取远程 MediaStream\n        └─ <video> 元素播放 → DesktopViewer 组件\n```\n\n### 4.8 ICE 配置设计\n\n#### 默认 STUN 服务器\n\n```typescript\nconst DEFAULT_ICE_SERVERS = [\n  { urls: 'stun:stun.easyvoip.com:3478' },\n  { urls: 'stun:stun.miwifi.com:3478' },\n  { urls: 'stun:stun.l.google.com:19302' },\n  { urls: 'stun:stun1.l.google.com:19302' },\n  { urls: 'stun:global.stun.twilio.com:3478' },\n];\n```\n\n#### 用户自定义\n\n- 通过 WebRTCSettings 界面添加 STUN/TURN 服务器\n- 配置持久化到 `localStorage`（key: `webrtc-ice-config`）\n- `getIceServersConfig()` 导出给非 React 代码使用\n- 配置变更后提示用户断开重连\n\n### 4.9 环境适配设计\n\n```\n                    ┌─────────────────────────────────────────┐\n                    │           config.ts 环境判断              │\n                    │                                         │\n                    │  isStaticMode = Next.js 构建输出判断      │\n                    │  isDev = NODE_ENV === 'development'      │\n                    └────────────────┬────────────────────────┘\n                                     │\n                   ┌─────────────────┼─────────────────┐\n                   │                 │                 │\n          ┌────────▼────────┐ ┌─────▼──────┐ ┌───────▼───────┐\n          │ 开发模式          │ │ SSG 静态模式 │ │ 嵌入 Go 模式   │\n          │ (yarn dev)       │ │ (build:ssg) │ │ (go run)      │\n          │                  │ │             │ │               │\n          │ API:             │ │ API:        │ │ API:          │\n          │ /api/* → Next.js │ │ 直连 Go     │ │ 同源 /api/*   │\n          │ API Route 代理   │ │ 后端地址     │ │               │\n          │                  │ │             │ │               │\n          │ WS:              │ │ WS:         │ │ WS:           │\n          │ ws://localhost   │ │ 当前域名     │ │ 当前域名       │\n          │ :8080/...        │ │ ws[s]://    │ │ ws[s]://      │\n          └──────────────────┘ └─────────────┘ └───────────────┘\n```\n\n#### URL 动态计算逻辑\n\n```typescript\n// API URL\ngetApiUrl() → \n  开发模式 ? '/api'  (通过 Next.js API Route 代理到 GO_BACKEND_URL)\n  静态模式 ? getDirectBackendUrl() + '/api'  (直连 Go 后端)\n\n// WebSocket URL\ngetWsUrl() →\n  开发模式 ? 'ws://localhost:8080'  (直连 Go 后端 WS)\n  静态模式 ? 'ws[s]://' + window.location.host  (同源)\n```\n\n### 4.10 URL 路由设计\n\n前端通过 URL 参数控制功能和模式：\n\n```\n/?type=webrtc&mode=send            → 文件传输-发送模式\n/?type=webrtc&mode=receive         → 文件传输-接收模式\n/?type=webrtc&mode=receive&code=ABC123  → 自动填入取件码\n/?type=message&mode=send           → 文字消息-发送模式\n/?type=desktop&mode=send           → 桌面共享-共享模式\n```\n\n**URL 参数映射**：\n- `type`: `webrtc` | `message` | `desktop` | `wechat` | `settings` → 对应 Tab\n- `mode`: `send` | `receive` → 发送/接收子模式\n- `code`: 6 位取件码 → 自动填入并可自动加入房间\n\n---\n\n## 五、关键设计决策\n\n### 5.1 为什么用纯 P2P？\n\n- **隐私**：文件内容不经服务器，用户数据零留存\n- **成本**：服务器仅做信令，带宽成本极低\n- **性能**：局域网环境下可接近网卡极限速度\n\n### 5.2 为什么共享单个 DataChannel？\n\n三个功能（文件/文字/桌面控制）共享同一个 `shared-channel`，通过 JSON `channel` 字段路由：\n\n- **简化连接管理**：只需建立一次 P2P 连接\n- **减少信令开销**：无需为每个功能单独协商\n- **统一状态管理**：连接/断开状态全局一致\n\n### 5.3 为什么用 CRC32 + ACK？\n\nWebRTC DataChannel 本身基于 SCTP（配置为 `ordered: true, maxRetransmits: 3`），已有一定可靠性。额外的 CRC32 + ACK 是 **应用层二次保障**：\n\n- 防止极端情况下 SCTP 重传仍失败后的数据损坏\n- 提供块级粒度的错误恢复（只重传失败的块，无需从头开始）\n- 接收方独立验证数据完整性\n\n### 5.4 为什么 256KB 块大小？\n\n- WebRTC DataChannel 最大消息大小约 256KB（不同浏览器有差异）\n- 小块有利于进度反馈的精度\n- 小块降低单次重传的代价\n\n### 5.5 为什么关闭 React Strict Mode？\n\n```typescript\n// next.config.ts\nreactStrictMode: false\n```\n\nReact 18+ Strict Mode 会在开发环境 **双重调用** effect，导致：\n- WebSocket 连接被创建两次\n- PeerConnection 生命周期混乱\n- DataChannel 状态不一致\n\n---\n\n## 六、部署模式\n\n### 模式一：开发模式\n\n```bash\n# 终端 1：Go 后端\ngo run cmd/main.go          # :8080\n\n# 终端 2：Next.js 前端\ncd chuan-next && yarn dev   # :3000 (turbopack)\n```\n\n前端 API 调用链：`浏览器 → localhost:3000/api/* → Next.js API Route → localhost:8080/api/*`\n\n### 模式二：生产模式（Go 嵌入前端）\n\n```bash\ncd chuan-next && yarn build:ssg    # 输出到 out/\ncp -r out/ ../internal/web/frontend/  # 复制到 Go embed 目录\ngo build -o chuan cmd/*.go         # 编译\n./chuan                            # :8080 同时提供 API + 前端\n```\n\n所有请求都由 Go 单进程处理，前端静态文件通过 `go:embed` 嵌入二进制。\n\n---\n\n## 七、安全与限制\n\n| 维度 | 现状 |\n|------|------|\n| 加密 | WebRTC 自带 DTLS 加密，P2P 数据全程加密传输 |\n| 认证 | 无用户认证，仅靠 6 位取件码 |\n| 房间安全 | 取件码空间 34^6 ≈ 15 亿，暴力破解概率低 |\n| 数据留存 | 服务器零数据留存，所有文件仅在浏览器内存/临时存储 |\n| 并发限制 | 每房间最多 2 人，单进程内存管理 |\n| 文件大小 | 受限于浏览器内存（大文件需接收方有足够内存组装 Blob）|\n| NAT 穿透 | 依赖 STUN 服务器，对称 NAT 需 TURN 服务器（默认未配置）|\n</yakaai_skills>"}