Private
Public Access
1
0
Files
u-desk/docs/04-功能迭代/GO-DESK-7.u-fs-agent远程文件服务/设计文档.md

48 KiB
Raw Blame History

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

目录


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/agentHTTP 服务)共存于同一仓库
  • 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 HandlerHTTP → 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/deskcmd/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)

{
  "code": 0,
  "message": "ok",
  "data": {
    "status": "up",
    "timestamp": "2026-04-26T10:30:00Z",
    "version": "0.1.0"
  }
}

GET /api/v1/info

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

请求:无参数

响应 (200)

{
  "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)

{
  "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)

{
  "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)

{
  "code": 0,
  "message": "ok",
  "data": {
    "content": "package main\n\nimport \"fmt\"\n...",
    "size": 2048,
    "encoding": "utf-8"
  }
}

错误 (413):文件超过 10MB 限制

{
  "code": 41301,
  "message": "文件过大 (15.2 MB),超过读取上限 (10 MB)"
}

PUT /api/v1/files/*/content

写入文件文本内容。

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

请求体

{
  "content": "package main\n\n..."
}

响应 (200)

{
  "code": 0,
  "message": "ok",
  "data": {
    "path": "/home/user/project/main.go",
    "name": "main.go",
    "size": 2048,
    "is_dir": false
  }
}

POST /api/v1/files/*

新建文件或目录。

路径参数* -- 目标路径

请求体

{
  "type": "file"    // "file" | "dir"
}

响应 (201)

{
  "code": 0,
  "message": "created",
  "data": {
    "path": "/home/user/project/newfile.txt",
    "name": "newfile.txt",
    "size": 0,
    "is_dir": false
  }
}

DELETE /api/v1/files/*

删除文件或目录。

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

响应 (200)

{
  "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/*

重命名文件或目录。

路径参数* -- 原路径

请求体

{
  "new_name": "renamed.txt"    // 仅新名称(非完整路径)
}

响应 (200)

{
  "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 编码上传二进制文件内容(图片等非文本文件)。

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

请求体

{
  "base64_content": "iVBORw0KGgoAAAANSUhEUgAA...",
  "filename": "screenshot.png"
}

响应 (201)

{
  "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)

{
  "code": 0,
  "message": "ok",
  "data": {
    "detected_type": "image/png",
    "extension": ".png",
    "confidence": "high"
  }
}

4.4 系统信息端点

GET /api/v1/system/common-paths

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

响应 (200)

{
  "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)

{
  "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 响应信封:

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 实例 & 注册全局中间件
  │     ├─ Recoverypanic 恢复)
  │     ├─ RequestLogger结构化请求日志
  │     ├─ CORS跨域配置
  │     └─ AuthBearer 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)

配置结构

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 结构

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 中间件

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)

// 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 接口进行,前端业务组件不关心底层实现:

// 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 数据模型

// 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 实例:

// 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 设计

// 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支持过期时间和刷新
  • 可对接外部 IdPOAuth2/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 编译

# 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 远端部署

方式一:直接运行

# 上传编译好的二进制和配置文件
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 推荐)

# /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
sudo systemctl enable u-fs-agent
sudo systemctl start u-fs-agent
sudo systemctl status u-fs-agent

方式三Docker

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 隧道示例(推荐开发阶段使用):

# 本地执行:将远端的 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 完整配置

# 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 远程文件服务