diff --git a/lib/core/auth/auth_cache_manager.dart b/lib/core/auth/auth_cache_manager.dart index 2be9fa8..0d81bc0 100644 --- a/lib/core/auth/auth_cache_manager.dart +++ b/lib/core/auth/auth_cache_manager.dart @@ -1,198 +1,131 @@ -import 'auth_state_manager.dart'; +import '../services/cache_manager.dart'; import '../utils/debug_logger.dart'; +import 'auth_state_manager.dart'; -/// Comprehensive caching manager for auth-related operations -/// Reduces redundant operations and improves app performance +/// Comprehensive caching manager for auth-related operations. +/// +/// Delegates to the shared [CacheManager] to keep TTL and eviction behavior +/// consistent across the app. class AuthCacheManager { static final AuthCacheManager _instance = AuthCacheManager._internal(); factory AuthCacheManager() => _instance; AuthCacheManager._internal(); - // Cache for various auth-related operations - final Map _cache = {}; - final Map _cacheTimestamps = {}; + static const Duration _shortCache = Duration(minutes: 2); + static const Duration _mediumCache = Duration(minutes: 5); + static const Duration _longCache = Duration(minutes: 15); - // Cache timeouts for different types of data - static const Duration _shortCache = Duration( - minutes: 2, - ); // For frequently changing data - static const Duration _mediumCache = Duration( - minutes: 5, - ); // For moderately stable data - static const Duration _longCache = Duration(minutes: 15); // For stable data - - // Cache keys static const String _userDataKey = 'user_data'; static const String _serverConnectionKey = 'server_connection'; static const String _credentialsExistKey = 'credentials_exist'; static const String _serverConfigsKey = 'server_configs'; + static const String _authStatusKey = 'auth_status'; + + final CacheManager _cache = CacheManager( + defaultTtl: _mediumCache, + maxEntries: 32, + ); - /// Cache user data with medium timeout void cacheUserData(dynamic userData) { - _cache[_userDataKey] = userData; - _cacheTimestamps[_userDataKey] = DateTime.now(); + _cache.write(_userDataKey, userData, ttl: _mediumCache); DebugLogger.storage('User data cached'); } - /// Get cached user data dynamic getCachedUserData() { - if (_isCacheValid(_userDataKey, _mediumCache)) { + final (hit: hit, value: user) = _cache.lookup(_userDataKey); + if (hit) { DebugLogger.storage('Using cached user data'); - return _cache[_userDataKey]; } - return null; + return user; } - /// Cache server connection status with short timeout void cacheServerConnection(bool isConnected) { - _cache[_serverConnectionKey] = isConnected; - _cacheTimestamps[_serverConnectionKey] = DateTime.now(); + _cache.write(_serverConnectionKey, isConnected, ttl: _shortCache); } - /// Get cached server connection status bool? getCachedServerConnection() { - if (_isCacheValid(_serverConnectionKey, _shortCache)) { - return _cache[_serverConnectionKey] as bool?; - } - return null; + final (hit: hit, value: connection) = _cache.lookup( + _serverConnectionKey, + ); + return hit ? connection : null; } - /// Cache credentials existence with medium timeout void cacheCredentialsExist(bool exist) { - _cache[_credentialsExistKey] = exist; - _cacheTimestamps[_credentialsExistKey] = DateTime.now(); + _cache.write(_credentialsExistKey, exist, ttl: _mediumCache); } - /// Get cached credentials existence bool? getCachedCredentialsExist() { - if (_isCacheValid(_credentialsExistKey, _mediumCache)) { - return _cache[_credentialsExistKey] as bool?; - } - return null; + final (hit: hit, value: hasCreds) = _cache.lookup( + _credentialsExistKey, + ); + return hit ? hasCreds : null; } - /// Cache server configurations with long timeout void cacheServerConfigs(List configs) { - _cache[_serverConfigsKey] = configs; - _cacheTimestamps[_serverConfigsKey] = DateTime.now(); + _cache.write>(_serverConfigsKey, configs, ttl: _longCache); } - /// Get cached server configurations List? getCachedServerConfigs() { - if (_isCacheValid(_serverConfigsKey, _longCache)) { - return _cache[_serverConfigsKey] as List?; - } - return null; + final (hit: hit, value: configs) = _cache.lookup>( + _serverConfigsKey, + ); + return hit ? configs : null; } - /// Check if cache entry is valid - bool _isCacheValid(String key, Duration timeout) { - final timestamp = _cacheTimestamps[key]; - if (timestamp == null) return false; - - return DateTime.now().difference(timestamp) < timeout; - } - - /// Clear specific cache entry void clearCacheEntry(String key) { - _cache.remove(key); - _cacheTimestamps.remove(key); + _cache.invalidate(key); DebugLogger.storage('Cache entry cleared: $key'); } - /// Clear all auth-related cache including server configs void clearAuthCache() { _cache.clear(); - _cacheTimestamps.clear(); - DebugLogger.storage('All auth cache cleared (including server configs and custom headers)'); - } - - /// Clear expired cache entries - void cleanExpiredCache() { - final now = DateTime.now(); - final expiredKeys = []; - - for (final entry in _cacheTimestamps.entries) { - // Use the longest timeout for cleanup to be conservative - if (now.difference(entry.value) > _longCache) { - expiredKeys.add(entry.key); - } - } - - for (final key in expiredKeys) { - _cache.remove(key); - _cacheTimestamps.remove(key); - } - - if (expiredKeys.isNotEmpty) { - DebugLogger.storage( - 'Cleaned ${expiredKeys.length} expired cache entries', - ); - } - } - - /// Get cache statistics for monitoring - Map getCacheStats() { - final now = DateTime.now(); - final stats = {}; - - stats['totalEntries'] = _cache.length; - stats['entries'] = >{}; - - for (final key in _cache.keys) { - final timestamp = _cacheTimestamps[key]; - if (timestamp != null) { - stats['entries'][key] = { - 'age': now.difference(timestamp).inSeconds, - 'hasData': _cache[key] != null, - }; - } - } - - return stats; - } - - /// Optimize cache by removing least recently used entries if cache gets too large - void optimizeCache() { - const maxCacheSize = 20; // Reasonable limit for auth cache - - if (_cache.length <= maxCacheSize) return; - - // Sort by timestamp (oldest first) - final sortedEntries = _cacheTimestamps.entries.toList() - ..sort((a, b) => a.value.compareTo(b.value)); - - // Remove oldest entries - final entriesToRemove = sortedEntries.length - maxCacheSize; - for (int i = 0; i < entriesToRemove; i++) { - final key = sortedEntries[i].key; - _cache.remove(key); - _cacheTimestamps.remove(key); - } - DebugLogger.storage( - 'Cache optimized, removed $entriesToRemove old entries', + 'All auth cache cleared (including server configs and custom headers)', ); } - /// Cache state from AuthState for quick access + void cleanExpiredCache() { + final stats = _cache.stats(); + final entries = stats['entries']; + if (entries is! Map) return; + + var expiredCount = 0; + entries.forEach((key, value) { + if (value is! Map) return; + final ageSeconds = value['ageSeconds']; + final ttlSeconds = value['ttlSeconds']; + if (ageSeconds is num && ttlSeconds is num && ageSeconds > ttlSeconds) { + _cache.invalidate(key); + expiredCount++; + } + }); + + if (expiredCount > 0) { + DebugLogger.storage('Cleaned $expiredCount expired auth cache entries'); + } + } + + Map getCacheStats() => _cache.stats(); + + void optimizeCache() { + // CacheManager enforces maxEntries using LRU; no extra work needed. + } + void cacheAuthState(AuthState authState) { if (authState.user != null) { cacheUserData(authState.user); } - - // Don't cache loading or error states if (authState.status == AuthStatus.authenticated) { - _cache['auth_status'] = authState.status; - _cacheTimestamps['auth_status'] = DateTime.now(); + _cache.write( + _authStatusKey, + authState.status, + ttl: _shortCache, + ); } } - /// Get cached auth status AuthStatus? getCachedAuthStatus() { - if (_isCacheValid('auth_status', _shortCache)) { - return _cache['auth_status'] as AuthStatus?; - } - return null; + final (hit: hit, value: status) = _cache.lookup(_authStatusKey); + return hit ? status : null; } } diff --git a/lib/core/services/optimized_storage_service.dart b/lib/core/services/optimized_storage_service.dart index 000026f..4e989e6 100644 --- a/lib/core/services/optimized_storage_service.dart +++ b/lib/core/services/optimized_storage_service.dart @@ -59,6 +59,10 @@ class OptimizedStorageService { static const String _localFoldersKey = HiveStoreKeys.localFolders; static const String _onboardingSeenKey = PreferenceKeys.onboardingSeen; static const String _reviewerModeKey = PreferenceKeys.reviewerMode; + // Longer TTLs to reduce secure storage churn for OpenWebUI sessions. + static const Duration _authTokenTtl = Duration(hours: 12); + static const Duration _serverIdTtl = Duration(days: 7); + static const Duration _credentialsFlagTtl = Duration(hours: 12); // --------------------------------------------------------------------------- // Auth token APIs (secure storage + in-memory cache) @@ -66,7 +70,7 @@ class OptimizedStorageService { Future saveAuthToken(String token) async { try { await _secureCredentialStorage.saveAuthToken(token); - _cacheManager.write(_authTokenKey, token); + _cacheManager.write(_authTokenKey, token, ttl: _authTokenTtl); DebugLogger.log( 'Auth token saved and cached', scope: 'storage/optimized', @@ -91,7 +95,7 @@ class OptimizedStorageService { try { final token = await _secureCredentialStorage.getAuthToken(); if (token != null) { - _cacheManager.write(_authTokenKey, token); + _cacheManager.write(_authTokenKey, token, ttl: _authTokenTtl); } return token; } catch (error) { @@ -136,7 +140,7 @@ class OptimizedStorageService { password: password, ); - _cacheManager.write('has_credentials', true); + _cacheManager.write('has_credentials', true, ttl: _credentialsFlagTtl); DebugLogger.log( 'Credentials saved via optimized storage', @@ -154,7 +158,11 @@ class OptimizedStorageService { Future?> getSavedCredentials() async { try { final credentials = await _secureCredentialStorage.getSavedCredentials(); - _cacheManager.write('has_credentials', credentials != null); + _cacheManager.write( + 'has_credentials', + credentials != null, + ttl: _credentialsFlagTtl, + ); return credentials; } catch (error) { DebugLogger.log( @@ -243,7 +251,7 @@ class OptimizedStorageService { } else { await _preferencesBox.delete(_activeServerIdKey); } - _cacheManager.write(_activeServerIdKey, serverId); + _cacheManager.write(_activeServerIdKey, serverId, ttl: _serverIdTtl); } Future getActiveServerId() async { @@ -254,7 +262,7 @@ class OptimizedStorageService { return cachedId; } final serverId = _preferencesBox.get(_activeServerIdKey) as String?; - _cacheManager.write(_activeServerIdKey, serverId); + _cacheManager.write(_activeServerIdKey, serverId, ttl: _serverIdTtl); return serverId; }