6 files changed
+37
-138
No files matched your search
@@ -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];
|
||||
@@ -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: {
|
||||
|
||||
@@ -339,6 +339,11 @@ export function useWorkflowSubmission(
|
||||
referenceImages: string[],
|
||||
retryContext: WorkflowRetryContext
|
||||
): Promise<string> => {
|
||||
// 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,
|
||||
|
||||
@@ -176,40 +176,31 @@ class AssetStorageService {
|
||||
|
||||
/**
|
||||
* 迁移旧版数据
|
||||
* 1. 删除 AI 生成的素材(现在从任务队列读取)
|
||||
* 2. 将本地上传的旧版 Blob 数据迁移到统一缓存
|
||||
* 将旧版 Blob 数据迁移到统一缓存(包括 AI 生成的素材和本地上传的素材)
|
||||
* 注意:不再删除 AI 生成的素材,因为任务队列迁移可能失败
|
||||
*/
|
||||
private async migrateOldData(): Promise<void> {
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -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<boolean> {
|
||||
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<boolean> {
|
||||
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<void> {
|
||||
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<string, unknown>).__dbCleanup = {
|
||||
run: runDatabaseCleanup,
|
||||
reset: resetCleanupFlag,
|
||||
checkDB: checkDBExists,
|
||||
deleteDB: deleteDatabase,
|
||||
cleanupLS: cleanupDeprecatedLocalStorage,
|
||||
LEGACY_DATABASES,
|
||||
LS_KEYS_DEPRECATED,
|
||||
};
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in new issue
Block a user