/** * SFTP Transport — 通过 Wails IPC 调用 Go 后端 SFTP 客户端 */ import type { FsTransport, FileItem, FileOperationResult, DetectTypeResult, } from './transport' import { transformFileList, joinPath, probeAlive } from './transport' import type { ConnectionProfile } from './connection-manager' import { SftpConnect, SftpDisconnect, SftpListDir, SftpReadFile, SftpWriteFile, SftpGetFileInfo, SftpCreateDir, SftpCreateFile, SftpDeletePath, SftpRenamePath, SftpDownloadToTemp, SftpDownloadSiteForPreview, SftpDownloadToFile, SftpUploadFromFile, SftpGetCommonPaths, SftpWriteBase64File, } from '@bindings/u-desk/app' export class SftpTransport implements FsTransport { private profile: ConnectionProfile private connID: string | null = null constructor(profile: ConnectionProfile) { this.profile = profile } async connect(): Promise { const result = await SftpConnect({ host: this.profile.host, port: this.profile.port || 22, username: this.profile.username || 'root', password: this.profile.password || '', key_path: this.profile.keyPath || '', key_passphrase: '', }) this.connID = result return result } async isAlive(): Promise { return probeAlive(this.connID, id => SftpGetFileInfo(id, '/')) } get sessionId(): string | null { return this.connID } async disconnect(): Promise { if (this.connID) { try { await SftpDisconnect(this.connID) } catch (e) { console.warn('[SFTP] disconnect error:', e) } this.connID = null } } private requireConn(): string { if (!this.connID) throw new Error('SFTP 未连接') return this.connID } // ====== 文件列表与信息 ====== async listDir(path: string): Promise { return transformFileList(await SftpListDir(this.requireConn(), path)) } async getFileInfo(path: string): Promise> { return SftpGetFileInfo(this.requireConn(), path) } // ====== 文件读写 ====== async readFile(path: string): Promise { return SftpReadFile(this.requireConn(), path) } async writeFile(path: string, content: string): Promise { await SftpWriteFile({ session_id: this.requireConn(), path, content, }) } async saveBase64File(path: string, content: string): Promise { if (!content) throw new Error('无效的 base64 内容') await SftpWriteBase64File(this.requireConn(), path, content) } // ====== 文件操作 ====== async createFile(dirPath: string, filename: string): Promise { return SftpCreateFile(this.requireConn(), joinPath(dirPath, filename)) } async createDir(parentPath: string, dirname: string): Promise { return SftpCreateDir(this.requireConn(), joinPath(parentPath, dirname)) } async deletePath(path: string): Promise { return SftpDeletePath(this.requireConn(), path) } async renamePath(oldPath: string, newPath: string): Promise { return SftpRenamePath({ session_id: this.requireConn(), old_path: oldPath, new_path: newPath, }) } // ====== 复制/移动(粘贴仅本地面板支持)====== async copyPath(_src: string, _dst: string): Promise { throw new Error('SFTP 面板暂不支持粘贴,可使用拖拽上传') } async movePath(_src: string, _dst: string): Promise { throw new Error('SFTP 面板暂不支持粘贴,可使用拖拽上传') } // ====== 传输 ====== /** 下载远程文件/目录到用户指定目录(流式无上限,重名自动副本),返回本地落地路径 */ async downloadToFile(remotePath: string, localDstDir: string): Promise { return SftpDownloadToFile(this.requireConn(), remotePath, localDstDir) } /** 上传本地文件/目录到远程目录(流式,目录递归) */ async uploadFromFile(localPath: string, remoteDir: string): Promise { await SftpUploadFromFile(this.requireConn(), localPath, remoteDir) } // ====== ZIP 操作(不支持)====== async listZipContents(_zipPath: string): Promise { throw new Error('ZIP 操作在 SFTP 模式暂未实现') } async extractFileFromZip(_zipPath: string, _filePath: string): Promise { throw new Error('ZIP 操作在 SFTP 模式暂未实现') } async extractFileFromZipToTemp(_zipPath: string, _filePath: string): Promise { throw new Error('ZIP 操作在 SFTP 模式暂未实现') } async getZipFileInfo(_zipPath: string, _filePath: string): Promise { throw new Error('ZIP 操作在 SFTP 模式暂未实现') } // ====== 系统操作 ====== async openPath(_path: string): Promise { throw new Error('SFTP 模式不支持打开本地路径') } async getFileServerURL(): Promise { const { getFileServerBaseURL } = await import('./file-server') return getFileServerBaseURL() } getPreviewToken(): string { return '' } async resolveShortcut(_lnkPath: string): Promise { return null } async detectFileTypeByContent(_path: string): Promise { return { extension: '', category: 'unknown', mime_type: '', confidence: 0 } } async getCommonPaths(): Promise> { return SftpGetCommonPaths(this.requireConn()) } // ====== 回收站(无)====== async getRecycleBinEntries(): Promise { return [] } async restoreFromRecycleBin(_path: string): Promise {} async deletePermanently(_path: string): Promise {} async emptyRecycleBin(): Promise {} // ====== 预览辅助 ====== /** 下载远程文件到本地临时目录用于预览(缓存由 Go 后端管理) */ async downloadForPreview(remotePath: string): Promise { return SftpDownloadToTemp(this.requireConn(), remotePath) } /** 下载 HTML 及其网站资源到临时目录用于预览 */ async downloadSiteForPreview(remotePath: string): Promise { return SftpDownloadSiteForPreview(this.requireConn(), remotePath) } async watchFile(_path: string): Promise { /* SFTP 模式不支持文件监听 */ } async unwatchFile(): Promise { /* SFTP 模式不支持文件监听 */ } }