mirror of
https://github.com/CherryHQ/cherry-studio.git
synced 2026-01-05 20:41:30 +08:00
fix:修复在备份和恢复备份时由于无法删除临时目录导致的功能终止 (#2224)
* refactor: Remove chat settings toolbar button from input bar * fix:修复在备份时,由于临时文件只读导致的备份失败 * fix:修复在恢复备份时,由于临时文件只读导致的备份失败 --------- Co-authored-by: kangfenmao <kangfenmao@qq.com>
This commit is contained in:
parent
146cabf768
commit
0a0c3d0ac7
@ -6,6 +6,7 @@ import * as fs from 'fs-extra'
|
|||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
|
|
||||||
import WebDav from './WebDav'
|
import WebDav from './WebDav'
|
||||||
|
import { exec } from 'child_process'
|
||||||
|
|
||||||
class BackupManager {
|
class BackupManager {
|
||||||
private tempDir = path.join(app.getPath('temp'), 'cherry-studio', 'backup', 'temp')
|
private tempDir = path.join(app.getPath('temp'), 'cherry-studio', 'backup', 'temp')
|
||||||
@ -18,6 +19,53 @@ class BackupManager {
|
|||||||
this.restoreFromWebdav = this.restoreFromWebdav.bind(this)
|
this.restoreFromWebdav = this.restoreFromWebdav.bind(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async setWritableRecursive(dirPath: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
const items = await fs.readdir(dirPath, { withFileTypes: true });
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
const fullPath = path.join(dirPath, item.name);
|
||||||
|
|
||||||
|
// 先处理子目录
|
||||||
|
if (item.isDirectory()) {
|
||||||
|
await this.setWritableRecursive(fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 统一设置权限(Windows需要特殊处理)
|
||||||
|
await this.forceSetWritable(fullPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确保根目录权限
|
||||||
|
await this.forceSetWritable(dirPath);
|
||||||
|
} catch (error) {
|
||||||
|
Logger.error(`权限设置失败:${dirPath}`, error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增跨平台权限设置方法
|
||||||
|
private async forceSetWritable(targetPath: string): Promise<void> {
|
||||||
|
try {
|
||||||
|
// Windows系统需要先取消只读属性
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
await fs.chmod(targetPath, 0o666); // Windows会忽略权限位但能移除只读
|
||||||
|
} else {
|
||||||
|
const stats = await fs.stat(targetPath);
|
||||||
|
const mode = stats.isDirectory() ? 0o777 : 0o666;
|
||||||
|
await fs.chmod(targetPath, mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 双重保险:使用文件属性命令(Windows专用)
|
||||||
|
if (process.platform === 'win32') {
|
||||||
|
await exec(`attrib -R "${targetPath}" /L /D`);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||||||
|
Logger.warn(`权限设置警告:${targetPath}`, error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async backup(
|
async backup(
|
||||||
_: Electron.IpcMainInvokeEvent,
|
_: Electron.IpcMainInvokeEvent,
|
||||||
fileName: string,
|
fileName: string,
|
||||||
@ -35,6 +83,7 @@ class BackupManager {
|
|||||||
const sourcePath = path.join(app.getPath('userData'), 'Data')
|
const sourcePath = path.join(app.getPath('userData'), 'Data')
|
||||||
const tempDataDir = path.join(this.tempDir, 'Data')
|
const tempDataDir = path.join(this.tempDir, 'Data')
|
||||||
await fs.copy(sourcePath, tempDataDir)
|
await fs.copy(sourcePath, tempDataDir)
|
||||||
|
await this.setWritableRecursive(tempDataDir)
|
||||||
|
|
||||||
// 使用 adm-zip 创建压缩文件
|
// 使用 adm-zip 创建压缩文件
|
||||||
const zip = new AdmZip()
|
const zip = new AdmZip()
|
||||||
@ -75,6 +124,7 @@ class BackupManager {
|
|||||||
// 恢复 Data 目录
|
// 恢复 Data 目录
|
||||||
const sourcePath = path.join(this.tempDir, 'Data')
|
const sourcePath = path.join(this.tempDir, 'Data')
|
||||||
const destPath = path.join(app.getPath('userData'), 'Data')
|
const destPath = path.join(app.getPath('userData'), 'Data')
|
||||||
|
await this.setWritableRecursive(destPath)
|
||||||
await fs.remove(destPath)
|
await fs.remove(destPath)
|
||||||
await fs.copy(sourcePath, destPath)
|
await fs.copy(sourcePath, destPath)
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user