( dataDir: string | null, migrate: boolean = true )
| 171 | * @param migrate 是否迁移现有数据(合并复制,不会覆盖目标文件) |
| 172 | */ |
| 173 | export function setCustomDataDir( |
| 174 | dataDir: string | null, |
| 175 | migrate: boolean = true |
| 176 | ): { success: boolean; error?: string; from?: string; to?: string; requiresRelaunch?: boolean } { |
| 177 | const normalized = typeof dataDir === 'string' ? dataDir.trim() : '' |
| 178 | const oldDir = getUserDataDir() |
| 179 | |
| 180 | try { |
| 181 | if (process.env.CHATLAB_DATA_DIR) { |
| 182 | return { success: false, error: 'CHATLAB_DATA_DIR 已设置,不能在界面中切换数据目录' } |
| 183 | } |
| 184 | |
| 185 | const newDir = normalized || getDefaultUserDataDir() |
| 186 | |
| 187 | if (!path.isAbsolute(newDir)) { |
| 188 | return { success: false, error: '数据目录必须是绝对路径' } |
| 189 | } |
| 190 | |
| 191 | if (migrate && oldDir !== newDir && isSubPath(oldDir, newDir)) { |
| 192 | return { success: false, error: '目标目录不能是当前数据目录的子目录' } |
| 193 | } |
| 194 | |
| 195 | if (!isPathSafe(newDir)) { |
| 196 | return { success: false, error: '不能使用系统关键目录作为数据目录' } |
| 197 | } |
| 198 | |
| 199 | try { |
| 200 | const exePath = app.getPath('exe') |
| 201 | if (isInsideAppInstallDir(newDir, exePath)) { |
| 202 | return { success: false, error: '不能将数据目录放在应用安装目录下,应用更新时该目录会被清空' } |
| 203 | } |
| 204 | } catch { |
| 205 | // 获取 exe 路径失败时跳过此检查 |
| 206 | } |
| 207 | |
| 208 | if (!isUserDataDirSafeToUse(newDir)) { |
| 209 | return { success: false, error: '目标目录不为空且不包含 ChatLab 数据,请选择空目录或已有数据目录' } |
| 210 | } |
| 211 | |
| 212 | if (path.resolve(oldDir) === path.resolve(newDir)) { |
| 213 | const config = readStorageConfig() |
| 214 | writeStorageConfig({ ...config, pendingDataDirMigration: undefined }) |
| 215 | return { success: true, from: oldDir, to: newDir, requiresRelaunch: false } |
| 216 | } |
| 217 | |
| 218 | const config = readStorageConfig() |
| 219 | const pending = createPendingDataDirMigration({ |
| 220 | from: oldDir, |
| 221 | to: newDir, |
| 222 | migrate, |
| 223 | targetWasEmpty: isDirectoryEmptyOrMissing(newDir), |
| 224 | }) |
| 225 | writeStorageConfig({ ...config, pendingDataDirMigration: pending }) |
| 226 | |
| 227 | return { success: true, from: oldDir, to: newDir, requiresRelaunch: true } |
| 228 | } catch (error) { |
| 229 | console.error('[Paths] Error setting custom data dir:', error) |
| 230 | return { success: false, error: error instanceof Error ? error.message : String(error) } |
no test coverage detected