mirror of
https://github.com/hellodigua/ChatLab.git
synced 2026-10-27 02:45:26 +08:00
164 lines
4.4 KiB
TypeScript
164 lines
4.4 KiB
TypeScript
/**
|
|
* PathProvider 的 Node.js 独立实现
|
|
*
|
|
* 为 CLI / npm 服务版提供路径管理,不依赖 Electron。
|
|
*
|
|
* 目录分为两类:
|
|
* - 系统数据:固定在 ~/.chatlab/,存放配置、日志、缓存、AI 数据等
|
|
* - 用户数据:可配置位置,存放聊天记录数据库等核心资产
|
|
*
|
|
* 用户数据目录(userDataDir)解析优先级:
|
|
* 1. 构造函数传入的 userDataDir 参数
|
|
* 2. CHATLAB_DATA_DIR 环境变量
|
|
* 3. ~/.chatlab/config.toml → [data] user_data_dir
|
|
* 4. 平台默认路径
|
|
*/
|
|
|
|
import * as fs from 'fs'
|
|
import * as os from 'os'
|
|
import * as path from 'path'
|
|
import type { PathProvider } from '@openchatlab/core'
|
|
import { writeConfigField, loadConfig } from '@openchatlab/config'
|
|
import { migrateFromElectronIfNeeded } from './migrations/electron-data-migration'
|
|
import { applyPendingNodeDataDirMigration } from './data-dir-switch'
|
|
import { getChatLabTempScopeDir } from './temp-workspace'
|
|
|
|
const SYSTEM_DIR = path.join(os.homedir(), '.chatlab')
|
|
|
|
/**
|
|
* Set after resolveUserDataDir() detects Electron installation but cannot find databases.
|
|
* CLI should check this and block execution with instructions.
|
|
*/
|
|
let _pendingElectronDataWarning = false
|
|
|
|
export function hasPendingElectronDataWarning(): boolean {
|
|
return _pendingElectronDataWarning
|
|
}
|
|
|
|
export function applyPendingNodeDataDirMigrationIfNeeded(): { success: boolean; skipped?: boolean; error?: string } {
|
|
if (process.env.CHATLAB_DATA_DIR) return { success: true, skipped: true }
|
|
return applyPendingNodeDataDirMigration(SYSTEM_DIR)
|
|
}
|
|
|
|
export class NodePathProvider implements PathProvider {
|
|
private systemDir: string
|
|
private userDataDir: string
|
|
|
|
constructor(userDataDir?: string) {
|
|
this.systemDir = SYSTEM_DIR
|
|
this.userDataDir = userDataDir || resolveUserDataDir()
|
|
}
|
|
|
|
getSystemDir(): string {
|
|
return this.systemDir
|
|
}
|
|
|
|
getUserDataDir(): string {
|
|
return this.userDataDir
|
|
}
|
|
|
|
getDatabaseDir(): string {
|
|
return path.join(this.userDataDir, 'databases')
|
|
}
|
|
|
|
getVectorDir(): string {
|
|
return path.join(this.userDataDir, 'vector')
|
|
}
|
|
|
|
getAiDataDir(): string {
|
|
return path.join(this.systemDir, 'ai')
|
|
}
|
|
|
|
getSettingsDir(): string {
|
|
return path.join(this.systemDir, 'settings')
|
|
}
|
|
|
|
getCacheDir(): string {
|
|
return path.join(this.systemDir, 'cache')
|
|
}
|
|
|
|
getTempDir(): string {
|
|
return getChatLabTempScopeDir('runtime')
|
|
}
|
|
|
|
getLogsDir(): string {
|
|
return path.join(this.systemDir, 'logs')
|
|
}
|
|
|
|
getDownloadsDir(): string {
|
|
return path.join(os.homedir(), 'Downloads')
|
|
}
|
|
|
|
/**
|
|
* 确保系统目录和用户数据目录都存在
|
|
*/
|
|
ensureAllDirs(): void {
|
|
const dirs = [
|
|
this.systemDir,
|
|
this.userDataDir,
|
|
this.getDatabaseDir(),
|
|
this.getVectorDir(),
|
|
this.getAiDataDir(),
|
|
this.getSettingsDir(),
|
|
this.getCacheDir(),
|
|
this.getTempDir(),
|
|
this.getLogsDir(),
|
|
]
|
|
for (const dir of dirs) {
|
|
if (!fs.existsSync(dir)) {
|
|
fs.mkdirSync(dir, { recursive: true })
|
|
}
|
|
}
|
|
|
|
const markerPath = path.join(this.userDataDir, '.chatlab')
|
|
if (!fs.existsSync(markerPath)) {
|
|
fs.writeFileSync(markerPath, 'ChatLab Data Directory', 'utf-8')
|
|
}
|
|
}
|
|
}
|
|
|
|
function resolveUserDataDir(): string {
|
|
const envDir = process.env.CHATLAB_DATA_DIR
|
|
if (envDir) {
|
|
return expandHome(envDir)
|
|
}
|
|
|
|
const config = loadConfig()
|
|
if (config.data.user_data_dir) {
|
|
return expandHome(config.data.user_data_dir)
|
|
}
|
|
|
|
// First run: check for existing Electron desktop data before using default
|
|
const result = migrateFromElectronIfNeeded(SYSTEM_DIR)
|
|
if (result.migrated && result.userDataDir) {
|
|
return result.userDataDir
|
|
}
|
|
|
|
const defaultDir = getDefaultNodeUserDataDir()
|
|
|
|
if (result.electronDetected) {
|
|
// Electron was used but we couldn't find databases — likely a custom data directory.
|
|
// Don't persist default to config.toml so migration retries on next startup.
|
|
_pendingElectronDataWarning = true
|
|
return defaultDir
|
|
}
|
|
|
|
writeConfigField('data', 'user_data_dir', defaultDir)
|
|
return defaultDir
|
|
}
|
|
|
|
export function getDefaultNodeUserDataDir(): string {
|
|
return path.join(os.homedir(), '.chatlab', 'data')
|
|
}
|
|
|
|
export function getSystemLogsDir(): string {
|
|
return path.join(SYSTEM_DIR, 'logs')
|
|
}
|
|
|
|
function expandHome(filePath: string): string {
|
|
if (filePath.startsWith('~/') || filePath === '~') {
|
|
return path.join(os.homedir(), filePath.slice(1))
|
|
}
|
|
return filePath
|
|
}
|