| | 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(); |