178 lines
7.2 KiB
Markdown
178 lines
7.2 KiB
Markdown
# GO-DESK-10: SFTP 直连支持
|
||
|
||
> 版本: v0.5.0 | 状态: 开发中 | 分支: fs-only-v3 → u-desk-sftp
|
||
|
||
## 概述
|
||
|
||
为 U-Desk 新增 **SFTP(SSH File Transfer Protocol)直连模式**,作为第三种文件系统传输方式,与现有的本地模式(Wails IPC)和远程 HTTP Agent 模式并列。
|
||
|
||
用户无需在目标机器部署任何 Agent 服务,仅需 SSH 账号即可直接浏览和操作远程 Linux 服务器的文件系统。
|
||
|
||
## 架构设计
|
||
|
||
```
|
||
FsTransport (interface)
|
||
├── WailsTransport → Wails IPC → FileSystemService (本地 os)
|
||
├── HttpTransport → HTTP REST → u-fs-agent (远程部署)
|
||
└── SftpTransport [NEW]→ Wails IPC → SftpService → pkg/sftp (SSH/SFTP)
|
||
```
|
||
|
||
**核心原则**:复用现有 `FsTransport` 接口抽象,前端组件无感知。
|
||
|
||
## 技术方案
|
||
|
||
### 后端(Go)
|
||
|
||
**新增依赖**:
|
||
- `github.com/pkg/sftp` — SFTP 客户端库
|
||
- `golang.org/x/crypto/ssh` — SSH 协议(已在 indirect 中,提升为 direct)
|
||
|
||
**新增包 `internal/sftp/`**(4 个文件):
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `config.go` | `Config` 结构体:Host/Port/Username/Password/KeyPath/Timeout |
|
||
| `client.go` | `Manager`(sync.Map 连接池,以 host:port 为 key)+ `Client`(单连接封装:SSH 握手、健康检查、自动重连、双认证) |
|
||
| `service.go` | `Service`(对齐 FileSystemService 返回格式的文件操作方法) |
|
||
| `errors.go` | `ConnectionError` + `ToUserMessage()` 中文友好错误映射 |
|
||
|
||
**连接管理**:
|
||
- 以 `host:port` 为 key 的 `sync.Map` 连接池
|
||
- 支持密码认证 / 私钥文件认证(二选一)
|
||
- `WithRetry(fn)` — 操作前检查健康度,断线自动重连(3 次,指数退避)
|
||
- `IsHealthy()` — 通过 `Stat("/")` 探测
|
||
- 切换 profile 时复用已有连接(避免重复 SSH 握手)
|
||
|
||
**App 层新增绑定方法**(12 个):
|
||
|
||
```
|
||
SftpConnect(req) → connID 建立连接
|
||
SftpDisconnect(connID) 断开连接
|
||
SftpListDir(connID, path) 列目录
|
||
SftpReadFile(connID, path) 读文件
|
||
SftpWriteFile(req) 写文件
|
||
SftpGetFileInfo(connID, path) 文件信息
|
||
SftpCreateDir(connID, path) 创建目录
|
||
SftpCreateFile(connID, path) 创建文件
|
||
SftpDeletePath(connID, path) 删除
|
||
SftpRenamePath(req) 重命名
|
||
SftpDownloadToTemp(connID, path) 下载到临时目录(预览用)
|
||
SftpGetCommonPaths(connID) 远程主机常用路径
|
||
```
|
||
|
||
### 前端(TypeScript/Vue)
|
||
|
||
**新建 `frontend/src/api/sftp-transport.ts`**:
|
||
- 实现 `FsTransport` 接口的完整 23 个方法
|
||
- `connect()/disconnect()/requireConn()` 管理 SFTP 会话生命周期
|
||
- `downloadForPreview(remotePath)` 下载远程文件到本地临时目录(带缓存)
|
||
- ZIP/回收站/openPath 等不适用方法抛出 "暂未实现"
|
||
|
||
**修改 `connection-manager.ts`**:
|
||
- `ConnectionType` 扩展:`'local' | 'remote' | 'sftp'`
|
||
- `ConnectionProfile` 新增字段:`username?`, `password?`, `keyPath?`
|
||
- `applyActive()` 增加 sftp 分支(创建 SftpTransport → connect() → 连通性检查)
|
||
- 新增 `isSftp()` 方法
|
||
- `isRemote()` 扩展为包含 `'sftp'`
|
||
- `getFileServerBaseURL()` sftp 模式返回 `'http://localhost:8073'`
|
||
- 切换/删除 profile 时显式断开 SFTP 连接
|
||
|
||
**修改 `ConnectionDialog.vue`**:
|
||
- 新增连接类型选择器(RadioGroup: HTTP Agent / SFTP)
|
||
- SFTP 类型显示额外字段:用户名(默认 root)、密码、私钥路径
|
||
- HTTP Agent 类型保持原有 Token 字段
|
||
- 默认端口根据类型切换(SFTP=22,HTTP=9876)
|
||
- `addProfile` 时 type 使用表单选择的值
|
||
|
||
**修改 `ConnectionIndicator.vue`**:
|
||
- `.dot.sftp` 样式(紫色 `#7c3aed`)
|
||
- `dotClass(p)` 函数返回 local/remote/sftp
|
||
- "更多操作"按钮条件从 `type === 'remote'` 改为 `type !== 'local'`
|
||
|
||
**修改 `Sidebar.vue`**:
|
||
- 模式标签区分显示:本地(绿)/远程(蓝)/SFTP(紫)
|
||
- 新增 `isSftp` 响应式变量
|
||
|
||
**修改 `useFilePreview.ts`**:
|
||
- SFTP 模式下 `updatePreviewUrl()` 先调用 `downloadForPreview()` 下载到本地临时目录
|
||
- 下载完成后使用 `http://localhost:8073/localfs/{temp_path}` 预览(复用现有 LocalFileServer)
|
||
- 已下载文件缓存避免重复下载
|
||
|
||
### 文件预览方案
|
||
|
||
**策略:下载到本地临时目录 + 复用 LocalFileServer**
|
||
|
||
```
|
||
用户点击 SFTP 远程文件
|
||
→ useFilePreview 检测 isSftp()
|
||
→ SftpTransport.downloadForPreview(remotePath)
|
||
→ Go 后端 SFTP 下载到 %TEMP%/udesk-sftp-preview-{filename}
|
||
→ 返回本地绝对路径
|
||
→ 预览 URL = http://localhost:8073/localfs/{temp_path}
|
||
→ 现有 LocalFileServer 直接提供服务
|
||
```
|
||
|
||
**临时文件管理**:
|
||
- 启动时清理上次遗留的 `udesk-sftp-preview-*` 文件(`ServiceStartup` 中调用 `CleanupTempFiles()`)
|
||
- 关闭时清理本次创建的临时文件(`ServiceShutdown` 中调用)
|
||
- SftpTransport 内部维护 remotePath → localTempPath 缓存
|
||
|
||
## 文件变更清单
|
||
|
||
### 新增(5 个)
|
||
|
||
| 文件 | 说明 |
|
||
|------|------|
|
||
| `internal/sftp/config.go` | SFTP 配置结构体 |
|
||
| `internal/sftp/client.go` | SSH/SFTP 连接管理器 |
|
||
| `internal/sftp/service.go` | SFTP 文件操作服务 |
|
||
| `internal/sftp/errors.go` | 错误处理 + 用户友好消息 |
|
||
| `frontend/src/api/sftp-transport.ts` | 前端 FsTransport 实现 |
|
||
|
||
### 修改(8 个)
|
||
|
||
| 文件 | 变更 |
|
||
|------|------|
|
||
| `go.mod` | 添加 `github.com/pkg/sftp` 依赖 |
|
||
| `internal/filesystem/fs.go` | `formatBytes` 导出为 `FormatBytes` |
|
||
| `app.go` | +sftpService 字段 + 12 个绑定方法 + Shutdown 扩展 + 清理临时文件 |
|
||
| `frontend/src/api/connection-manager.ts` | 类型扩展 + sftp 分支 + isSftp() |
|
||
| `frontend/src/.../ConnectionDialog.vue` | 类型选择器 + SFTP 表单 |
|
||
| `frontend/src/.../ConnectionIndicator.vue` | sftp 样式 + dotClass |
|
||
| `frontend/src/.../Sidebar.vue` | SFTP 模式标签 |
|
||
| `frontend/src/.../useFilePreview.ts` | SFTP 下载预览 |
|
||
|
||
### 自动生成
|
||
|
||
| 文件 | 触发方式 |
|
||
|------|---------|
|
||
| `frontend/src/wailsjs/v3-bindings/u-desk/app.ts` | `wails dev` 启动时重新生成 |
|
||
|
||
## 与现有模式的对比
|
||
|
||
| 特性 | 本地 (WailsTransport) | 远程 (HttpTransport) | SFTP (SftpTransport) |
|
||
|------|----------------------|---------------------|---------------------|
|
||
| 协议 | Wails IPC | HTTP REST | SSH/SFTP |
|
||
| 目标要求 | 本地桌面 | 部署 u-fs-agent | SSH 服务 |
|
||
| 认证 | 无 | Bearer Token | 密码 / 私钥 |
|
||
| 文件预览 | LocalFileServer | Agent 反向代理 | 下载到临时目录 |
|
||
| ZIP 支持 | ✅ | ❌ | ❌ |
|
||
| 回收站 | ✅ | ❌ | ❌ |
|
||
| 延迟 | < 10ms | 取决于网络 | 取决于网络(首次握手 ~2s) |
|
||
| 连接复用 | N/A | 每次请求 HTTP | sync.Map 连接池 |
|
||
|
||
## 后续迭代方向
|
||
|
||
### Phase 2(体验完善)
|
||
- 密钥文件选择器(Wails OpenFileDialog)
|
||
- 断线重连 UI 提示 + 手动重连按钮
|
||
- 大文件传输进度显示
|
||
- saveBase64File 二进制上传支持
|
||
|
||
### Phase 3(高级功能)
|
||
- SSH known_hosts 安全验证(替换 InsecureIgnoreHostKey)
|
||
- TCP KeepAlive + 应用层心跳防空闲断开
|
||
- 端口转发(SOCKS5/本地转发)
|
||
- 符号链接处理选项
|
||
- 并发传输队列 + 带宽限制
|