# u-fs-agent 远程文件服务 - 设计文档

## 目录

- [1. 项目概述](#1-项目概述)
- [2. 架构设计](#2-架构设计)
  - [2.1 整体架构](#21-整体架构)
  - [2.2 单仓库双入口模式](#22-单仓库双入口模式)
  - [2.3 共享包零拷贝](#23-共享包零拷贝)
  - [2.4 数据流](#24-数据流)
- [3. 技术决策](#3-技术决策)
- [4. API 端点设计（MVP v0.1）](#4-api-端点设计mvp-v01)
  - [4.1 概览](#41-概览)
  - [4.2 系统端点](#42-系统端点)
  - [4.3 文件操作端点](#43-文件操作端点)
  - [4.4 系统信息端点](#44-系统信息端点)
  - [4.5 代理端点](#45-代理端点)
  - [4.6 统一响应格式](#46-统一响应格式)
  - [4.7 错误码规范](#47-错误码规范)
- [5. 后端实现细节](#5-后端实现细节)
  - [5.1 Agent 入口 (cmd/agent/main.go)](#51-agent-入口-cmdagentmaingo)
  - [5.2 配置管理 (internal/agent/config)](#52-配置管理-internalagentconfig)
  - [5.3 Handler 层 (internal/agent/handler)](#53-handler-层-internalagenthandler)
  - [5.4 中间件 (internal/agent/middleware)](#54-中间件-internalagentmiddleware)
  - [5.5 响应模型 (internal/agent/model)](#55-响应模型-internalagentmodel)
- [6. 前端改造方案](#6-前端改造方案)
  - [6.1 Transport 适配器模式](#61-transport-适配器模式)
  - [6.2 ConnectionProfile 数据模型](#62-connectionprofile-数据模型)
  - [6.3 连接管理器](#63-连接管理器)
  - [6.4 Pinia Store 设计](#64-pinia-store-设计)
  - [6.5 Toolbar 连接指示器](#65-toolbar-连接指示器)
  - [6.6 文件变更清单](#66-文件变更清单)
- [7. 目录结构](#7-目录结构)
- [8. 分阶段实施计划](#8-分阶段实施计划)
  - [8.1 Wave 1: 核心连通（MVP）](#81-wave-1-核心连通mvp)
  - [8.2 Wave 2: 编辑体验增强](#82-wave-2-编辑体验增强)
  - [8.3 Wave 3: 增强功能](#83-wave-3-增强功能)
  - [8.4 Wave 4: 用户间文件共享](#84-wave-4-用户间文件共享)
- [9. 安全设计](#9-安全设计)
- [10. 部署方案](#10-部署方案)
- [11. 配置说明](#11-配置说明)
- [12. 测试策略](#12-测试策略)

---

## 1. 项目概述

### 1.1 背景

u-desk 当前是一个基于 Wails 的桌面应用，所有文件操作通过 Wails Bindings 在本地执行。随着使用场景扩展，用户需要能够访问和管理远程服务器上的文件 -- 例如开发机、测试环境、生产服务器的文件系统。

### 1.2 目标

在 u-desk 同一 Git 仓库中新增 `cmd/agent` 入口，编译为独立的 HTTP 服务程序 `u-fs-agent`，部署到远端服务器后，本地 u-desk 通过 REST API 远程操作远端文件系统。

**核心价值**：

| 维度 | 本地模式 | 远程模式 |
|------|---------|---------|
| 访问范围 | 本地文件系统 | 远程服务器文件系统 |
| 通信方式 | Wails Bindings（进程内） | REST HTTP（网络） |
| 文件系统 | filesystem 包直接调用 | filesystem 包通过 HTTP 代理调用 |
| 用户体验 | 无感知切换 | Transport 抽象层自动路由 |

### 1.3 核心原则

- **单仓库双入口**：`cmd/desk`（桌面端）和 `cmd/agent`（HTTP 服务）共存于同一仓库
- **filesystem 零拷贝**：`internal/filesystem` 包被两个入口共享引用，无代码复制
- **前端无感知**：通过 Transport 抽象层，前端不关心底层是本地还是远程
- **渐进式增强**：MVP 先跑通核心链路，后续 Wave 迭代增强能力

---

## 2. 架构设计

### 2.1 整体架构

```
┌─────────────────────────────────────────────────────────────────────┐
│                         u-desk 前端 (Vue 3)                          │
│                                                                     │
│  ┌──────────────┐  ┌──────────────┐  ┌───────────────────────────┐  │
│  │  FileExplorer │  │   Editor     │  │    ConnectionManager      │  │
│  └──────┬───────┘  └──────┬───────┘  └───────────┬───────────────┘  │
│         │                 │                      │                  │
│         └────────┬────────┘                      │                  │
│                  ▼                               │                  │
│          ┌───────────────┐                       │                  │
│          │  Transport    │◄──────────────────────┘                  │
│          │  (抽象接口)    │  根据 ConnectionProfile 选择实现          │
│          └───────┬───────┘                                          │
└──────────────────┼──────────────────────────────────────────────────┘
                   │              ┌──────────────────────────────┐
           ┌───────┴───────┐      │                              │
           ▼               ▼      ▼                              │
┌──────────────────┐  ┌────────────────────┐                     │
│ WailsTransport   │  │ HttpTransport      │   REST over HTTP    │
│ (本地 Wails 调用) │  │ (远程 HTTP 调用)   │   Bearer Token      │
└────────┬─────────┘  └────────┬───────────┘                     │
         │                     │                                  │
         ▼                     ▼                                  │
┌──────────────────┐  ┌──────────────────────────────────────────┐ │
│  cmd/desk       │  │        cmd/agent (u-fs-agent)             │ │
│  Wails 桌面端    │  │        Echo v4 HTTP 服务                 │ │
│  main.go        │  │        main.go                           │ │
└────────┬─────────┘  └─────────────────┬────────────────────────┘ │
         │                              │                          │
         └──────────────┬───────────────┘                          │
                        ▼                                          │
            ┌───────────────────────┐                               │
            │  internal/filesystem  │ ◄── 共享包，零拷贝             │
            │  (17 个 Go 源文件)     │                               │
            │                       │                               │
            │  ┌──────────────────┐  │                               │
            │  │FileSystemService │  │                               │
            │  ├──────────────────┤  │                               │
            │  │PathValidator     │  │                               │
            │  │FileTypeManager   │  │                               │
            │  │AssetHandler      │  │                               │
            │  │AuditLogger       │  │                               │
            │  │RecycleBin        │  │                               │
            │  │FileLockChecker   │  │                               │
            │  └──────────────────┘  │                               │
            └───────────────────────┘                               │
                        │                                           │
                        ▼                                           │
              ┌─────────────────┐                                   │
              │  操作系统文件系统  │ ◄── 本地磁盘 / 远程服务器磁盘      │
              └─────────────────┘                                   │
└──────────────────────────────────────────────────────────────────┘
```

### 2.2 单仓库双入口模式

```
u-desk/
├── cmd/
│   ├── desk/
│   │   └── main.go          # Wails 桌面端入口（已有）
│   └── agent/
│       └── main.go          # u-fs-agent HTTP 服务入口（新增）
│
├── internal/
│   ├── filesystem/          # ★ 共享包：两个入口都引用
│   │   ├── service.go       #   FileSystemService 核心
│   │   ├── config.go        #   安全/性能/功能配置
│   │   ├── path_validator.go #  路径安全验证
│   │   ├── filetype_manager.go # 文件类型管理
│   │   ├── asset_handler.go #  静态资源处理
│   │   ├── audit_log.go     #  审计日志
│   │   ├── recycle_bin.go   #  回收站
│   │   ├── file_lock.go     #  文件锁检查
│   │   ├── content_detector.go # 内容类型检测
│   │   ├── zip.go / zip_helper.go # ZIP 操作
│   │   ├── directory_stats.go # 目录统计
│   │   ├── fs.go            #  文件系统工具函数
│   │   ├── errors.go        #  错误定义
│   │   ├── constants.go     #  常量定义
│   │   └── logger.go        #  日志工具
│   │
│   ├── agent/               # ★ Agent 专用模块（仅 agent 入口使用）
│   │   ├── config/          #   Agent 配置加载
│   │   ├── handler/         #   Echo Handler（HTTP → FileSystemService）
│   │   ├── middleware/       #   认证/CORS/恢复中间件
│   │   └── model/           #   API 请求/响应模型
│   │
│   ├── app/                 # 桌面端 App 结构体（仅 desk 使用）
│   ├── common/              # 公共工具
│   ├── service/             # 业务服务
│   ├── storage/             # 存储层
│   └── system/              # 系统信息
│
├── frontend/src/                 # 前端 Vue 3 项目
│   └── api/
│       ├── transport.ts     #   ★ Transport 接口定义
│       ├── wails-transport.ts # ★ WailsTransport 实现
│       ├── http-transport.ts  # ★ HttpTransport 实现
│       ├── connection-manager.ts # ★ 连接管理器
│       ├── types.ts         #   类型定义
│       └── index.ts         #   统一导出
│
└── configs/
    └── agent.yaml           # ★ Agent 运行时配置
```

**关键约束**：

- `internal/filesystem` **不导入**任何 Wails 或 Echo 依赖，保持纯净
- `internal/agent` **只导入** `internal/filesystem`，不导入 `internal/app`
- `cmd/desk` 和 `cmd/agent` 编译产物完全独立，可分别部署

### 2.3 共享包零拷贝

filesystem 包的 17 个源文件在编译时被两个二进制文件共同链接，不存在运行时代码复制：

```
编译流程:

  go build ./cmd/desk  →  u-desk.exe  (包含 filesystem 全部代码)
  go build ./cmd/agent →  u-fs-agent  (包含 filesystem 全部代码)

  两者独立编译，各自包含一份 filesystem 的机器码
  源码层面零拷贝 -- 同一套 .go 文件，import 路径一致
```

**filesystem 包对外暴露的核心能力**（Agent Handler 直接调用的方法）：

| 方法 | 说明 | Agent 端点映射 |
|------|------|---------------|
| `ListDir(path)` | 列出目录内容 | `GET /api/v1/files/*` |
| `GetFileInfo(path)` | 获取文件/目录信息 | `GET /api/v1/files/*?action=stat` |
| `ReadFile(path)` | 读取文件内容 | `GET /api/v1/files/*/content` |
| `WriteFile(path, content)` | 写入文件内容 | `PUT /api/v1/files/*/content` |
| `CreateFile(path)` | 创建空文件 | `POST /api/v1/files/*` (type=file) |
| `CreateDir(path)` | 创建目录 | `POST /api/v1/files/*` (type=dir) |
| `DeletePath(path)` | 删除文件/目录 | `DELETE /api/v1/files/*` |
| `RenamePath(old, new)` | 重命名 | `PATCH /api/v1/files/*` |
| `SaveBase64File(path, b64)` | Base64 写入 | `POST /api/v1/files/*/upload` |
| `DetectContentType(data)` | 内容类型检测 | `GET /api/v1/files/*/detect-type` |
| `StartLocalFileServer()` | 启动内置文件服务器 | 代理端点内部调用 |

### 2.4 数据流

**本地模式数据流**：

```
用户点击文件 → Vue Component → WailsTransport.ReadFile(path)
  → window.go.main.App.ReadFile(path)  // Wails Binding
  → app.go → filesystem.FileSystemService.ReadFile(path)
  → os.ReadFile(path) → 返回内容给前端
```

**远程模式数据流**：

```
用户点击文件 → Vue Component → HttpTransport.readFile(path)
  → fetch(`http://remote:9876/api/v1/files/${path}/content`, { headers: { Authorization: 'Bearer xxx' } })
  → [网络] → u-fs-agent Echo Router → handler.ReadFile
  → filesystem.FileSystemService.ReadFile(path)
  → os.ReadFile(path) → JSON Response → [网络] → 前端解析展示
```

---

## 3. 技术决策

| 决策项 | 选择 | 备选方案 | 选择理由 |
|--------|------|---------|---------|
| 工程组织 | 单仓库双入口 | 独立仓库 / monorepo | filesystem 包零拷贝，避免同步噩梦；同一仓库便于 CI 统一管理 |
| HTTP 框架 | Echo v4 | Gin / Chi / net/http | 已在项目依赖树中；API 定义简洁优雅；中间件生态成熟 |
| 通信协议 | REST + JSON | gRPC / WebSocket | CRUD 天然映射 HTTP 动词；前端 fetch 原生支持；调试友好（curl 即可） |
| 认证方式 | Bearer Token (MVP) | JWT / OAuth2 / mTLS | MVP 最简实现；Token 可配置为空（内网信任场景）；后续 Wave 4 升级 JWT |
| 前端架构 | Transport 接口 | 条件分支 / 策略模式 | Composable 设计，业务代码零感知；新增协议只需实现接口 |
| 配置格式 | YAML | JSON / TOML / 环境变量 | 可读性好；支持注释；Go 生态 yaml.v3 成熟 |
| 文件上传 | Base64 JSON | multipart/form-data | MVP 简化实现；与现有 Wails Base64 接口对齐；Wave 2 升级 multipart |
| 内嵌文件服务器 | 复用 AssetHandler | 独立代理服务 | 复用现有 8073 端口逻辑；HTML 预览代理无缝衔接 |

---

## 4. API 端点设计（MVP v0.1）

### 4.1 概览

| Method | Path | 说明 | Handler |
|--------|------|------|---------|
| GET | `/api/v1/ping` | 健康检查 | Ping |
| GET | `/api/v1/info` | Agent 信息 | Info |
| GET | `/api/v1/files/*` | 列目录 / 文件信息 | ListOrStat |
| GET | `/api/v1/files/*/content` | 读文件内容 | ReadFile |
| PUT | `/api/v1/files/*/content` | 写文件内容 | WriteFile |
| POST | `/api/v1/files/*` | 新建文件/目录 | Create |
| DELETE | `/api/v1/files/*` | 删除 | Delete |
| PATCH | `/api/v1/files/*` | 重命名 | Rename |
| POST | `/api/v1/files/*/upload` | Base64 上传 | Upload |
| GET | `/api/v1/files/*/detect-type` | 内容类型检测 | DetectType |
| GET | `/api/v1/system/common-paths` | 常用路径列表 | CommonPaths |
| GET | `/api/v1/system/drives` | 磁盘分区列表 | Drives |
| GET | `/api/v1/proxy/localfs/*` | 文件服务器代理 | FileServerProxy |
| GET | `/api/v1/proxy/html-preview` | HTML 预览代理 | HTMLPreviewProxy |

**Base URL**: `http://{host}:{port}/api/v1`
**默认端口**: `9876`

### 4.2 系统端点

#### GET /api/v1/ping

健康检查端点，用于负载均衡探活和客户端连通性检测。

**请求**：无参数

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "status": "up",
    "timestamp": "2026-04-26T10:30:00Z",
    "version": "0.1.0"
  }
}
```

#### GET /api/v1/info

返回 Agent 实例信息，用于前端展示连接状态和版本校验。

**请求**：无参数

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "hostname": "dev-server-01",
    "os": "linux",
    "arch": "amd64",
    "version": "0.1.0",
    "uptime_seconds": 3600,
    "root_path": "/home/user",
    "features": {
      "recycle_bin": true,
      "audit_log": true,
      "zip_extraction": true
    }
  }
}
```

### 4.3 文件操作端点

#### GET /api/v1/files/*

根据查询参数决定行为：默认列目录，`?action=stat` 返回单个文件信息。

**路径参数**：`*` -- 文件或目录路径（URL 编码）

**查询参数**：

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| action | string | `list` | `list`=列目录, `stat`=文件信息 |

**列目录响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": [
    {
      "name": "src",
      "path": "/home/user/project/src",
      "is_dir": true,
      "size": 4096,
      "mod_time": "2026-04-26 10:00:00"
    },
    {
      "name": "main.go",
      "path": "/home/user/project/main.go",
      "is_dir": false,
      "size": 2048,
      "mod_time": "2026-04-26 09:30:00"
    }
  ]
}
```

**文件信息响应 (200, ?action=stat)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "name": "main.go",
    "path": "/home/user/project/main.go",
    "size": 2048,
    "size_str": "2 KB",
    "is_dir": false,
    "mod_time": "2026-04-26 09:30:00",
    "mode": "0644"
  }
}
```

#### GET /api/v1/files/*/content

读取文件文本内容（限制最大 10MB）。

**路径参数**：`*` -- 文件路径

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "content": "package main\n\nimport \"fmt\"\n...",
    "size": 2048,
    "encoding": "utf-8"
  }
}
```

**错误 (413)**：文件超过 10MB 限制
```json
{
  "code": 41301,
  "message": "文件过大 (15.2 MB)，超过读取上限 (10 MB)"
}
```

#### PUT /api/v1/files/*/content

写入文件文本内容。

**路径参数**：`*` -- 目标文件路径

**请求体**：
```json
{
  "content": "package main\n\n..."
}
```

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "path": "/home/user/project/main.go",
    "name": "main.go",
    "size": 2048,
    "is_dir": false
  }
}
```

#### POST /api/v1/files/*

新建文件或目录。

**路径参数**：`*` -- 目标路径

**请求体**：
```json
{
  "type": "file"    // "file" | "dir"
}
```

**响应 (201)**：
```json
{
  "code": 0,
  "message": "created",
  "data": {
    "path": "/home/user/project/newfile.txt",
    "name": "newfile.txt",
    "size": 0,
    "is_dir": false
  }
}
```

#### DELETE /api/v1/files/*

删除文件或目录。

**路径参数**：`*` -- 要删除的路径

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "path": "/home/user/project/oldfile.txt",
    "name": "oldfile.txt",
    "size": 1024,
    "is_dir": false,
    "deleted": true
  }
}
```

#### PATCH /api/v1/files/*

重命名文件或目录。

**路径参数**：`*` -- 原路径

**请求体**：
```json
{
  "new_name": "renamed.txt"    // 仅新名称（非完整路径）
}
```

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "path": "/home/user/project/renamed.txt",
    "name": "renamed.txt",
    "old_path": "/home/user/project/oldfile.txt"
  }
}
```

#### POST /api/v1/files/*/upload

以 Base64 编码上传二进制文件内容（图片等非文本文件）。

**路径参数**：`*` -- 目标文件路径

**请求体**：
```json
{
  "base64_content": "iVBORw0KGgoAAAANSUhEUgAA...",
  "filename": "screenshot.png"
}
```

**响应 (201)**：
```json
{
  "code": 0,
  "message": "created",
  "data": {
    "path": "/home/user/images/screenshot.png",
    "name": "screenshot.png",
    "size": 15360,
    "is_dir": false
  }
}
```

#### GET /api/v1/files/*/detect-type

检测文件内容的实际类型（基于 magic bytes，而非扩展名）。

**路径参数**：`*` -- 文件路径

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "detected_type": "image/png",
    "extension": ".png",
    "confidence": "high"
  }
}
```

### 4.4 系统信息端点

#### GET /api/v1/system/common-paths

返回当前系统的常用路径列表（用户主目录、桌面、文档等）。

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": {
    "home": "/home/user",
    "desktop": "/home/user/Desktop",
    "documents": "/home/user/Documents",
    "downloads": "/home/user/Downloads",
    "temp": "/tmp"
  }
}
```

#### GET /api/v1/system/drives

返回磁盘分区列表（Windows 显示 C:/ D:/ 等，Linux 显示挂载点）。

**响应 (200)**：
```json
{
  "code": 0,
  "message": "ok",
  "data": [
    { "letter": "C:", "path": "C:\\", "total_gb": 256, "free_gb": 80, "fs_type": "NTFS" },
    { "letter": "D:", "path": "D:\\", "total_gb": 512, "free_gb": 200, "fs_type": "NTFS" }
  ]
}
```

### 4.5 代理端点

#### GET /api/v1/proxy/localfs/*

代理访问 Agent 内嵌的本地文件服务器（端口 8073），用于媒体文件预览。

**路径参数**：`*` -- 相对于文件服务器根目录的路径

**行为**：反向代理到 `http://127.0.0.1:8073/{path}`，透传响应。

**用途**：前端 `<img>` / `<video>` 标签可直接引用此端点加载远程媒体资源。

#### GET /api/v1/proxy/html-preview?path=

代理渲染 HTML 文件预览，处理相对路径资源引用问题。

**查询参数**：

| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| path | string | 是 | HTML 文件的绝对路径 |

**行为**：读取指定 HTML 文件，将其中相对路径的资源引用替换为 `/api/v1/proxy/localfs/` 前缀后返回。

### 4.6 统一响应格式

所有 API 端点遵循统一的 JSON 响应信封：

```typescript
interface ApiResponse<T = any> {
  code: number;       // 业务错误码，0 表示成功
  message: string;    // 人类可读消息
  data: T;            // 业务载荷（成功时），错误详情（失败时可选）
  timestamp?: string; // 服务端时间戳（可选）
}
```

**HTTP 状态码与业务 code 映射**：

| HTTP Status | code | 场景 |
|-------------|------|------|
| 200 | 0 | 成功 |
| 201 | 0 | 创建成功 |
| 400 | 400xx | 请求参数错误 |
| 401 | 401xx | 未认证 / Token 无效 |
| 403 | 403xx | 无权限 / 路径禁止访问 |
| 404 | 404xx | 资源不存在 |
| 413 | 41301 | 文件过大 |
| 500 | 500xx | 服务器内部错误 |

### 4.7 错误码规范

```
0xxxx -- 成功
4xxxx -- 客户端错误
  40001 -- 参数缺失
  40002 -- 参数格式错误
  40003 -- 路径格式非法
  40101 -- Token 缺失
  40102 -- Token 无效
  40301 -- 路径越权（目录穿越）
  40302 -- 敏感路径禁止访问
  40303 -- 禁止的文件类型
  40401 -- 文件/目录不存在
  40402 -- 端点不存在
  40901 -- 文件已存在（创建冲突）
  41301 -- 文件超过大小限制
5xxxx -- 服务端错误
  50001 -- 文件系统 I/O 错误
  50002 -- 内部服务异常
```

---

## 5. 后端实现细节

### 5.1 Agent 入口 (cmd/agent/main.go)

Agent 的启动流程：

```
main()
  ├─ 1. 加载配置 (config.Load)
  │     └─ 读取 configs/agent.yaml，缺失则使用默认值
  │
  ├─ 2. 初始化 FileSystemService
  │     └─ filesystem.NewFileSystemService(fsConfig)
  │        ├─ PathValidator（路径安全验证）
  │        ├─ FileTypeManager（文件类型管理）
  │        ├─ AuditLogger（审计日志）
  │        └─ RecycleBin（回收站）
  │
  ├─ 3. 创建 Echo 实例 & 注册全局中间件
  │     ├─ Recovery（panic 恢复）
  │     ├─ RequestLogger（结构化请求日志）
  │     ├─ CORS（跨域配置）
  │     └─ Auth（Bearer Token 认证，可选）
  │
  ├─ 4. 初始化 Handler & 注册路由
  │     └─ handler.New(fsSvc, cfg) → 路由组注册
  │
  ├─ 5. 启动 HTTP 服务器 (goroutine)
  │     └─ e.Start(addr)
  │
  ├─ 6. 启动内嵌文件服务器 (goroutine)
  │     └─ filesystem.StartLocalFileServer() :8073
  │
  └─ 7. 信号监听 & 优雅关闭
        ├─ SIGINT / SIGTERM
        ├─ e.Shutdown(ctx)     -- 10s 超时
        ├─ filesystem.ShutdownLocalFileServer()
        └─ fsSvc.Close(ctx)
```

**关键设计点**：

- HTTP 服务器和内嵌文件服务器并行启动（两个 goroutine）
- 优雅关闭按序执行：先停 Echo（等待请求完成），再停文件服务器，最后释放 FileSystemService
- 所有启动步骤任一失败均 `log.Fatalf` 退出，不做部分降级

### 5.2 配置管理 (internal/agent/config)

**配置结构**：

```go
type Config struct {
    Server      ServerConfig      `yaml:"server"`       // HTTP 服务监听
    Auth        AuthConfig        `yaml:"auth"`         // Bearer Token
    CORS        CORSConfig        `yaml:"cors"`         // 跨域策略
    Log         LogConfig         `yaml:"log"`          // 日志配置
    FileServer FileServerConfig  `yaml:"file_server"`  // 内嵌文件服务器
    Security    SecurityConfig    `yaml:"security"`     // 安全策略
}
```

**配置加载策略**：

1. 优先读取 `configs/agent.yaml`（相对于工作目录）
2. 文件不存在时使用 `Default()` 全部默认值
3. YAML 解析失败返回错误（不静默降级）
4. 支持环境变量覆盖（未来扩展点）

### 5.3 Handler 层 (internal/agent/handler)

Handler 是 HTTP 请求和 FileSystemService 之间的桥梁，职责单一：

```
Handler 职责:
  1. 解析请求参数（路径、查询参数、请求体）
  2. 调用 FileSystemService 对应方法
  3. 将结果包装为统一 ApiResponse 格式
  4. 将 filesystem 错误翻译为 HTTP 状态码 + 业务错误码
```

**Handler 结构**：

```go
type Handler struct {
    fsSvc *filesystem.FileSystemService
    cfg   *config.Config
}

func New(fsSvc *filesystem.FileSystemService, cfg *config.Config) *Handler
```

**各 Handler 方法签名**：

| 方法 | HTTP 方法 | 功能 | FileSystemService 调用 |
|------|----------|------|-----------------------|
| `Ping(c echo.Context)` | GET | 健康检查 | 无（直接返回） |
| `Info(c echo.Context)` | GET | Agent 信息 | os.Hostname + runtime 信息 |
| `ListOrStat(c echo.Context)` | GET | 列目录/文件信息 | ListDir / GetFileInfo |
| `ReadFile(c echo.Context)` | GET | 读文件 | ReadFile |
| `WriteFile(c echo.Context)` | PUT | 写文件 | WriteFile |
| `Create(c echo.Context)` | POST | 新建 | CreateFile / CreateDir |
| `Delete(c echo.Context)` | DELETE | 删除 | DeletePath |
| `Rename(c echo.Context)` | PATCH | 重命名 | RenamePath |
| `Upload(c echo.Context)` | POST | Base64 上传 | SaveBase64File |
| `DetectType(c echo.Context)` | GET | 类型检测 | DetectContentType |
| `CommonPaths(c echo.Context)` | GET | 常用路径 | 系统函数 |
| `Drives(c echo.Context)` | GET | 磁盘列表 | 系统函数 |
| `FileServerProxy(c echo.Context)` | GET | 文件服务代理 | http.ReverseProxy |
| `HTMLPreviewProxy(c echo.Context)` | GET | HTML 预览 | ReadFile + 路径替换 |

### 5.4 中间件 (internal/agent/middleware)

#### Auth 中间件

```go
func Auth(token string) echo.MiddlewareFunc
```

- 从 `Authorization: Bearer <token>` 提取 Token
- 与配置中的 `auth.token` 比较
- 配置为空字符串时跳过认证（内网/开发模式）
- 认证失败返回 `401 {"code": 40102, "message": "无效的认证令牌"}`

#### CORS 中间件

- 使用 Echo 内置 `middleware.CORS`
- 默认允许所有来源 (`*`)，生产环境应限定
- 允许的方法：GET/PUT/POST/DELETE/PATCH/OPTIONS
- 允许的头：Origin/Content-Type/Authorization/Accept

#### Recovery 中间件

- 使用 Echo 内置 `middleware.Recovery`
- panic 时返回 `500 {"code": 50002, "message": "内部服务异常"}`
- 不泄露堆栈信息到响应体

### 5.5 响应模型 (internal/agent/model/response.go)

```go
// Response 统一 API 响应信封
type Response struct {
    Code      int         `json:"code"`
    Message   string      `json:"message"`
    Data      interface{} `json:"data,omitempty"`
    Timestamp time.Time   `json:"timestamp,omitempty"`
}

// Ok 成功响应快捷构造
func Ok(data interface{}) *Response

// Created 创建成功响应快捷构造 (HTTP 201)
func Created(data interface{}) *Response

// BadRequest 错误响应 (400)
func BadRequest(code int, message string) *Response

// Unauthorized 未认证 (401)
func Unauthorized(message string) *Response

// Forbidden 禁止访问 (403)
func Forbidden(code int, message string) *Response

// NotFound 资源不存在 (404)
func NotFound(message string) *Response

// InternalError 内部错误 (500)
func InternalError(message string) *Response
```

---

## 6. 前端改造方案

### 6.1 Transport 接口定义

核心抽象 -- 所有文件操作通过 Transport 接口进行，前端业务组件不关心底层实现：

```typescript
// frontend/src/api/transport.ts

/**
 * 文件信息
 */
export interface FileInfo {
  name: string
  path: string
  is_dir: boolean
  size: number
  size_str?: string
  mod_time?: string
  mode?: string
}

/**
 * 文件操作结果
 */
export interface FileOperationResult {
  path: string
  name: string
  size: number
  size_str?: string
  is_dir: boolean
  mod_time?: string
  mode?: string
  old_path?: string
  deleted?: boolean
}

/**
 * Transport 抽象接口
 * 定义所有文件操作的能力契约，本地和远程实现必须满足此接口
 */
export interface Transport {
  // === 基础文件操作 ===

  /** 列出目录内容 */
  listDir(path: string): Promise<FileInfo[]>

  /** 获取文件/目录详细信息 */
  getFileInfo(path: string): Promise<FileInfo>

  /** 读取文件文本内容 */
  readFile(path: string): Promise<string>

  /** 写入文件文本内容 */
  writeFile(path: string, content: string): Promise<FileOperationResult>

  /** 创建空文件 */
  createFile(path: string): Promise<FileOperationResult>

  /** 创建目录 */
  createDir(path: string): Promise<FileOperationResult>

  /** 删除文件或目录 */
  deletePath(path: string): Promise<FileOperationResult>

  /** 重命名文件或目录 */
  renamePath(oldPath: string, newPath: string): Promise<FileOperationResult>

  /** Base64 上传（二进制文件） */
  saveBase64File(path: string, base64Content: string): Promise<FileOperationResult>

  /** 检测文件内容类型 */
  detectContentType(path: string): Promise<string>

  // === 系统信息 ===

  /** 获取常用路径 */
  getCommonPaths(): Promise<Record<string, string>>

  /** 获取磁盘列表 */
  getDrives(): Promise<Array<{ letter: string; path: string; total_gb: number; free_gb: number }>>

  // === 连接状态 ===

  /** 检查连接是否可用 */
  ping(): Promise<boolean>

  /** 获取连接信息 */
  getInfo(): Promise<Record<string, any>>
}
```

### 6.2 ConnectionProfile 数据模型

```typescript
// frontend/src/api/connection-manager.ts (部分)

/**
 * 连接配置文件
 */
export interface ConnectionProfile {
  /** 唯一标识 */
  id: string
  /** 显示名称 */
  name: string
  /** 连接类型 */
  type: 'local' | 'remote'
  /** 远程连接配置 (type=remote 时必填) */
  remote?: {
    /** Agent 地址，如 http://192.168.1.100:9876 */
    url: string
    /** Bearer Token */
    token: string
    /** 连接超时 (ms) */
    timeout?: number
  }
  /** 是否置顶 */
  pinned?: boolean
  /** 最后连接时间 */
  lastConnectedAt?: string
  /** 创建时间 */
  createdAt: string
}
```

### 6.3 连接管理器

负责创建和管理 Transport 实例：

```typescript
// frontend/src/api/connection-manager.ts

export class ConnectionManager {
  private activeTransport: Transport | null = null
  private profiles: Map<string, ConnectionProfile> = new Map()

  /** 根据配置创建对应的 Transport 实现 */
  createTransport(profile: ConnectionProfile): Transport {
    if (profile.type === 'local' || !profile.remote) {
      return new WailsTransport()
    }
    return new HttpTransport(profile.remote!)
  }

  /** 切换活跃连接 */
  async switchConnection(profileId: string): Promise<void>

  /** 获取当前活跃 Transport */
  getTransport(): Transport

  /** 测试连接是否可达 */
  async testConnection(profile: ConnectionProfile): Promise<boolean>

  /** 保存/更新配置 */
  saveProfile(profile: ConnectionProfile): void

  /** 删除配置 */
  removeProfile(id: string): void
}
```

### 6.4 Pinia Store 设计

```typescript
// frontend/src/stores/connection.ts

export const useConnectionStore = defineStore('connection', () => {
  const manager = ref(new ConnectionManager())
  const activeProfileId = ref<string>('local')
  const isConnected = ref(true)
  const connectionStatus = ref<'connected' | 'disconnected' | 'connecting' | 'error'>('connected')
  const profiles = ref<ConnectionProfile[]>([
    { id: 'local', name: '本地文件系统', type: 'local', createdAt: '' }
  ])

  /** 获取当前 Transport（供所有组件使用） */
  function getTransport(): Transport {
    return manager.value.getTransport()
  }

  /** 切换连接 */
  async switchTo(profileId: string): Promise<void> { ... }

  /** 刷新连接状态 */
  async refreshStatus(): Promise<void> { ... }

  return { activeProfileId, isConnected, connectionStatus, profiles, getTransport, switchTo, refreshStatus }
})
```

### 6.5 Toolbar 连接指示器

在 Toolbar 区域嵌入连接状态指示器，提供快速切换入口：

```
┌─────────────────────────────────────────────────────────┐
│ [📁] [🔍] [⚙]  ...  [● 本地文件系统 ▾]  [➕ 添加连接]  │
│                      ↑                                    │
│                 连接指示器                                 │
│                 ● 绿色 = 已连接                             │
●                 ● 黄色 = 连接中                            │
●                 ● 红色 = 断开                              │
│                 点击展开连接列表                             │
└─────────────────────────────────────────────────────────┘
```

**交互行为**：

- 默认显示「本地文件系统」（绿色圆点）
- 点击展开下拉列表，显示所有已保存的 ConnectionProfile
- 选择远程连接后，自动 ping 测试并切换 Transport
- 连接失败时红点闪烁，提示错误信息
- 右侧「+」按钮打开连接配置弹窗

### 6.6 文件变更清单

**新增文件 (7)**：

| 文件 | 说明 |
|------|------|
| `frontend/src/api/transport.ts` | Transport 接口定义 |
| `frontend/src/api/wails-transport.ts` | WailsTransport 实现（封装现有 Wails 调用） |
| `frontend/src/api/http-transport.ts` | HttpTransport 实现（fetch + Bearer Token） |
| `frontend/src/api/connection-manager.ts` | 连接管理器 |
| `frontend/src/stores/connection.ts` | Pinia Store |
| `frontend/src/components/ConnectionIndicator.vue` | 连接状态指示器组件 |
| `frontend/src/components/ConnectionDialog.vue` | 连接配置弹窗组件 |

**改造文件 (5)**：

| 文件 | 改造内容 |
|------|---------|
| `frontend/src/api/index.ts` | 新增 export transport 相关模块 |
| `frontend/src/api/types.ts` | 新增 ConnectionProfile 等类型 |
| `frontend/src/components/Toolbar.vue` | 嵌入 ConnectionIndicator |
| `frontend/src/views/FileExplorer.vue` | 从直接 Wails 调用改为 Transport 调用 |
| `frontend/src/views/EditorView.vue` | 从直接 Wails 调用改为 Transport 调用 |

---

## 7. 目录结构

```
u-desk/
│
├── cmd/
│   ├── desk/
│   │   └── main.go                # 现有 Wails 桌面端入口
│   │
│   └── agent/
│       └── main.go                # ★ u-fs-agent HTTP 服务入口
│
├── internal/
│   ├── agent/                     # ★ Agent 专用模块
│   │   ├── config/
│   │   │   └── config.go          #   配置加载 (Server/Auth/CORS/Log/Security)
│   │   │
│   │   ├── handler/
│   │   │   └── handler.go         #   Echo Handler 集合
│   │   │                           #   (Ping/Info/ListOrStat/ReadFile/WriteFile/
│   │   │                           #    Create/Delete/Rename/Upload/DetectType/
│   │   │                           #    CommonPaths/Drives/FileServerProxy/HTMLPreviewProxy)
│   │   │
│   │   ├── middleware/
│   │   │   └── auth.go            #   Bearer Token 认证中间件
│   │   │
│   │   └── model/
│   │       └── response.go        #   统一响应结构 (Response/Ok/Created/Error...)
│   │
│   ├── filesystem/                # ★ 共享包（17 文件，双入口共用）
│   │   ├── service.go             #   FileSystemService 核心
│   │   ├── config.go              #   安全/性能/功能配置
│   │   ├── path_validator.go      #   路径安全验证
│   │   ├── filetype_manager.go    #   文件类型管理
│   │   ├── asset_handler.go       #   静态资源处理
│   │   ├── audit_log.go           #   审计日志
│   │   ├── recycle_bin.go         #   回收站
│   │   ├── file_lock.go           #   文件锁检查
│   │   ├── content_detector.go    #   内容类型检测
│   │   ├── zip.go                 #   ZIP 读取
│   │   ├── zip_helper.go          #   ZIP 辅助
│   │   ├── directory_stats.go     #   目录统计
│   │   ├── fs.go                  #   文件系统工具
│   │   ├── errors.go              #   错误定义
│   │   ├── constants.go           #   常量
│   │   └── logger.go              #   日志
│   │
│   ├── app/                       # 桌面端 App（仅 desk 引用）
│   ├── common/                    # 公共工具
│   ├── service/                   # 业务服务
│   ├── storage/                   # 存储层
│   └── system/                    # 系统信息
│
├── configs/
│   └── agent.yaml                 # ★ Agent 运行时配置
│
├── frontend/src/
│   ├── api/
│   │   ├── transport.ts           # ★ Transport 接口定义
│   │   ├── wails-transport.ts     # ★ WailsTransport 实现
│   │   ├── http-transport.ts      # ★ HttpTransport 实现
│   │   ├── connection-manager.ts  # ★ 连接管理器
│   │   ├── types.ts               #   类型定义（扩展）
│   │   ├── system.ts              #   系统API（不变）
│   │   └── index.ts               #   统一导出（扩展）
│   │
│   ├── stores/
│   │   └── connection.ts          # ★ Pinia 连接状态 Store
│   │
│   └── components/
│       ├── ConnectionIndicator.vue # ★ 连接状态指示器
│       └── ConnectionDialog.vue    # ★ 连接配置弹窗
│
├── go.mod
├── go.sum
└── wails.json
```

---

## 8. 分阶段实施计划

### 8.1 Wave 1: 核心连通（MVP）

**目标**：本地 u-desk 能够通过 HTTP 连接到远端 u-fs-agent，完成基本的文件浏览和读写操作。

**范围**：

| 模块 | 任务 | 优先级 |
|------|------|--------|
| 后端 | `cmd/agent/main.go` 入口实现 | P0 |
| 后端 | `internal/agent/config` 配置加载 | P0 |
| 后端 | `internal/agent/handler` 14 个 Handler | P0 |
| 后端 | `internal/agent/middleware/auth` 认证中间件 | P0 |
| 后端 | `internal/agent/model/response` 响应模型 | P0 |
| 后端 | `configs/agent.yaml` 默认配置 | P0 |
| 前端 | `transport.ts` 接口定义 | P0 |
| 前端 | `wails-transport.ts` 本地实现 | P0 |
| 前端 | `http-transport.ts` 远程实现 | P0 |
| 前端 | `connection-manager.ts` 连接管理 | P0 |
| 前端 | `connection.ts` Pinia Store | P1 |
| 前端 | `ConnectionIndicator.vue` 指示器 | P1 |
| 前端 | `Toolbar.vue` 嵌入指示器 | P1 |
| 前端 | `FileExplorer.vue` Transport 化改造 | P0 |
| 前端 | `EditorView.vue` Transport 化改造 | P0 |

**验收标准**：

- [ ] `go build ./cmd/agent` 编译通过，生成 `u-fs-agent` 可执行文件
- [ ] 启动 agent 后 `GET /api/v1/ping` 返回 `{"code":0}`
- [ ] 前端添加远程连接后，文件浏览器能列出远端目录
- [ ] 能在编辑器中打开、编辑、保存远端文件
- [ ] 本地/远程切换时 UI 无卡顿
- [ ] 断网时给出明确错误提示

### 8.2 Wave 2: 编辑体验增强

**目标**：远程编辑体验接近本地，解决网络延迟带来的交互问题。

**范围**：

| 特性 | 说明 |
|------|------|
| ETag 缓存 | 文件读写带 ETag，减少不必要的传输 |
| Multipart 上传 | 替代 Base64，大文件传输效率提升 |
| 写入防抖 (Debounce) | 编辑器输入防抖，减少网络请求数量 |
| 乐观更新 | 先更新 UI 再发请求，回滚失败 |
| 断点续传 | 大文件上传中断后可续传 |
| 并发锁 | 多标签编辑同一文件时的冲突检测 |

### 8.3 Wave 3: 增强功能

**目标**：补齐高级文件操作能力。

| 特性 | 说明 |
|------|------|
| ZIP 远程操作 | 远端打包/解压，流式传输 |
| 回收站远程管理 | 查看/恢复/清空远端回收站 |
| 批量操作 | 批量删除、批量移动 |
| 文件搜索 | 远端文件名/内容搜索（grep） |
| 文件监控 | 远端文件变更实时推送（WebSocket/SSE） |
| 权限信息 | 显示远端文件 owner/group/mode |

### 8.4 Wave 4: 用户间文件共享

**目标**：多用户协作场景支持。

| 特性 | 说明 |
|------|------|
| JWT 认证 | 替换 Bearer Token，支持用户身份 |
| 分享链接 | 生成临时访问链接（含过期时间） |
| WebSocket | 实时协同光标/选区 |
| 操作审计 API | 查询远端操作日志 |
| 配额管理 | 用户/目录级别的存储配额 |
| Web 终端 | 通过 Agent 代理 SSH/WebShell |

---

## 9. 安全设计

### 9.1 认证

**MVP 阶段 (Wave 1)**：

- Bearer Token 静态配置在 `agent.yaml`
- Token 为空时跳过认证（适用于内网/隧道场景）
- Token 存储在前端 ConnectionProfile 中（内存态，不持久化明文）

**后续升级 (Wave 4)**：

- JWT Token，支持过期时间和刷新
- 可对接外部 IdP（OAuth2/OIDC）

### 9.2 路径安全

复用 filesystem 包已有的 `PathValidator` 三层防护：

1. **路径清洗**：消除 `..`、重复分隔符、空白字符
2. **路径规范化**：统一为绝对路径，解析符号链接
3. **路径黑名单**：禁止访问系统敏感目录（`/etc`, `C:\Windows` 等）

### 9.3 传输安全

| 场景 | 推荐方案 |
|------|---------|
| 本地网络 | HTTP 明文（Token 认证） |
| 公网部署 | HTTPS 反向代理（Nginx/Caddy 终止 TLS） |
| 高安全要求 | mTLS 双向证书认证 |

### 9.4 CORS 策略

- 默认允许所有来源（`*`）-- 开发便利
- 生产环境建议限定为 u-desk 前端来源
- 仅允许必要的方法和 Header

### 9.5 审计日志

复用 filesystem 包的 `AuditLogger`，记录所有远程操作的：

- 操作时间戳
- 操作类型（读/写/删/重命名...）
- 操作路径
- 操作结果（成功/失败）
- 来源 IP（从 Echo Context 提取）

---

## 10. 部署方案

### 10.1 编译

```bash
# Linux amd64 (最常见的服务器架构)
GOOS=linux GOARCH=amd64 go build -o u-fs-agent-linux-amd64 ./cmd/agent

# Windows amd64 (远程 Windows 服务器)
GOOS=windows GOARCH=amd64 go build -o u-fs-agent.exe ./cmd/agent

# Linux arm64 (ARM 服务器/树莓派)
GOOS=linux GOARCH=arm64 go build -o u-fs-agent-linux-arm64 ./cmd/agent

# 交叉编译（在 Windows 开发机上编译 Linux 版本）
set GOOS=linux
set GOARCH=amd64
go build -o u-fs-agent ./cmd/agent
```

### 10.2 远端部署

**方式一：直接运行**

```bash
# 上传编译好的二进制和配置文件
scp u-fs-agent configs/agent.yaml user@server:/opt/u-fs-agent/

# SSH 登录后执行
cd /opt/u-fs-agent
chmod +x u-fs-agent
./u-fs-agent
```

**方式二：systemd 服务（Linux 推荐）**

```ini
# /etc/systemd/system/u-fs-agent.service
[Unit]
Description=u-fs-agent Remote File Service
After=network.target

[Service]
Type=simple
User=nobody
WorkingDirectory=/opt/u-fs-agent
ExecStart=/opt/u-fs-agent/u-fs-agent -config /opt/u-fs-agent/agent.yaml
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target
```

```bash
sudo systemctl enable u-fs-agent
sudo systemctl start u-fs-agent
sudo systemctl status u-fs-agent
```

**方式三：Docker**

```dockerfile
FROM golang:1.23-alpine AS builder
COPY . /app
WORKDIR /app
RUN CGO_ENABLED=0 go build -o /u-fs-agent ./cmd/agent

FROM alpine:3.19
COPY --from=builder /u-fs-agent /usr/local/bin/u-fs-agent
COPY configs/agent.yaml /etc/u-fs-agent/agent.yaml
EXPOSE 9876 8073
ENTRYPOINT ["u-fs-agent", "-config", "/etc/u-fs-agent/agent.yaml"]
```

### 10.3 网络打通

| 方案 | 适用场景 | 配置复杂度 |
|------|---------|-----------|
| **公网 IP 直连** | 云服务器有公网 IP | 低 |
| **SSH 隧道** | 无公网 IP，有 SSH 访问权限 | 中 |
| **frp/Tailscale** | NAT 内网穿透 | 中 |
| **Cloudflare Tunnel** | 零配置公网暴露 | 低 |

**SSH 隧道示例**（推荐开发阶段使用）：

```bash
# 本地执行：将远端的 9876 端口映射到本地 19876
ssh -N -L 19876:127.0.0.1:9876 user@remote-server

# u-desk 前端连接地址填写: http://127.0.0.1:19876
```

---

## 11. 配置说明

### agent.yaml 完整配置

```yaml
# u-fs-agent 运行时配置

server:
  port: 9876          # HTTP API 监听端口
  host: "0.0.0.0"     # 监听地址（0.0.0.0 = 所有网卡）

auth:
  token: ""            # Bearer Token（空 = 不需要认证）

cors:
  allowed_origins:     # 允许的跨域来源
    - "*"

log:
  level: info          # 日志级别: debug / info / warn / error
  format: json         # 日志格式: json / text

file_server:
  port: 8073           # 内嵌文件服务器端口（用于媒体预览代理）
  max_file_size: 524288000  # 文件服务器最大文件大小 (500MB)

security:
  allow_symlinks: false       # 是否允许符号链接
  check_system_paths: true    # 是否检查系统敏感路径
```

### 配置优先级

```
命令行参数 > 环境变量 > agent.yaml > 默认值
```

（MVP 阶段仅支持 YAML + 默认值，命令行和环境变量覆盖在 Wave 2 补充）

---

## 12. 测试策略

### 12.1 单元测试

| 模块 | 测试重点 | 覆盖率目标 |
|------|---------|-----------|
| `agent/config` | 配置加载、默认值、YAML 解析 | 90%+ |
| `agent/model/response` | 响应构造、错误码 | 100% |
| `agent/middleware/auth` | Token 校验、空 Token 跳过 | 95%+ |
| `agent/handler` | 参数解析、错误翻译、边界条件 | 85%+ |

### 12.2 集成测试

- 启动完整 agent 进程，使用 `net/http` 发送真实请求
- 覆盖全部 14 个端点的正常和异常路径
- 测试认证中间件的拦截效果
- 测试文件服务器代理的透传正确性

### 12.3 端到端测试

- 前端连接远程 agent，走通完整的浏览-编辑-保存流程
- 模拟网络断开，验证错误处理和重连机制
- 本地/远程切换的功能回归

### 12.4 安全测试

- 路径穿越攻击：`../../../etc/passwd`
- 超大文件上传：超过 10MB 限制
- 无 Token 访问：401 拦截
- CORS 跨域：Origin 验证

---

*文档版本: 1.0*
*创建日期: 2026-04-26*
*所属迭代: GO-DESK-7*
*关联任务: u-fs-agent 远程文件服务*
