| import localforage from 'localforage'; |
|
|
| class CacheService { |
| constructor() { |
| this.storage = localforage.createInstance({ |
| name: 'LinAppCache', |
| storeName: 'authCache' |
| }); |
| this.cachePrefix = 'lin_cache_'; |
| this.defaultTTL = 7 * 24 * 60 * 60 * 1000; |
| } |
|
|
| |
| |
| |
| |
| |
| _generateKey(key) { |
| return `${this.cachePrefix}${key}`; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| async set(key, data, ttl = this.defaultTTL) { |
| const cacheKey = this._generateKey(key); |
| const expiry = Date.now() + ttl; |
| |
| const cacheData = { |
| data, |
| expiry, |
| metadata: { |
| createdAt: Date.now(), |
| ttl, |
| key |
| } |
| }; |
|
|
| try { |
| await this.storage.setItem(cacheKey, cacheData); |
| if (import.meta.env.VITE_NODE_ENV === 'development') { |
| console.log(`ποΈ [Cache] Set cache for key: ${key}`); |
| } |
| } catch (error) { |
| console.error(`ποΈ [Cache] Error setting cache for key: ${key}`, error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async get(key) { |
| const cacheKey = this._generateKey(key); |
| |
| try { |
| const cacheData = await this.storage.getItem(cacheKey); |
| |
| if (!cacheData) { |
| if (import.meta.env.VITE_NODE_ENV === 'development') { |
| console.log(`ποΈ [Cache] Cache miss for key: ${key}`); |
| } |
| return null; |
| } |
|
|
| |
| if (Date.now() > cacheData.expiry) { |
| await this.remove(key); |
| if (import.meta.env.VITE_NODE_ENV === 'development') { |
| console.log(`ποΈ [Cache] Cache expired for key: ${key}`); |
| } |
| return null; |
| } |
|
|
| if (import.meta.env.VITE_NODE_ENV === 'development') { |
| console.log(`ποΈ [Cache] Cache hit for key: ${key}`); |
| } |
| |
| return cacheData.data; |
| } catch (error) { |
| console.error(`ποΈ [Cache] Error getting cache for key: ${key}`, error); |
| return null; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async remove(key) { |
| const cacheKey = this._generateKey(key); |
| |
| try { |
| await this.storage.removeItem(cacheKey); |
| if (import.meta.env.VITE_NODE_ENV === 'development') { |
| console.log(`ποΈ [Cache] Removed cache for key: ${key}`); |
| } |
| } catch (error) { |
| console.error(`ποΈ [Cache] Error removing cache for key: ${key}`, error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| async clear() { |
| try { |
| await this.storage.clear(); |
| if (import.meta.env.VITE_NODE_ENV === 'development') { |
| console.log('ποΈ [Cache] Cleared all cache'); |
| } |
| } catch (error) { |
| console.error('ποΈ [Cache] Error clearing cache', error); |
| throw error; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async exists(key) { |
| const cacheKey = this._generateKey(key); |
| |
| try { |
| const cacheData = await this.storage.getItem(cacheKey); |
| |
| if (!cacheData) { |
| return false; |
| } |
|
|
| |
| if (Date.now() > cacheData.expiry) { |
| await this.remove(key); |
| return false; |
| } |
|
|
| return true; |
| } catch (error) { |
| console.error(`ποΈ [Cache] Error checking cache existence for key: ${key}`, error); |
| return false; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| async getMetadata(key) { |
| const cacheKey = this._generateKey(key); |
| |
| try { |
| const cacheData = await this.storage.getItem(cacheKey); |
| |
| if (!cacheData) { |
| return null; |
| } |
|
|
| return cacheData.metadata || null; |
| } catch (error) { |
| console.error(`ποΈ [Cache] Error getting cache metadata for key: ${key}`, error); |
| return null; |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| async setAuthCache(authData, rememberMe = false) { |
| const ttl = rememberMe ? this.defaultTTL : 60 * 60 * 1000; |
| |
| await this.set('auth_token', authData.token, ttl); |
| await this.set('user_data', authData.user, ttl); |
| await this.set('remember_me', rememberMe, ttl); |
| await this.set('auth_expiry', Date.now() + ttl, ttl); |
| |
| |
| const deviceFingerprint = this.generateDeviceFingerprint(); |
| await this.set('device_fingerprint', deviceFingerprint, ttl); |
| } |
|
|
| |
| |
| |
| |
| async getAuthCache() { |
| try { |
| const [token, userData, rememberMe, expiry, deviceFingerprint] = await Promise.all([ |
| this.get('auth_token'), |
| this.get('user_data'), |
| this.get('remember_me'), |
| this.get('auth_expiry'), |
| this.get('device_fingerprint') |
| ]); |
|
|
| if (!token || !userData || !expiry) { |
| return null; |
| } |
|
|
| |
| if (Date.now() > expiry) { |
| await this.clearAuthCache(); |
| return null; |
| } |
|
|
| |
| const currentFingerprint = this.generateDeviceFingerprint(); |
| if (deviceFingerprint && deviceFingerprint !== currentFingerprint) { |
| await this.clearAuthCache(); |
| return null; |
| } |
|
|
| return { |
| token, |
| user: userData, |
| rememberMe: rememberMe || false, |
| expiresAt: expiry, |
| deviceFingerprint |
| }; |
| } catch (error) { |
| console.error('ποΈ [Cache] Error getting auth cache', error); |
| return null; |
| } |
| } |
|
|
| |
| |
| |
| |
| async clearAuthCache() { |
| const keys = ['auth_token', 'user_data', 'remember_me', 'auth_expiry', 'device_fingerprint']; |
| |
| await Promise.all(keys.map(key => this.remove(key))); |
| |
| |
| localStorage.removeItem('token'); |
| localStorage.removeItem('rememberMePreference'); |
| } |
|
|
| |
| |
| |
| |
| generateDeviceFingerprint() { |
| const userAgent = navigator.userAgent; |
| const screenResolution = `${screen.width}x${screen.height}`; |
| const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; |
| const language = navigator.language; |
| |
| |
| const fingerprint = `${userAgent}-${screenResolution}-${timezone}-${language}`; |
| return btoa(fingerprint).replace(/[^a-zA-Z0-9]/g, '').substring(0, 32); |
| } |
|
|
| |
| |
| |
| |
| |
| async setUserPreferences(preferences) { |
| await this.set('user_preferences', preferences, 30 * 24 * 60 * 60 * 1000); |
| } |
|
|
| |
| |
| |
| |
| async getUserPreferences() { |
| return await this.get('user_preferences'); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| async cacheApiResponse(endpoint, data, ttl = 5 * 60 * 1000) { |
| await this.set(`api_${endpoint}`, data, ttl); |
| } |
|
|
| |
| |
| |
| |
| |
| async getCachedApiResponse(endpoint) { |
| return await this.get(`api_${endpoint}`); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| async setAuthToken(token, rememberMe = false) { |
| const ttl = rememberMe ? this.defaultTTL : 60 * 60 * 1000; |
| await this.set('auth_token', token, ttl); |
| await this.set('remember_me', rememberMe, ttl); |
| await this.set('auth_expiry', Date.now() + ttl, ttl); |
| } |
|
|
| |
| |
| |
| |
| async getAuthToken() { |
| try { |
| const [token, rememberMe, expiry] = await Promise.all([ |
| this.get('auth_token'), |
| this.get('remember_me'), |
| this.get('auth_expiry') |
| ]); |
|
|
| if (!token || !expiry) { |
| return null; |
| } |
|
|
| |
| if (Date.now() > expiry) { |
| await this.clearAuthToken(); |
| return null; |
| } |
|
|
| return { |
| token, |
| rememberMe: rememberMe || false, |
| expiresAt: expiry |
| }; |
| } catch (error) { |
| console.error('ποΈ [Cache] Error getting auth token', error); |
| return null; |
| } |
| } |
|
|
| |
| |
| |
| |
| async clearAuthToken() { |
| const keys = ['auth_token', 'remember_me', 'auth_expiry']; |
| await Promise.all(keys.map(key => this.remove(key))); |
| localStorage.removeItem('token'); |
| localStorage.removeItem('rememberMePreference'); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| async setUserData(userData, rememberMe = false) { |
| const ttl = rememberMe ? this.defaultTTL : 60 * 60 * 1000; |
| await this.set('user_data', userData, ttl); |
| } |
|
|
| |
| |
| |
| |
| async getUserData() { |
| return await this.get('user_data'); |
| } |
|
|
| |
| |
| |
| |
| |
| async setSessionInfo(sessionData) { |
| const ttl = sessionData.rememberMe ? this.defaultTTL : 60 * 60 * 1000; |
| await this.set('session_info', sessionData, ttl); |
| } |
|
|
| |
| |
| |
| |
| async getSessionInfo() { |
| return await this.get('session_info'); |
| } |
| } |
|
|
| |
| export default new CacheService(); |