fix(storage): require legacy data before showing migrate hint

This commit is contained in:
digua committed 2026-06-03 00:46:34 +08:00
1 parent 42837278fa
commit f234a25ff6
4 files changed
+66 -1

No files matched your search

@@ -1,11 +1,14 @@
import { describe, it } from 'node:test'
import assert from 'node:assert/strict'
import fs from 'node:fs'
import os from 'node:os'
import path from 'node:path'
import Fastify from 'fastify'
import type { PathProvider } from '@openchatlab/core'
import type { HttpRouteContext } from '../../context'
import { registerCacheRoutes } from './cache'
function createPathProvider(): PathProvider {
function createPathProvider(overrides: Partial<PathProvider> = {}): PathProvider {
return {
getSystemDir: () => '/tmp/chatlab-test',
getUserDataDir: () => '/tmp/chatlab-test/data',
@@ -16,9 +19,15 @@ function createPathProvider(): PathProvider {
getTempDir: () => '/tmp/chatlab-test/temp',
getLogsDir: () => '/tmp/chatlab-test/logs',
getDownloadsDir: () => '/tmp/chatlab-test/downloads',
...overrides,
}
}
function makeTempDir(): string {
const baseDir = fs.existsSync('/private/tmp') ? '/private/tmp' : os.tmpdir()
return fs.mkdtempSync(path.join(baseDir, 'chatlab-cache-routes-'))
}
describe('registerCacheRoutes data directory routes', () => {
it('returns data directory capability and pending migration', async () => {
const app = Fastify()
@@ -45,6 +54,7 @@ describe('registerCacheRoutes data directory routes', () => {
defaultPath: '/tmp/chatlab-test/default-data',
isCustom: true,
canSetDataDir: true,
hasLegacyDataAtDefaultDir: false,
pendingMigration: {
from: '/tmp/chatlab-test/data',
to: '/tmp/chatlab-test/new-data',
@@ -55,6 +65,42 @@ describe('registerCacheRoutes data directory routes', () => {
await app.close()
})
it('reports legacy data at default directory only when default databases contain db files', async () => {
const root = makeTempDir()
const currentDir = path.join(root, 'custom-data')
const defaultDir = path.join(root, 'default-data')
fs.mkdirSync(path.join(defaultDir, 'databases'), { recursive: true })
fs.writeFileSync(path.join(defaultDir, '.chatlab'), 'ChatLab Data Directory')
const appWithoutDb = Fastify()
registerCacheRoutes(appWithoutDb, {
pathProvider: createPathProvider({ getUserDataDir: () => currentDir }),
defaultUserDataDir: defaultDir,
isCustomDataDir: true,
} as unknown as HttpRouteContext)
await appWithoutDb.ready()
const emptyResponse = await appWithoutDb.inject({ method: 'GET', url: '/_web/cache/data-dir' })
assert.equal(emptyResponse.statusCode, 200)
assert.equal(emptyResponse.json().hasLegacyDataAtDefaultDir, false)
await appWithoutDb.close()
fs.writeFileSync(path.join(defaultDir, 'databases', 'legacy.db'), 'sqlite')
const appWithDb = Fastify()
registerCacheRoutes(appWithDb, {
pathProvider: createPathProvider({ getUserDataDir: () => currentDir }),
defaultUserDataDir: defaultDir,
isCustomDataDir: true,
} as unknown as HttpRouteContext)
await appWithDb.ready()
const response = await appWithDb.inject({ method: 'GET', url: '/_web/cache/data-dir' })
assert.equal(response.statusCode, 200)
assert.equal(response.json().hasLegacyDataAtDefaultDir, true)
await appWithDb.close()
})
it('delegates data directory changes to context callback', async () => {
const app = Fastify()
const calls: Array<{ path: string | null; migrate?: boolean }> = []
@@ -43,6 +43,17 @@ async function getFileCount(dirPath: string): Promise<number> {
return count
}
function hasChatLabDatabases(dirPath: string): boolean {
try {
const markerPath = path.join(dirPath, '.chatlab')
const dbDir = path.join(dirPath, 'databases')
if (!fs.existsSync(markerPath) || !fs.existsSync(dbDir)) return false
return fs.readdirSync(dbDir).some((file) => file.endsWith('.db'))
} catch {
return false
}
}
export function registerCacheRoutes(server: FastifyInstance, ctx: HttpRouteContext): void {
const pp = ctx.pathProvider
const downloadsDir = ctx.downloadsDir ?? pp.getDownloadsDir()
@@ -128,6 +139,10 @@ export function registerCacheRoutes(server: FastifyInstance, ctx: HttpRouteConte
defaultPath: ctx.defaultUserDataDir,
isCustom: ctx.isCustomDataDir ?? false,
canSetDataDir: ctx.canSetDataDir ?? Boolean(ctx.setDataDir),
hasLegacyDataAtDefaultDir:
Boolean(ctx.defaultUserDataDir) &&
path.resolve(pp.getUserDataDir()) !== path.resolve(ctx.defaultUserDataDir ?? '') &&
hasChatLabDatabases(ctx.defaultUserDataDir ?? ''),
pendingMigration: pending
? {
from: pending.from,
@@ -20,6 +20,7 @@ const clearingId = ref<string | null>(null)
const dataDir = ref('')
const dataDirInput = ref('')
const defaultDataDir = ref('')
const hasLegacyDataAtDefaultDir = ref(false)
const isCustomDataDir = ref(false)
const canSetDataDir = ref(false)
const isUpdatingDataDir = ref(false)
@@ -72,6 +73,7 @@ const canMigrateToDefault = computed(() => {
dataDir.value &&
defaultDataDir.value &&
dataDir.value !== defaultDataDir.value &&
hasLegacyDataAtDefaultDir.value &&
!isMigrateHintIgnored.value
)
})
@@ -95,6 +97,7 @@ async function loadDataDir() {
dataDir.value = info.path
dataDirInput.value = info.pendingMigration?.to || info.path
defaultDataDir.value = info.defaultPath || ''
hasLegacyDataAtDefaultDir.value = Boolean(info.hasLegacyDataAtDefaultDir)
isCustomDataDir.value = info.isCustom
canSetDataDir.value = IS_ELECTRON || Boolean(info.canSetDataDir)
updateMigrateHintIgnored()
+1
View File
@@ -21,6 +21,7 @@ export interface DataDirInfo {
defaultPath?: string
isCustom: boolean
canSetDataDir?: boolean
hasLegacyDataAtDefaultDir?: boolean
pendingMigration?: {
from: string
to: string