- 文件剪贴板复制剪切粘贴,重名自动副本 - SFTP下载到本机与外部文件拖入,目录递归 - 传输进度浮动面板,transfer-progress事件链 - Drawio预览,Excel工作线程,冻结看门狗 - cmd与bat自写batch语法高亮,psm1等映射补齐
204 lines
6.2 KiB
TypeScript
204 lines
6.2 KiB
TypeScript
/**
|
|
* 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<string> {
|
|
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<boolean> {
|
|
return probeAlive(this.connID, id => SftpGetFileInfo(id, '/'))
|
|
}
|
|
|
|
get sessionId(): string | null { return this.connID }
|
|
|
|
async disconnect(): Promise<void> {
|
|
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<FileItem[]> {
|
|
return transformFileList(await SftpListDir(this.requireConn(), path))
|
|
}
|
|
|
|
async getFileInfo(path: string): Promise<Record<string, any>> {
|
|
return SftpGetFileInfo(this.requireConn(), path)
|
|
}
|
|
|
|
// ====== 文件读写 ======
|
|
|
|
async readFile(path: string): Promise<string> {
|
|
return SftpReadFile(this.requireConn(), path)
|
|
}
|
|
|
|
async writeFile(path: string, content: string): Promise<void> {
|
|
await SftpWriteFile({
|
|
session_id: this.requireConn(),
|
|
path,
|
|
content,
|
|
})
|
|
}
|
|
|
|
async saveBase64File(path: string, content: string): Promise<void> {
|
|
if (!content) throw new Error('无效的 base64 内容')
|
|
await SftpWriteBase64File(this.requireConn(), path, content)
|
|
}
|
|
|
|
// ====== 文件操作 ======
|
|
|
|
async createFile(dirPath: string, filename: string): Promise<FileOperationResult> {
|
|
return SftpCreateFile(this.requireConn(), joinPath(dirPath, filename))
|
|
}
|
|
|
|
async createDir(parentPath: string, dirname: string): Promise<FileOperationResult> {
|
|
return SftpCreateDir(this.requireConn(), joinPath(parentPath, dirname))
|
|
}
|
|
|
|
async deletePath(path: string): Promise<FileOperationResult> {
|
|
return SftpDeletePath(this.requireConn(), path)
|
|
}
|
|
|
|
async renamePath(oldPath: string, newPath: string): Promise<FileOperationResult> {
|
|
return SftpRenamePath({
|
|
session_id: this.requireConn(),
|
|
old_path: oldPath,
|
|
new_path: newPath,
|
|
})
|
|
}
|
|
|
|
// ====== 复制/移动(粘贴仅本地面板支持)======
|
|
|
|
async copyPath(_src: string, _dst: string): Promise<void> {
|
|
throw new Error('SFTP 面板暂不支持粘贴,可使用拖拽上传')
|
|
}
|
|
|
|
async movePath(_src: string, _dst: string): Promise<void> {
|
|
throw new Error('SFTP 面板暂不支持粘贴,可使用拖拽上传')
|
|
}
|
|
|
|
// ====== 传输 ======
|
|
|
|
/** 下载远程文件/目录到用户指定目录(流式无上限,重名自动副本),返回本地落地路径 */
|
|
async downloadToFile(remotePath: string, localDstDir: string): Promise<string> {
|
|
return SftpDownloadToFile(this.requireConn(), remotePath, localDstDir)
|
|
}
|
|
|
|
/** 上传本地文件/目录到远程目录(流式,目录递归) */
|
|
async uploadFromFile(localPath: string, remoteDir: string): Promise<void> {
|
|
await SftpUploadFromFile(this.requireConn(), localPath, remoteDir)
|
|
}
|
|
|
|
// ====== ZIP 操作(不支持)======
|
|
|
|
async listZipContents(_zipPath: string): Promise<FileItem[]> {
|
|
throw new Error('ZIP 操作在 SFTP 模式暂未实现')
|
|
}
|
|
|
|
async extractFileFromZip(_zipPath: string, _filePath: string): Promise<string> {
|
|
throw new Error('ZIP 操作在 SFTP 模式暂未实现')
|
|
}
|
|
|
|
async extractFileFromZipToTemp(_zipPath: string, _filePath: string): Promise<string> {
|
|
throw new Error('ZIP 操作在 SFTP 模式暂未实现')
|
|
}
|
|
|
|
async getZipFileInfo(_zipPath: string, _filePath: string): Promise<FileItem> {
|
|
throw new Error('ZIP 操作在 SFTP 模式暂未实现')
|
|
}
|
|
|
|
// ====== 系统操作 ======
|
|
|
|
async openPath(_path: string): Promise<void> {
|
|
throw new Error('SFTP 模式不支持打开本地路径')
|
|
}
|
|
|
|
async getFileServerURL(): Promise<string> {
|
|
const { getFileServerBaseURL } = await import('./file-server')
|
|
return getFileServerBaseURL()
|
|
}
|
|
|
|
getPreviewToken(): string {
|
|
return ''
|
|
}
|
|
|
|
async resolveShortcut(_lnkPath: string): Promise<any> {
|
|
return null
|
|
}
|
|
|
|
async detectFileTypeByContent(_path: string): Promise<DetectTypeResult> {
|
|
return { extension: '', category: 'unknown', mime_type: '', confidence: 0 }
|
|
}
|
|
|
|
async getCommonPaths(): Promise<Record<string, string>> {
|
|
return SftpGetCommonPaths(this.requireConn())
|
|
}
|
|
|
|
// ====== 回收站(无)======
|
|
|
|
async getRecycleBinEntries(): Promise<any[]> {
|
|
return []
|
|
}
|
|
|
|
async restoreFromRecycleBin(_path: string): Promise<void> {}
|
|
|
|
async deletePermanently(_path: string): Promise<void> {}
|
|
|
|
async emptyRecycleBin(): Promise<void> {}
|
|
|
|
// ====== 预览辅助 ======
|
|
|
|
/** 下载远程文件到本地临时目录用于预览(缓存由 Go 后端管理) */
|
|
async downloadForPreview(remotePath: string): Promise<string> {
|
|
return SftpDownloadToTemp(this.requireConn(), remotePath)
|
|
}
|
|
|
|
/** 下载 HTML 及其网站资源到临时目录用于预览 */
|
|
async downloadSiteForPreview(remotePath: string): Promise<string> {
|
|
return SftpDownloadSiteForPreview(this.requireConn(), remotePath)
|
|
}
|
|
|
|
async watchFile(_path: string): Promise<void> { /* SFTP 模式不支持文件监听 */ }
|
|
async unwatchFile(): Promise<void> { /* SFTP 模式不支持文件监听 */ }
|
|
}
|