修复: 粘贴图失效与SFTP串台回退等一批问题

粘贴图根修(Ctrl+V直读系统剪贴板,不再依赖不可靠的paste事件)/SFTP串台双刀(拨号期禁回退本机+盘符路径自动回根)/PDF导出目录设置与文案带落盘路径/性能批(连接保存下载防重/md渲染防抖+预热/hljs按需加载/审计锁外置/快捷方式解析超时/webview-watch可停)/前端62类型错清零(含keyPath蛇形字段炸弹)/目录图标字段错位修复
This commit is contained in:
lxy
2026-10-24 13:48:54 +08:00
parent 4675ae381d
commit 75a42c0b6a
48 changed files with 1072 additions and 220 deletions
+43 -9
View File
@@ -24,16 +24,29 @@ export class SftpTransport implements FsTransport {
}
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: '',
// 前端反馈层 9s 超时:后端连接继续由既有逻辑管理(不主动断),
// 仅避免 UI 在网络不可达时无限挂起,用户能立刻拿到失败反馈
let timer: ReturnType<typeof setTimeout> | undefined
const timeout = new Promise<never>((_, reject) => {
timer = setTimeout(() => reject(new Error('连接超时,请检查网络')), 9000)
})
this.connID = result
return result
try {
const result = await Promise.race([
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: '',
}),
timeout,
])
this.connID = result
return result
} finally {
if (timer) clearTimeout(timer)
}
}
async isAlive(): Promise<boolean> {
@@ -56,23 +69,34 @@ export class SftpTransport implements FsTransport {
return this.connID
}
/** 远程路径守卫:本机盘符路径串入 SFTP(tab 恢复/面板状态错位)会在服务器端报 No such file */
private assertRemotePath(path: string): void {
if (/^[A-Za-z]:[\\/]/.test(path)) {
throw new Error(`本机路径不可用于远程操作: ${path}`)
}
}
// ====== 文件列表与信息 ======
async listDir(path: string): Promise<FileItem[]> {
this.assertRemotePath(path)
return transformFileList(await SftpListDir(this.requireConn(), path))
}
async getFileInfo(path: string): Promise<Record<string, any>> {
this.assertRemotePath(path)
return SftpGetFileInfo(this.requireConn(), path)
}
// ====== 文件读写 ======
async readFile(path: string): Promise<string> {
this.assertRemotePath(path)
return SftpReadFile(this.requireConn(), path)
}
async writeFile(path: string, content: string): Promise<void> {
this.assertRemotePath(path)
await SftpWriteFile({
session_id: this.requireConn(),
path,
@@ -82,24 +106,30 @@ export class SftpTransport implements FsTransport {
async saveBase64File(path: string, content: string): Promise<void> {
if (!content) throw new Error('无效的 base64 内容')
this.assertRemotePath(path)
await SftpWriteBase64File(this.requireConn(), path, content)
}
// ====== 文件操作 ======
async createFile(dirPath: string, filename: string): Promise<FileOperationResult> {
this.assertRemotePath(dirPath)
return SftpCreateFile(this.requireConn(), joinPath(dirPath, filename))
}
async createDir(parentPath: string, dirname: string): Promise<FileOperationResult> {
this.assertRemotePath(parentPath)
return SftpCreateDir(this.requireConn(), joinPath(parentPath, dirname))
}
async deletePath(path: string): Promise<FileOperationResult> {
this.assertRemotePath(path)
return SftpDeletePath(this.requireConn(), path)
}
async renamePath(oldPath: string, newPath: string): Promise<FileOperationResult> {
this.assertRemotePath(oldPath)
this.assertRemotePath(newPath)
return SftpRenamePath({
session_id: this.requireConn(),
old_path: oldPath,
@@ -121,11 +151,13 @@ export class SftpTransport implements FsTransport {
/** 下载远程文件/目录到用户指定目录(流式无上限,重名自动副本),返回本地落地路径 */
async downloadToFile(remotePath: string, localDstDir: string): Promise<string> {
this.assertRemotePath(remotePath)
return SftpDownloadToFile(this.requireConn(), remotePath, localDstDir)
}
/** 上传本地文件/目录到远程目录(流式,目录递归) */
async uploadFromFile(localPath: string, remoteDir: string): Promise<void> {
this.assertRemotePath(remoteDir)
await SftpUploadFromFile(this.requireConn(), localPath, remoteDir)
}
@@ -190,11 +222,13 @@ export class SftpTransport implements FsTransport {
/** 下载远程文件到本地临时目录用于预览(缓存由 Go 后端管理) */
async downloadForPreview(remotePath: string): Promise<string> {
this.assertRemotePath(remotePath)
return SftpDownloadToTemp(this.requireConn(), remotePath)
}
/** 下载 HTML 及其网站资源到临时目录用于预览 */
async downloadSiteForPreview(remotePath: string): Promise<string> {
this.assertRemotePath(remotePath)
return SftpDownloadSiteForPreview(this.requireConn(), remotePath)
}