# 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}`,透传响应。 **用途**:前端 `` / `