UNPKG

capacitor-cors-bypass-enhanced

Version:

Enhanced Capacitor plugin for CORS bypass with HTTP/2, HTTP/3, gRPC, GraphQL, file operations, and advanced networking features. Modular TypeScript definitions for better maintainability.

640 lines (553 loc) 17.7 kB
/** * Cache Manager Implementation * 缓存管理器实现 */ import type { CacheEntry, CacheStats, CacheOptions, CacheEvictionPolicy, CacheEvictionOptions, } from '../types/cache'; import type { CacheManager } from '../types/cache'; // Re-export CacheManager for external use export type { CacheManager } from '../types/cache'; /** * Memory Cache Manager * 内存缓存管理器 */ export class MemoryCacheManager implements CacheManager { private cache: Map<string, CacheEntry> = new Map(); private accessCount: Map<string, number> = new Map(); private accessTime: Map<string, number> = new Map(); private stats = { hits: 0, misses: 0, }; private maxSize: number; private evictionPolicy: CacheEvictionPolicy; private cleanupInterval?: NodeJS.Timeout; constructor( maxSize: number = 50 * 1024 * 1024, // 50MB evictionOptions?: CacheEvictionOptions ) { this.maxSize = maxSize; this.evictionPolicy = evictionOptions?.policy || 'lru'; // 启动自动清理 if (evictionOptions?.interval) { this.startAutoCleanup(evictionOptions.interval); } } async get(key: string): Promise<CacheEntry | null> { const entry = this.cache.get(key); if (!entry) { this.stats.misses++; return null; } // 检查是否过期 if (Date.now() > entry.expiresAt) { await this.delete(key); this.stats.misses++; return null; } // 更新访问统计 this.accessCount.set(key, (this.accessCount.get(key) || 0) + 1); this.accessTime.set(key, Date.now()); this.stats.hits++; return entry; } async set(key: string, entry: CacheEntry): Promise<void> { // 检查是否需要清理空间 const currentSize = await this.getSize(); if (currentSize + entry.size > this.maxSize) { await this.evict(entry.size); } this.cache.set(key, entry); this.accessCount.set(key, 1); this.accessTime.set(key, Date.now()); } async delete(key: string): Promise<boolean> { this.accessCount.delete(key); this.accessTime.delete(key); return this.cache.delete(key); } async clear(): Promise<void> { this.cache.clear(); this.accessCount.clear(); this.accessTime.clear(); this.stats.hits = 0; this.stats.misses = 0; } async has(key: string): Promise<boolean> { const entry = this.cache.get(key); if (!entry) return false; // 检查是否过期 if (Date.now() > entry.expiresAt) { await this.delete(key); return false; } return true; } async keys(): Promise<string[]> { return Array.from(this.cache.keys()); } async getStats(): Promise<CacheStats> { const entries = Array.from(this.cache.values()); const totalSize = entries.reduce((sum, entry) => sum + entry.size, 0); const timestamps = entries.map(e => e.timestamp); return { totalEntries: this.cache.size, totalSize, hits: this.stats.hits, misses: this.stats.misses, hitRate: this.stats.hits / (this.stats.hits + this.stats.misses) || 0, oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : undefined, newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : undefined, }; } async cleanup(): Promise<number> { const now = Date.now(); let cleaned = 0; for (const [key, entry] of this.cache.entries()) { if (now > entry.expiresAt) { await this.delete(key); cleaned++; } } return cleaned; } async getSize(): Promise<number> { let totalSize = 0; for (const entry of this.cache.values()) { totalSize += entry.size; } return totalSize; } /** * 清理缓存以腾出空间 */ private async evict(requiredSpace: number): Promise<void> { const currentSize = await this.getSize(); const targetSize = this.maxSize * 0.7; // 清理到70% let freedSpace = 0; const entries = Array.from(this.cache.entries()); const sortedEntries = this.sortEntriesForEviction(entries); for (const [key] of sortedEntries) { if (currentSize - freedSpace <= targetSize && freedSpace >= requiredSpace) { break; } const entry = this.cache.get(key); if (entry) { freedSpace += entry.size; await this.delete(key); } } } /** * 根据清理策略排序条目 */ private sortEntriesForEviction( entries: Array<[string, CacheEntry]> ): Array<[string, CacheEntry]> { switch (this.evictionPolicy) { case 'lru': // 最近最少使用 return entries.sort((a, b) => { const timeA = this.accessTime.get(a[0]) || 0; const timeB = this.accessTime.get(b[0]) || 0; return timeA - timeB; }); case 'lfu': // 最不经常使用 return entries.sort((a, b) => { const countA = this.accessCount.get(a[0]) || 0; const countB = this.accessCount.get(b[0]) || 0; return countA - countB; }); case 'fifo': // 先进先出 return entries.sort((a, b) => a[1].timestamp - b[1].timestamp); case 'ttl': // 基于过期时间 return entries.sort((a, b) => a[1].expiresAt - b[1].expiresAt); default: return entries; } } /** * 启动自动清理 */ private startAutoCleanup(interval: number): void { this.cleanupInterval = setInterval(() => { this.cleanup().catch(console.error); }, interval); } /** * 停止自动清理 */ stopAutoCleanup(): void { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = undefined; } } /** * 销毁缓存管理器 */ destroy(): void { this.stopAutoCleanup(); this.clear(); } } /** * LocalStorage Cache Manager * LocalStorage 缓存管理器 */ export class LocalStorageCacheManager implements CacheManager { private prefix: string; private stats = { hits: 0, misses: 0, }; constructor(prefix: string = 'cache_') { this.prefix = prefix; } async get(key: string): Promise<CacheEntry | null> { try { const data = localStorage.getItem(this.prefix + key); if (!data) { this.stats.misses++; return null; } const entry: CacheEntry = JSON.parse(data); // 检查是否过期 if (Date.now() > entry.expiresAt) { await this.delete(key); this.stats.misses++; return null; } this.stats.hits++; return entry; } catch (error) { console.error('[Cache] Error reading from localStorage:', error); this.stats.misses++; return null; } } async set(key: string, entry: CacheEntry): Promise<void> { try { localStorage.setItem(this.prefix + key, JSON.stringify(entry)); } catch (error) { console.error('[Cache] Error writing to localStorage:', error); // 如果存储失败(可能是空间不足),尝试清理 await this.cleanup(); try { localStorage.setItem(this.prefix + key, JSON.stringify(entry)); } catch (retryError) { console.error('[Cache] Failed to write after cleanup:', retryError); } } } async delete(key: string): Promise<boolean> { try { localStorage.removeItem(this.prefix + key); return true; } catch (error) { console.error('[Cache] Error deleting from localStorage:', error); return false; } } async clear(): Promise<void> { const keys = await this.keys(); for (const key of keys) { await this.delete(key); } this.stats.hits = 0; this.stats.misses = 0; } async has(key: string): Promise<boolean> { const entry = await this.get(key); return entry !== null; } async keys(): Promise<string[]> { const keys: string[] = []; for (let i = 0; i < localStorage.length; i++) { const key = localStorage.key(i); if (key && key.startsWith(this.prefix)) { keys.push(key.substring(this.prefix.length)); } } return keys; } async getStats(): Promise<CacheStats> { const keys = await this.keys(); let totalSize = 0; const timestamps: number[] = []; for (const key of keys) { const entry = await this.get(key); if (entry) { totalSize += entry.size; timestamps.push(entry.timestamp); } } return { totalEntries: keys.length, totalSize, hits: this.stats.hits, misses: this.stats.misses, hitRate: this.stats.hits / (this.stats.hits + this.stats.misses) || 0, oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : undefined, newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : undefined, }; } async cleanup(): Promise<number> { const keys = await this.keys(); const now = Date.now(); let cleaned = 0; for (const key of keys) { const entry = await this.get(key); if (entry && now > entry.expiresAt) { await this.delete(key); cleaned++; } } return cleaned; } async getSize(): Promise<number> { const keys = await this.keys(); let totalSize = 0; for (const key of keys) { const entry = await this.get(key); if (entry) { totalSize += entry.size; } } return totalSize; } } /** * IndexedDB Cache Manager * IndexedDB 缓存管理器(用于大数据量) */ export class IndexedDBCacheManager implements CacheManager { private dbName: string; private storeName: string; private db: IDBDatabase | null = null; private stats = { hits: 0, misses: 0, }; constructor(dbName: string = 'CacheDB', storeName: string = 'cache') { this.dbName = dbName; this.storeName = storeName; } private async getDB(): Promise<IDBDatabase> { if (this.db) return this.db; return new Promise((resolve, reject) => { const request = indexedDB.open(this.dbName, 1); request.onerror = () => reject(request.error); request.onsuccess = () => { this.db = request.result; resolve(request.result); }; request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; if (!db.objectStoreNames.contains(this.storeName)) { const store = db.createObjectStore(this.storeName, { keyPath: 'key' }); store.createIndex('expiresAt', 'expiresAt', { unique: false }); store.createIndex('timestamp', 'timestamp', { unique: false }); } }; }); } async get(key: string): Promise<CacheEntry | null> { try { const db = await this.getDB(); const transaction = db.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const request = store.get(key); return new Promise((resolve, reject) => { request.onsuccess = () => { const entry = request.result as CacheEntry | undefined; if (!entry) { this.stats.misses++; resolve(null); return; } // 检查是否过期 if (Date.now() > entry.expiresAt) { this.delete(key); this.stats.misses++; resolve(null); return; } this.stats.hits++; resolve(entry); }; request.onerror = () => { this.stats.misses++; reject(request.error); }; }); } catch (error) { console.error('[Cache] Error reading from IndexedDB:', error); this.stats.misses++; return null; } } async set(key: string, entry: CacheEntry): Promise<void> { try { const db = await this.getDB(); const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const request = store.put(entry); return new Promise((resolve, reject) => { request.onsuccess = () => resolve(); request.onerror = () => reject(request.error); }); } catch (error) { console.error('[Cache] Error writing to IndexedDB:', error); throw error; } } async delete(key: string): Promise<boolean> { try { const db = await this.getDB(); const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const request = store.delete(key); return new Promise((resolve, reject) => { request.onsuccess = () => resolve(true); request.onerror = () => reject(request.error); }); } catch (error) { console.error('[Cache] Error deleting from IndexedDB:', error); return false; } } async clear(): Promise<void> { try { const db = await this.getDB(); const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const request = store.clear(); return new Promise((resolve, reject) => { request.onsuccess = () => { this.stats.hits = 0; this.stats.misses = 0; resolve(); }; request.onerror = () => reject(request.error); }); } catch (error) { console.error('[Cache] Error clearing IndexedDB:', error); throw error; } } async has(key: string): Promise<boolean> { const entry = await this.get(key); return entry !== null; } async keys(): Promise<string[]> { try { const db = await this.getDB(); const transaction = db.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const request = store.getAllKeys(); return new Promise((resolve, reject) => { request.onsuccess = () => resolve(request.result as string[]); request.onerror = () => reject(request.error); }); } catch (error) { console.error('[Cache] Error getting keys from IndexedDB:', error); return []; } } async getStats(): Promise<CacheStats> { try { const db = await this.getDB(); const transaction = db.transaction([this.storeName], 'readonly'); const store = transaction.objectStore(this.storeName); const request = store.getAll(); return new Promise((resolve, reject) => { request.onsuccess = () => { const entries = request.result as CacheEntry[]; const totalSize = entries.reduce((sum, entry) => sum + entry.size, 0); const timestamps = entries.map(e => e.timestamp); resolve({ totalEntries: entries.length, totalSize, hits: this.stats.hits, misses: this.stats.misses, hitRate: this.stats.hits / (this.stats.hits + this.stats.misses) || 0, oldestEntry: timestamps.length > 0 ? Math.min(...timestamps) : undefined, newestEntry: timestamps.length > 0 ? Math.max(...timestamps) : undefined, }); }; request.onerror = () => reject(request.error); }); } catch (error) { console.error('[Cache] Error getting stats from IndexedDB:', error); return { totalEntries: 0, totalSize: 0, hits: this.stats.hits, misses: this.stats.misses, hitRate: 0, }; } } async cleanup(): Promise<number> { try { const db = await this.getDB(); const transaction = db.transaction([this.storeName], 'readwrite'); const store = transaction.objectStore(this.storeName); const index = store.index('expiresAt'); const now = Date.now(); const range = IDBKeyRange.upperBound(now); const request = index.openCursor(range); let cleaned = 0; return new Promise((resolve, reject) => { request.onsuccess = (event) => { const cursor = (event.target as IDBRequest).result as IDBCursorWithValue; if (cursor) { cursor.delete(); cleaned++; cursor.continue(); } else { resolve(cleaned); } }; request.onerror = () => reject(request.error); }); } catch (error) { console.error('[Cache] Error cleaning up IndexedDB:', error); return 0; } } async getSize(): Promise<number> { const stats = await this.getStats(); return stats.totalSize; } /** * 关闭数据库连接 */ close(): void { if (this.db) { this.db.close(); this.db = null; } } } /** * 创建缓存管理器工厂函数 */ export function createCacheManager(options?: CacheOptions): CacheManager { const storage = options?.storage || 'memory'; const maxSize = options?.maxSize || 50 * 1024 * 1024; switch (storage) { case 'localStorage': return new LocalStorageCacheManager(); case 'indexedDB': return new IndexedDBCacheManager(); case 'memory': default: return new MemoryCacheManager(maxSize); } }