From 3640d846b9af44642237f55328b4dccbc34244bc Mon Sep 17 00:00:00 2001 From: beyondkmp Date: Wed, 25 Jun 2025 00:39:28 +0800 Subject: [PATCH] feat: Implement occupied directories handling during data copy (#7485) * feat: Implement occupied directories handling during data copy - Added `occupiedDirs` constant to manage directories that should not be copied. - Enhanced the `copyOccupiedDirsInMainProcess` function to copy occupied directories to a new app data path in the main process. - Updated IPC and preload APIs to support passing occupied directories during the copy operation. - Modified the DataSettings component to utilize the new copy functionality with occupied directories. * fix: Improve occupied directories handling during data copy - Updated the filter logic in the `registerIpc` function to resolve directory paths correctly. - Modified the `DataSettings` component to pass the correct occupied directories format during the copy operation. --- packages/shared/config/constant.ts | 2 ++ src/main/bootstrap.ts | 28 +++++++++++++++++++ src/main/ipc.ts | 12 ++++++-- src/preload/index.ts | 3 +- .../settings/DataSettings/DataSettings.tsx | 7 ++++- 5 files changed, 48 insertions(+), 4 deletions(-) diff --git a/packages/shared/config/constant.ts b/packages/shared/config/constant.ts index 719600650e..0b568461ea 100644 --- a/packages/shared/config/constant.ts +++ b/packages/shared/config/constant.ts @@ -409,3 +409,5 @@ export enum FeedUrl { EARLY_ACCESS = 'https://github.com/CherryHQ/cherry-studio/releases/latest/download' } export const defaultTimeout = 5 * 1000 * 60 + +export const occupiedDirs = ['logs', 'Network', 'Partitions/webview/Network'] diff --git a/src/main/bootstrap.ts b/src/main/bootstrap.ts index f682c06fcd..15648f6ffc 100644 --- a/src/main/bootstrap.ts +++ b/src/main/bootstrap.ts @@ -1,5 +1,33 @@ +import { occupiedDirs } from '@shared/config/constant' import { app } from 'electron' +import fs from 'fs' +import path from 'path' import { initAppDataDir } from './utils/file' app.isPackaged && initAppDataDir() + +// 在主进程中复制 appData 中某些一直被占用的文件 +// 在renderer进程还没有启动时,主进程可以复制这些文件到新的appData中 +function copyOccupiedDirsInMainProcess() { + const newAppDataPath = process.argv + .slice(1) + .find((arg) => arg.startsWith('--new-data-path=')) + ?.split('--new-data-path=')[1] + if (!newAppDataPath) { + return + } + + if (process.platform === 'win32') { + const appDataPath = app.getPath('userData') + occupiedDirs.forEach((dir) => { + const dirPath = path.join(appDataPath, dir) + const newDirPath = path.join(newAppDataPath, dir) + if (fs.existsSync(dirPath)) { + fs.cpSync(dirPath, newDirPath, { recursive: true }) + } + }) + } +} + +copyOccupiedDirsInMainProcess() diff --git a/src/main/ipc.ts b/src/main/ipc.ts index 88d66d4a39..bcb90ee5d6 100644 --- a/src/main/ipc.ts +++ b/src/main/ipc.ts @@ -269,9 +269,17 @@ export function registerIpc(mainWindow: BrowserWindow, app: Electron.App) { }) // Copy user data to new location - ipcMain.handle(IpcChannel.App_Copy, async (_, oldPath: string, newPath: string) => { + ipcMain.handle(IpcChannel.App_Copy, async (_, oldPath: string, newPath: string, occupiedDirs: string[] = []) => { try { - await fs.promises.cp(oldPath, newPath, { recursive: true }) + await fs.promises.cp(oldPath, newPath, { + recursive: true, + filter: (src) => { + if (occupiedDirs.some((dir) => src.startsWith(path.resolve(dir)))) { + return false + } + return true + } + }) return { success: true } } catch (error: any) { log.error('Failed to copy user data:', error) diff --git a/src/preload/index.ts b/src/preload/index.ts index 114ad13ef6..b6c87259ce 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -32,7 +32,8 @@ const api = { hasWritePermission: (path: string) => ipcRenderer.invoke(IpcChannel.App_HasWritePermission, path), setAppDataPath: (path: string) => ipcRenderer.invoke(IpcChannel.App_SetAppDataPath, path), getDataPathFromArgs: () => ipcRenderer.invoke(IpcChannel.App_GetDataPathFromArgs), - copy: (oldPath: string, newPath: string) => ipcRenderer.invoke(IpcChannel.App_Copy, oldPath, newPath), + copy: (oldPath: string, newPath: string, occupiedDirs: string[] = []) => + ipcRenderer.invoke(IpcChannel.App_Copy, oldPath, newPath, occupiedDirs), setStopQuitApp: (stop: boolean, reason: string) => ipcRenderer.invoke(IpcChannel.App_SetStopQuitApp, stop, reason), flushAppData: () => ipcRenderer.invoke(IpcChannel.App_FlushAppData), isNotEmptyDir: (path: string) => ipcRenderer.invoke(IpcChannel.App_IsNotEmptyDir, path), diff --git a/src/renderer/src/pages/settings/DataSettings/DataSettings.tsx b/src/renderer/src/pages/settings/DataSettings/DataSettings.tsx index 0038f1947c..94030bfbab 100644 --- a/src/renderer/src/pages/settings/DataSettings/DataSettings.tsx +++ b/src/renderer/src/pages/settings/DataSettings/DataSettings.tsx @@ -19,6 +19,7 @@ import store, { useAppDispatch } from '@renderer/store' import { setSkipBackupFile as _setSkipBackupFile } from '@renderer/store/settings' import { AppInfo } from '@renderer/types' import { formatFileSize } from '@renderer/utils' +import { occupiedDirs } from '@shared/config/constant' import { Button, Progress, Switch, Typography } from 'antd' import { FileText, FolderCog, FolderInput, Sparkle } from 'lucide-react' import { FC, useEffect, useState } from 'react' @@ -500,7 +501,11 @@ const DataSettings: FC = () => { await new Promise((resolve) => setTimeout(resolve, 2000)) // 开始复制过程 - const copyResult = await window.api.copy(originalPath, newPath) + const copyResult = await window.api.copy( + originalPath, + newPath, + occupiedDirs.map((dir) => originalPath + '/' + dir) + ) // 停止进度更新 if (progressInterval) {