diff --git a/packages/drawnix/src/constants/storage-keys.ts b/packages/drawnix/src/constants/storage-keys.ts index a177c61c..15082383 100644 --- a/packages/drawnix/src/constants/storage-keys.ts +++ b/packages/drawnix/src/constants/storage-keys.ts @@ -197,13 +197,16 @@ export const IDB_DATABASES = { } as const; /** - * 废弃的 IndexedDB 数据库(用于清理) + * 废弃的 IndexedDB 数据库(仅作为参考记录) + * 注意:不再自动删除这些数据库,因为删除是危险操作 + * 数据迁移由各服务自行处理(如 useTaskStorage, asset-storage-service) + * @deprecated 仅供参考,不再使用 */ -export const IDB_LEGACY_DATABASES = [ +export const IDB_LEGACY_DATABASES_REFERENCE = [ 'Drawnix', // 旧版 localforage 默认数据库 - 'drawnix', // 旧版画板数据(注意:与 CHARACTERS 数据库同名,需谨慎处理) + 'drawnix', // 旧版画板数据(注意:与 CHARACTERS 数据库同名) 'localforage', // localforage 默认数据库 - 'aitu-task-queue', // 旧版任务队列 + 'aitu-task-queue', // 旧版任务队列(迁移到 sw-task-queue) 'aitu-media-cache', // 旧版媒体缓存 'aitu-url-cache', // 旧版 URL 缓存 ] as const; @@ -239,6 +242,3 @@ export type LSKeyDeprecated = (typeof LS_KEYS_DEPRECATED)[number]; /** IndexedDB 数据库名类型 */ export type IDBDatabaseName = (typeof IDB_DATABASES)[keyof typeof IDB_DATABASES]['NAME']; - -/** 废弃的 IndexedDB 数据库名类型 */ -export type IDBLegacyDatabase = (typeof IDB_LEGACY_DATABASES)[number]; diff --git a/packages/drawnix/src/hooks/useTaskStorage.ts b/packages/drawnix/src/hooks/useTaskStorage.ts index 94ef286e..1b81f26a 100644 --- a/packages/drawnix/src/hooks/useTaskStorage.ts +++ b/packages/drawnix/src/hooks/useTaskStorage.ts @@ -9,7 +9,7 @@ */ import { useEffect } from 'react'; -import { taskQueueService, shouldUseSWTaskQueue } from '../services/task-queue'; +import { taskQueueService, shouldUseSWTaskQueue, legacyTaskQueueService } from '../services/task-queue'; import { storageService } from '../services/storage-service'; import { UPDATE_INTERVALS } from '../constants/TASK_CONSTANTS'; import { migrateLegacyHistory } from '../utils/history-migration'; @@ -70,7 +70,7 @@ export function useTaskStorage(): void { // Migrate legacy tasks from old storage to SW (one-time migration) const legacyTasks = await storageService.loadTasks(); if (legacyTasks.length > 0) { - // console.log(`[useTaskStorage] Migrating ${legacyTasks.length} legacy tasks to SW`); + console.log(`[useTaskStorage] Migrating ${legacyTasks.length} legacy tasks to SW`); // Restore legacy tasks to SW service (which will sync to SW) await swTaskQueueService.restoreTasks(legacyTasks); @@ -79,7 +79,7 @@ export function useTaskStorage(): void { for (const task of legacyTasks) { await storageService.deleteTask(task.id); } - // console.log('[useTaskStorage] Legacy tasks migrated and cleared'); + console.log('[useTaskStorage] Legacy tasks migrated and cleared'); } // Sync tasks from SW to local state @@ -122,7 +122,8 @@ export function useTaskStorage(): void { errorCode = 'INTERRUPTED_DURING_SUBMISSION'; } - taskQueueService.updateTaskStatus(task.id, TaskStatus.FAILED, { + // Use legacy service directly since this code only runs in legacy mode + legacyTaskQueueService.updateTaskStatus(task.id, TaskStatus.FAILED, { startedAt: undefined, executionPhase: undefined, // Clear execution phase error: { diff --git a/packages/drawnix/src/hooks/useWorkflowSubmission.ts b/packages/drawnix/src/hooks/useWorkflowSubmission.ts index 21ca036e..1fb89893 100644 --- a/packages/drawnix/src/hooks/useWorkflowSubmission.ts +++ b/packages/drawnix/src/hooks/useWorkflowSubmission.ts @@ -339,6 +339,11 @@ export function useWorkflowSubmission( referenceImages: string[], retryContext: WorkflowRetryContext ): Promise => { + // Ensure SW task queue is initialized before submitting workflow + // This sends TASK_QUEUE_INIT to SW which initializes the workflowHandler + const { swTaskQueueService } = await import('../services/sw-task-queue-service'); + await swTaskQueueService.initialize(); + // Convert to SW workflow format const swWorkflow: SWWorkflowDefinition = { id: legacyWorkflow.id, diff --git a/packages/drawnix/src/services/asset-storage-service.ts b/packages/drawnix/src/services/asset-storage-service.ts index 07f6623e..ed3ad336 100644 --- a/packages/drawnix/src/services/asset-storage-service.ts +++ b/packages/drawnix/src/services/asset-storage-service.ts @@ -176,40 +176,31 @@ class AssetStorageService { /** * 迁移旧版数据 - * 1. 删除 AI 生成的素材(现在从任务队列读取) - * 2. 将本地上传的旧版 Blob 数据迁移到统一缓存 + * 将旧版 Blob 数据迁移到统一缓存(包括 AI 生成的素材和本地上传的素材) + * 注意:不再删除 AI 生成的素材,因为任务队列迁移可能失败 */ private async migrateOldData(): Promise { if (this.migrationDone || !this.store) return; - const migrationKey = 'drawnix_asset_migration_v3'; // 更新版本号 + const migrationKey = 'drawnix_asset_migration_v4'; // 更新版本号,重新迁移 const migrated = localStorage.getItem(migrationKey); if (migrated === 'done') { this.migrationDone = true; return; } - // console.log('[AssetStorageService] Starting data migration v3...'); + console.log('[AssetStorageService] Starting data migration v4...'); try { const keys = await this.store.keys(); let migratedCount = 0; - let deletedAiCount = 0; for (const key of keys) { try { const item = await this.store.getItem(key) as any; if (!item) continue; - // 删除 AI 生成的素材(现在从任务队列读取) - if (item.source === 'AI_GENERATED') { - await this.store.removeItem(key); - deletedAiCount++; - // console.log(`[AssetStorageService] Deleted AI asset ${item.id}`); - continue; - } - - // 迁移本地上传的旧版数据 + // 迁移旧版数据(包含 blobData 的格式) if (this.isLegacyFormat(item)) { // 生成新的 URL const assetUrl = this.generateAssetUrl(item.id, item.mimeType); @@ -218,6 +209,8 @@ class AssetStorageService { const cacheType = item.type === 'IMAGE' ? 'image' : 'video'; await unifiedCacheService.cacheMediaFromBlob(assetUrl, item.blobData, cacheType, { taskId: item.id, + prompt: item.prompt, + model: item.modelName, }); // 更新为新格式(不含 blobData) @@ -230,11 +223,13 @@ class AssetStorageService { mimeType: item.mimeType, createdAt: item.createdAt, size: item.size, + prompt: item.prompt, + modelName: item.modelName, }; await this.store.setItem(key, newStoredAsset); migratedCount++; - // console.log(`[AssetStorageService] Migrated local asset ${item.id}`); + console.log(`[AssetStorageService] Migrated asset ${item.id} (${item.source})`); } } catch (error) { console.error(`[AssetStorageService] Failed to process asset ${key}:`, error); @@ -243,7 +238,7 @@ class AssetStorageService { localStorage.setItem(migrationKey, 'done'); this.migrationDone = true; - // console.log(`[AssetStorageService] Migration v3 completed: migrated ${migratedCount} local assets, deleted ${deletedAiCount} AI assets`); + console.log(`[AssetStorageService] Migration v4 completed: migrated ${migratedCount} assets`); } catch (error) { console.error('[AssetStorageService] Migration failed:', error); } diff --git a/packages/drawnix/src/services/db-cleanup-service.ts b/packages/drawnix/src/services/db-cleanup-service.ts index db0b58cd..ff5785b2 100644 --- a/packages/drawnix/src/services/db-cleanup-service.ts +++ b/packages/drawnix/src/services/db-cleanup-service.ts @@ -1,17 +1,11 @@ /** * Database Cleanup Service * - * Cleans up legacy/redundant IndexedDB databases and LocalStorage data - * that are no longer needed. This service runs once on app startup and - * removes old data after migration to new storage locations. + * Cleans up deprecated LocalStorage data that is no longer needed. + * This service runs once on app startup. * - * Legacy databases to clean: - * - Drawnix: Old localforage default database - * - drawnix: Old board data (migrated to aitu-workspace) - * - localforage: localforage default database - * - aitu-task-queue: Old task queue (migrated to sw-task-queue) - * - aitu-media-cache: Old media cache (migrated to drawnix-unified-cache) - * - aitu-url-cache: Old URL cache (migrated to drawnix-unified-cache) + * Note: IndexedDB database cleanup has been removed as it's a dangerous operation. + * Data migration is handled by individual services (e.g., useTaskStorage, asset-storage-service). * * Legacy LocalStorage keys to clean: * - aitu-recent-colors-shadow: Orphaned data @@ -20,80 +14,11 @@ import { LS_KEYS, LS_KEYS_DEPRECATED, - IDB_LEGACY_DATABASES, } from '../constants/storage-keys'; // Storage key to track if cleanup has been performed const CLEANUP_DONE_KEY = LS_KEYS.DB_CLEANUP_DONE; -// List of legacy databases to delete (use from constants) -const LEGACY_DATABASES = [...IDB_LEGACY_DATABASES]; - -/** - * Check if a database exists - */ -async function checkDBExists(dbName: string): Promise { - return new Promise((resolve) => { - try { - const request = indexedDB.open(dbName); - let existed = false; - - request.onupgradeneeded = () => { - // Database didn't exist, was just created - existed = false; - }; - - request.onsuccess = () => { - const db = request.result; - // Check if it has any object stores (empty DB means it was just created) - existed = db.objectStoreNames.length > 0; - db.close(); - - // If we just created an empty DB, delete it - if (!existed) { - indexedDB.deleteDatabase(dbName); - } - - resolve(existed); - }; - - request.onerror = () => { - resolve(false); - }; - } catch { - resolve(false); - } - }); -} - -/** - * Delete a database - */ -async function deleteDatabase(dbName: string): Promise { - return new Promise((resolve) => { - try { - const request = indexedDB.deleteDatabase(dbName); - - request.onsuccess = () => { - // console.log(`[DBCleanup] Deleted database: ${dbName}`); - resolve(true); - }; - - request.onerror = () => { - console.warn(`[DBCleanup] Failed to delete database: ${dbName}`); - resolve(false); - }; - - request.onblocked = () => { - console.warn(`[DBCleanup] Database deletion blocked: ${dbName}`); - resolve(false); - }; - } catch { - resolve(false); - } - }); -} - /** * Clean up deprecated LocalStorage keys */ @@ -124,40 +49,14 @@ export async function runDatabaseCleanup(): Promise { return; } - // console.log('[DBCleanup] Starting legacy database cleanup...'); - - let deletedCount = 0; - let skippedCount = 0; - - // Clean up legacy IndexedDB databases - for (const dbName of LEGACY_DATABASES) { - try { - const exists = await checkDBExists(dbName); - - if (exists) { - const deleted = await deleteDatabase(dbName); - if (deleted) { - deletedCount++; - } else { - skippedCount++; - } - } - } catch (error) { - console.warn(`[DBCleanup] Error processing database ${dbName}:`, error); - skippedCount++; - } - } - // Clean up deprecated LocalStorage keys const lsCleanedCount = cleanupDeprecatedLocalStorage(); // Mark cleanup as done localStorage.setItem(CLEANUP_DONE_KEY, Date.now().toString()); - if (deletedCount > 0 || skippedCount > 0 || lsCleanedCount > 0) { - // console.log(`[DBCleanup] Cleanup complete: IDB deleted ${deletedCount}, IDB skipped ${skippedCount}, LS cleaned ${lsCleanedCount}`); - } else { - // console.log('[DBCleanup] No legacy data found'); + if (lsCleanedCount > 0) { + // console.log(`[DBCleanup] Cleanup complete: LS cleaned ${lsCleanedCount}`); } } @@ -174,10 +73,7 @@ if (typeof window !== 'undefined') { (window as unknown as Record).__dbCleanup = { run: runDatabaseCleanup, reset: resetCleanupFlag, - checkDB: checkDBExists, - deleteDB: deleteDatabase, cleanupLS: cleanupDeprecatedLocalStorage, - LEGACY_DATABASES, LS_KEYS_DEPRECATED, }; } diff --git a/packages/drawnix/src/types/asset.types.ts b/packages/drawnix/src/types/asset.types.ts index a6048b63..26168324 100644 --- a/packages/drawnix/src/types/asset.types.ts +++ b/packages/drawnix/src/types/asset.types.ts @@ -54,7 +54,7 @@ export interface Asset { /** * Stored Asset Interface * 存储的素材接口 - IndexedDB中的数据结构 - * 注意:只存储本地上传的素材,AI 生成的素材从任务队列读取 + * 支持本地上传和 AI 生成的素材 */ export interface StoredAsset { id: string; @@ -66,6 +66,8 @@ export interface StoredAsset { createdAt: number; size?: number; contentHash?: string; // 文件内容哈希,用于去重 + prompt?: string; // AI 生成的提示词(仅 AI_GENERATED) + modelName?: string; // 生成模型名称(仅 AI_GENERATED) } /**