You've already forked Nano-Banana-AI-Image-Editor
119 lines
3.7 KiB
TypeScript
119 lines
3.7 KiB
TypeScript
import { get, set, del, keys } from 'idb-keyval';
|
|
import { Project, Generation, Asset } from '../types';
|
|
|
|
const CACHE_PREFIX = 'nano-banana';
|
|
const CACHE_VERSION = '1.0';
|
|
// 限制缓存项目数量
|
|
const MAX_CACHED_ITEMS = 50;
|
|
// 限制缓存最大年龄 (3天)
|
|
const MAX_CACHE_AGE = 3 * 24 * 60 * 60 * 1000;
|
|
|
|
export class CacheService {
|
|
private static getKey(type: string, id: string): string {
|
|
return `${CACHE_PREFIX}-${CACHE_VERSION}-${type}-${id}`;
|
|
}
|
|
|
|
// Project caching
|
|
static async saveProject(project: Project): Promise<void> {
|
|
// 在保存新项目之前,清理旧缓存
|
|
await this.clearOldCache();
|
|
await set(this.getKey('project', project.id), project);
|
|
}
|
|
|
|
static async getProject(id: string): Promise<Project | null> {
|
|
return (await get(this.getKey('project', id))) || null;
|
|
}
|
|
|
|
static async getAllProjects(): Promise<Project[]> {
|
|
const allKeys = await keys();
|
|
const projectKeys = allKeys.filter(key =>
|
|
typeof key === 'string' && key.includes(`${CACHE_PREFIX}-${CACHE_VERSION}-project-`)
|
|
);
|
|
|
|
const projects = await Promise.all(
|
|
projectKeys.map(key => get(key as string))
|
|
);
|
|
|
|
return projects.filter(Boolean) as Project[];
|
|
}
|
|
|
|
// Asset caching (for offline access)
|
|
static async cacheAsset(asset: Asset, data: Blob): Promise<void> {
|
|
// 在保存新资产之前,清理旧缓存
|
|
await this.clearOldCache();
|
|
await set(this.getKey('asset', asset.id), {
|
|
asset,
|
|
data,
|
|
cachedAt: Date.now()
|
|
});
|
|
}
|
|
|
|
static async getCachedAsset(assetId: string): Promise<{ asset: Asset; data: Blob } | null> {
|
|
const cached = await get(this.getKey('asset', assetId));
|
|
return cached || null;
|
|
}
|
|
|
|
// Generation metadata caching
|
|
static async cacheGeneration(generation: Generation): Promise<void> {
|
|
// 在保存新生成记录之前,清理旧缓存
|
|
await this.clearOldCache();
|
|
await set(this.getKey('generation', generation.id), generation);
|
|
}
|
|
|
|
static async getGeneration(id: string): Promise<Generation | null> {
|
|
return (await get(this.getKey('generation', id))) || null;
|
|
}
|
|
|
|
// Clear old cache entries
|
|
static async clearOldCache(maxAge: number = MAX_CACHE_AGE): Promise<void> {
|
|
const allKeys = await keys();
|
|
const now = Date.now();
|
|
|
|
// 收集需要删除的键
|
|
const keysToDelete: string[] = [];
|
|
const validCachedItems: Array<{key: string, cachedAt: number}> = [];
|
|
|
|
for (const key of allKeys) {
|
|
if (typeof key === 'string' && key.startsWith(CACHE_PREFIX)) {
|
|
const cached = await get(key);
|
|
if (cached?.cachedAt) {
|
|
// 检查是否过期
|
|
if ((now - cached.cachedAt) > maxAge) {
|
|
keysToDelete.push(key);
|
|
} else {
|
|
validCachedItems.push({key, cachedAt: cached.cachedAt});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// 如果有效项目数量超过限制,删除最旧的项目
|
|
if (validCachedItems.length > MAX_CACHED_ITEMS) {
|
|
// 按时间排序,最旧的在前面
|
|
validCachedItems.sort((a, b) => a.cachedAt - b.cachedAt);
|
|
// 计算需要删除的数量
|
|
const excessCount = validCachedItems.length - MAX_CACHED_ITEMS;
|
|
// 添加最旧的项目到删除列表
|
|
for (let i = 0; i < excessCount; i++) {
|
|
keysToDelete.push(validCachedItems[i].key);
|
|
}
|
|
}
|
|
|
|
// 执行删除
|
|
for (const key of keysToDelete) {
|
|
await del(key);
|
|
}
|
|
}
|
|
|
|
// 清空所有缓存
|
|
static async clearAllCache(): Promise<void> {
|
|
const allKeys = await keys();
|
|
const cacheKeys = allKeys.filter(key =>
|
|
typeof key === 'string' && key.startsWith(CACHE_PREFIX)
|
|
);
|
|
|
|
for (const key of cacheKeys) {
|
|
await del(key);
|
|
}
|
|
}
|
|
} |