refactor: Extract notes backup and restore logic into separate methods

Extract the notes backup and restore logic from the main backup() and restore()
methods into separate private methods for better code clarity and maintainability.

- Add backupExternalNotes() method to handle backing up notes from external locations
- Add restoreExternalNotes() method to handle restoring notes to configured locations
- Both methods properly handle JSON parsing, path expansion, and file operations
- Maintains all existing functionality and error handling
- All tests pass successfully

This refactoring addresses PR review feedback requesting clearer separation of concerns.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
suyao 2025-12-16 16:21:52 +08:00
parent 4269a32cfb
commit 6919abf833
No known key found for this signature in database

View File

@ -248,41 +248,7 @@ class BackupManager {
await this.setWritableRecursive(tempDataDir) await this.setWritableRecursive(tempDataDir)
// 检查并备份 notes 目录(如果配置在 Data 目录外) // 检查并备份 notes 目录(如果配置在 Data 目录外)
try { await this.backupExternalNotes(data, onProgress, totalSize, copiedSize)
const backupData = JSON.parse(data)
const persistData = JSON.parse(backupData.localStorage?.['persist:cherry-studio'] || '{}')
const noteState = JSON.parse(persistData.note || '{}')
const notesPath = noteState.notesPath
if (notesPath) {
// 展开路径获取绝对路径
const expandedNotesPath = expandNotesPath(notesPath)
const dataPath = path.join(app.getPath('userData'), 'Data')
const normalizedDataPath = path.normalize(dataPath)
const normalizedNotesPath = path.normalize(expandedNotesPath)
// 检查 notes 是否在 Data 目录外
const isOutsideData =
!normalizedNotesPath.startsWith(normalizedDataPath + path.sep) &&
normalizedNotesPath !== normalizedDataPath
if (isOutsideData && fs.existsSync(expandedNotesPath)) {
logger.info(`Backing up notes from external location: ${expandedNotesPath}`)
const tempNotesDir = path.join(this.tempDir, 'Notes')
await this.copyDirWithProgress(expandedNotesPath, tempNotesDir, (size) => {
// Notes backup progress from 45% to 50%
copiedSize += size
const notesProgress = 45 + Math.min(5, Math.floor((size / totalSize) * 5))
onProgress({ stage: 'copying_notes', progress: notesProgress, total: 100 })
})
await this.setWritableRecursive(tempNotesDir)
logger.info('External notes directory backed up successfully')
}
}
} catch (error) {
// 如果解析失败或获取 notes 路径失败,继续备份其他内容
logger.warn('Failed to parse notes path from backup data, skipping external notes backup', error as Error)
}
onProgress({ stage: 'preparing_compression', progress: 50, total: 100 }) onProgress({ stage: 'preparing_compression', progress: 50, total: 100 })
} else { } else {
@ -447,42 +413,7 @@ class BackupManager {
// 检查并恢复外部 Notes 目录 // 检查并恢复外部 Notes 目录
logger.debug('step 3.5: check and restore external Notes directory') logger.debug('step 3.5: check and restore external Notes directory')
const notesBackupPath = path.join(this.tempDir, 'Notes') await this.restoreExternalNotes(data, onProgress)
const notesExists = await fs.pathExists(notesBackupPath)
if (notesExists) {
try {
// 从 data.json 中获取 notes 路径配置
const backupData = JSON.parse(data)
const persistData = JSON.parse(backupData.localStorage?.['persist:cherry-studio'] || '{}')
const noteState = JSON.parse(persistData.note || '{}')
const notesPath = noteState.notesPath
if (notesPath) {
const expandedNotesPath = expandNotesPath(notesPath)
logger.info(`Restoring notes to configured location: ${expandedNotesPath}`)
// 确保目标目录的父目录存在
await fs.ensureDir(path.dirname(expandedNotesPath))
// 如果目标已存在,先删除
if (await fs.pathExists(expandedNotesPath)) {
await this.setWritableRecursive(expandedNotesPath)
await fs.remove(expandedNotesPath)
}
// 复制 Notes 目录
await this.copyDirWithProgress(notesBackupPath, expandedNotesPath, (size) => {
const progress = Math.min(85, 75 + Math.floor(size / 1000000))
onProgress({ stage: 'copying_notes', progress, total: 100 })
})
logger.info('External notes directory restored successfully')
}
} catch (error) {
logger.warn('Failed to restore external notes directory', error as Error)
}
}
logger.debug('step 4: clean up temp directory') logger.debug('step 4: clean up temp directory')
// 清理临时目录 // 清理临时目录
@ -592,6 +523,103 @@ class BackupManager {
return size return size
} }
/**
* Backup external notes directory if configured outside Data folder
* @param data - The backup data JSON string
* @param onProgress - Progress callback function
* @param totalSize - Total size of Data directory for progress calculation
* @param copiedSize - Current copied size for progress offset
*/
private async backupExternalNotes(
data: string,
onProgress: (processData: { stage: string; progress: number; total: number }) => void,
totalSize: number,
copiedSize: number
): Promise<void> {
try {
const backupData = JSON.parse(data)
const persistData = JSON.parse(backupData.localStorage?.['persist:cherry-studio'] || '{}')
const noteState = JSON.parse(persistData.note || '{}')
const notesPath = noteState.notesPath
if (notesPath) {
// 展开路径获取绝对路径
const expandedNotesPath = expandNotesPath(notesPath)
const dataPath = path.join(app.getPath('userData'), 'Data')
const normalizedDataPath = path.normalize(dataPath)
const normalizedNotesPath = path.normalize(expandedNotesPath)
// 检查 notes 是否在 Data 目录外
const isOutsideData =
!normalizedNotesPath.startsWith(normalizedDataPath + path.sep) &&
normalizedNotesPath !== normalizedDataPath
if (isOutsideData && fs.existsSync(expandedNotesPath)) {
logger.info(`Backing up notes from external location: ${expandedNotesPath}`)
const tempNotesDir = path.join(this.tempDir, 'Notes')
await this.copyDirWithProgress(expandedNotesPath, tempNotesDir, (size) => {
// Notes backup progress from 45% to 50%
copiedSize += size
const notesProgress = 45 + Math.min(5, Math.floor((size / totalSize) * 5))
onProgress({ stage: 'copying_notes', progress: notesProgress, total: 100 })
})
await this.setWritableRecursive(tempNotesDir)
logger.info('External notes directory backed up successfully')
}
}
} catch (error) {
// 如果解析失败或获取 notes 路径失败,继续备份其他内容
logger.warn('Failed to parse notes path from backup data, skipping external notes backup', error as Error)
}
}
/**
* Restore external notes directory from backup
* @param data - The backup data JSON string
* @param onProgress - Progress callback function
*/
private async restoreExternalNotes(
data: string,
onProgress: (processData: { stage: string; progress: number; total: number }) => void
): Promise<void> {
const notesBackupPath = path.join(this.tempDir, 'Notes')
const notesExists = await fs.pathExists(notesBackupPath)
if (notesExists) {
try {
// 从 data.json 中获取 notes 路径配置
const backupData = JSON.parse(data)
const persistData = JSON.parse(backupData.localStorage?.['persist:cherry-studio'] || '{}')
const noteState = JSON.parse(persistData.note || '{}')
const notesPath = noteState.notesPath
if (notesPath) {
const expandedNotesPath = expandNotesPath(notesPath)
logger.info(`Restoring notes to configured location: ${expandedNotesPath}`)
// 确保目标目录的父目录存在
await fs.ensureDir(path.dirname(expandedNotesPath))
// 如果目标已存在,先删除
if (await fs.pathExists(expandedNotesPath)) {
await this.setWritableRecursive(expandedNotesPath)
await fs.remove(expandedNotesPath)
}
// 复制 Notes 目录
await this.copyDirWithProgress(notesBackupPath, expandedNotesPath, (size) => {
const progress = Math.min(85, 75 + Math.floor(size / 1000000))
onProgress({ stage: 'copying_notes', progress, total: 100 })
})
logger.info('External notes directory restored successfully')
}
} catch (error) {
logger.warn('Failed to restore external notes directory', error as Error)
}
}
}
private async copyDirWithProgress( private async copyDirWithProgress(
source: string, source: string,
destination: string, destination: string,