1464 lines
48 KiB
Markdown
1464 lines
48 KiB
Markdown
# 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 远程文件服务*
|