diff --git a/apps/desktop/main/paths/legacy-migration.ts b/apps/desktop/main/paths/legacy-migration.ts index 93e03c80..3a4f8c19 100644 --- a/apps/desktop/main/paths/legacy-migration.ts +++ b/apps/desktop/main/paths/legacy-migration.ts @@ -6,7 +6,7 @@ import { app } from 'electron' import * as fs from 'node:fs' import * as path from 'node:path' import { loadConfig, writeConfigField } from '@openchatlab/config' -import { copyDirMerge, copyDirRecursive, writeMigrationLog } from '../utils/pathUtils' +import { arePathsEqual, copyDirMerge, copyDirRecursive, writeMigrationLog } from '../utils/pathUtils' import { shouldMarkUnifiedDirMigrationDone } from '../utils/unifiedDirMigration' import { ensureDir, @@ -47,12 +47,8 @@ export function getLegacyDataDir(): string { export function needsLegacyMigration(): boolean { const legacyDir = getLegacyDataDir() - // 检查 Documents/ChatLab 是否存在 - if (fs.existsSync(legacyDir)) { - return true - } - - return false + // 用户可以主动将 Documents/ChatLab 设为当前数据目录,此时它不是待迁移的旧目录。 + return fs.existsSync(legacyDir) && !arePathsEqual(legacyDir, getUserDataDir()) } /** @@ -138,6 +134,14 @@ export function migrateFromLegacyDir(): { success: boolean; migratedDirs: string return { success: true, migratedDirs: [] } } + // 迁移函数包含递归删除,必须在执行层再次阻止源目录与当前数据目录相同。 + if (arePathsEqual(legacyDir, newDir)) { + const message = `Legacy migration skipped because source is the active data directory: ${legacyDir}` + console.warn(`[Paths] ${message}`) + writeMigrationLog(getLogsDir(), message, ensureDir) + return { success: true, migratedDirs: [] } + } + // 获取旧目录下的所有子目录和文件 const entries = fs.readdirSync(legacyDir, { withFileTypes: true }) const dirsToMigrate = entries @@ -198,6 +202,13 @@ export function removeLegacyDir(): boolean { return true } + if (arePathsEqual(legacyDir, getUserDataDir())) { + const message = `Legacy directory removal blocked because it is the active data directory: ${legacyDir}` + console.error(`[Paths] ${message}`) + writeMigrationLog(getLogsDir(), message, ensureDir) + return false + } + try { fs.rmSync(legacyDir, { recursive: true, force: true }) console.log(`[Paths] Removed legacy directory: ${legacyDir}`) diff --git a/apps/desktop/main/paths/paths.test.ts b/apps/desktop/main/paths/paths.test.ts index c00dd37c..cecf2e50 100644 --- a/apps/desktop/main/paths/paths.test.ts +++ b/apps/desktop/main/paths/paths.test.ts @@ -102,6 +102,17 @@ test('desktop paths preserve data across configured and legacy directory migrati fs.writeFileSync(path.join(legacyDir, 'databases', 'legacy.db'), 'legacy', 'utf-8') fs.writeFileSync(path.join(legacyDir, 'temp', 'stale.tmp'), 'temporary', 'utf-8') + configuredUserDataDir = legacyDir + paths.setCachedUserDataDir(legacyDir) + assert.equal(paths.needsLegacyMigration(), false) + const sameDirResult = paths.migrateFromLegacyDir() + assert.equal(sameDirResult.success, true) + assert.equal(fs.readFileSync(path.join(legacyDir, 'databases', 'legacy.db'), 'utf-8'), 'legacy') + assert.equal(paths.removeLegacyDir(), false) + assert.equal(fs.readFileSync(path.join(legacyDir, 'databases', 'legacy.db'), 'utf-8'), 'legacy') + + configuredUserDataDir = targetDir + paths.setCachedUserDataDir(targetDir) assert.equal(paths.needsLegacyMigration(), true) const legacyResult = paths.migrateFromLegacyDir() assert.equal(legacyResult.success, true) diff --git a/apps/desktop/main/utils/pathUtils.ts b/apps/desktop/main/utils/pathUtils.ts index 15fb0be9..f047218e 100644 --- a/apps/desktop/main/utils/pathUtils.ts +++ b/apps/desktop/main/utils/pathUtils.ts @@ -28,6 +28,13 @@ function normalizePathForCompare(input: string): string { return process.platform === 'win32' ? normalized.toLowerCase() : normalized } +/** + * 判断两个路径是否指向同一个规范化位置 + */ +export function arePathsEqual(first: string, second: string): boolean { + return normalizePathForCompare(first) === normalizePathForCompare(second) +} + /** * 判断 child 是否为 parent 的子目录 */