refactor(auth): Replace custom cache with shared CacheManager

This commit is contained in:
cogwheel0
2025-11-24 16:38:26 +05:30
parent aadabf90ae
commit 5d4843c368
2 changed files with 84 additions and 143 deletions

View File

@@ -1,198 +1,131 @@
import 'auth_state_manager.dart'; import '../services/cache_manager.dart';
import '../utils/debug_logger.dart'; import '../utils/debug_logger.dart';
import 'auth_state_manager.dart';
/// Comprehensive caching manager for auth-related operations /// Comprehensive caching manager for auth-related operations.
/// Reduces redundant operations and improves app performance ///
/// Delegates to the shared [CacheManager] to keep TTL and eviction behavior
/// consistent across the app.
class AuthCacheManager { class AuthCacheManager {
static final AuthCacheManager _instance = AuthCacheManager._internal(); static final AuthCacheManager _instance = AuthCacheManager._internal();
factory AuthCacheManager() => _instance; factory AuthCacheManager() => _instance;
AuthCacheManager._internal(); AuthCacheManager._internal();
// Cache for various auth-related operations static const Duration _shortCache = Duration(minutes: 2);
final Map<String, dynamic> _cache = {}; static const Duration _mediumCache = Duration(minutes: 5);
final Map<String, DateTime> _cacheTimestamps = {}; 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 _userDataKey = 'user_data';
static const String _serverConnectionKey = 'server_connection'; static const String _serverConnectionKey = 'server_connection';
static const String _credentialsExistKey = 'credentials_exist'; static const String _credentialsExistKey = 'credentials_exist';
static const String _serverConfigsKey = 'server_configs'; 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) { void cacheUserData(dynamic userData) {
_cache[_userDataKey] = userData; _cache.write<dynamic>(_userDataKey, userData, ttl: _mediumCache);
_cacheTimestamps[_userDataKey] = DateTime.now();
DebugLogger.storage('User data cached'); DebugLogger.storage('User data cached');
} }
/// Get cached user data
dynamic getCachedUserData() { dynamic getCachedUserData() {
if (_isCacheValid(_userDataKey, _mediumCache)) { final (hit: hit, value: user) = _cache.lookup<dynamic>(_userDataKey);
if (hit) {
DebugLogger.storage('Using cached user data'); DebugLogger.storage('Using cached user data');
return _cache[_userDataKey];
} }
return null; return user;
} }
/// Cache server connection status with short timeout
void cacheServerConnection(bool isConnected) { void cacheServerConnection(bool isConnected) {
_cache[_serverConnectionKey] = isConnected; _cache.write<bool>(_serverConnectionKey, isConnected, ttl: _shortCache);
_cacheTimestamps[_serverConnectionKey] = DateTime.now();
} }
/// Get cached server connection status
bool? getCachedServerConnection() { bool? getCachedServerConnection() {
if (_isCacheValid(_serverConnectionKey, _shortCache)) { final (hit: hit, value: connection) = _cache.lookup<bool>(
return _cache[_serverConnectionKey] as bool?; _serverConnectionKey,
} );
return null; return hit ? connection : null;
} }
/// Cache credentials existence with medium timeout
void cacheCredentialsExist(bool exist) { void cacheCredentialsExist(bool exist) {
_cache[_credentialsExistKey] = exist; _cache.write<bool>(_credentialsExistKey, exist, ttl: _mediumCache);
_cacheTimestamps[_credentialsExistKey] = DateTime.now();
} }
/// Get cached credentials existence
bool? getCachedCredentialsExist() { bool? getCachedCredentialsExist() {
if (_isCacheValid(_credentialsExistKey, _mediumCache)) { final (hit: hit, value: hasCreds) = _cache.lookup<bool>(
return _cache[_credentialsExistKey] as bool?; _credentialsExistKey,
} );
return null; return hit ? hasCreds : null;
} }
/// Cache server configurations with long timeout
void cacheServerConfigs(List<dynamic> configs) { void cacheServerConfigs(List<dynamic> configs) {
_cache[_serverConfigsKey] = configs; _cache.write<List<dynamic>>(_serverConfigsKey, configs, ttl: _longCache);
_cacheTimestamps[_serverConfigsKey] = DateTime.now();
} }
/// Get cached server configurations
List<dynamic>? getCachedServerConfigs() { List<dynamic>? getCachedServerConfigs() {
if (_isCacheValid(_serverConfigsKey, _longCache)) { final (hit: hit, value: configs) = _cache.lookup<List<dynamic>>(
return _cache[_serverConfigsKey] as List<dynamic>?; _serverConfigsKey,
} );
return null; 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) { void clearCacheEntry(String key) {
_cache.remove(key); _cache.invalidate(key);
_cacheTimestamps.remove(key);
DebugLogger.storage('Cache entry cleared: $key'); DebugLogger.storage('Cache entry cleared: $key');
} }
/// Clear all auth-related cache including server configs
void clearAuthCache() { void clearAuthCache() {
_cache.clear(); _cache.clear();
_cacheTimestamps.clear(); DebugLogger.storage(
DebugLogger.storage('All auth cache cleared (including server configs and custom headers)'); 'All auth cache cleared (including server configs and custom headers)',
);
} }
/// Clear expired cache entries
void cleanExpiredCache() { void cleanExpiredCache() {
final now = DateTime.now(); final stats = _cache.stats();
final expiredKeys = <String>[]; final entries = stats['entries'];
if (entries is! Map<String, dynamic>) return;
for (final entry in _cacheTimestamps.entries) { var expiredCount = 0;
// Use the longest timeout for cleanup to be conservative entries.forEach((key, value) {
if (now.difference(entry.value) > _longCache) { if (value is! Map) return;
expiredKeys.add(entry.key); 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');
} }
} }
for (final key in expiredKeys) { Map<String, dynamic> getCacheStats() => _cache.stats();
_cache.remove(key);
_cacheTimestamps.remove(key);
}
if (expiredKeys.isNotEmpty) {
DebugLogger.storage(
'Cleaned ${expiredKeys.length} expired cache entries',
);
}
}
/// Get cache statistics for monitoring
Map<String, dynamic> getCacheStats() {
final now = DateTime.now();
final stats = <String, dynamic>{};
stats['totalEntries'] = _cache.length;
stats['entries'] = <String, Map<String, dynamic>>{};
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() { void optimizeCache() {
const maxCacheSize = 20; // Reasonable limit for auth cache // CacheManager enforces maxEntries using LRU; no extra work needed.
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',
);
}
/// Cache state from AuthState for quick access
void cacheAuthState(AuthState authState) { void cacheAuthState(AuthState authState) {
if (authState.user != null) { if (authState.user != null) {
cacheUserData(authState.user); cacheUserData(authState.user);
} }
// Don't cache loading or error states
if (authState.status == AuthStatus.authenticated) { if (authState.status == AuthStatus.authenticated) {
_cache['auth_status'] = authState.status; _cache.write<AuthStatus>(
_cacheTimestamps['auth_status'] = DateTime.now(); _authStatusKey,
authState.status,
ttl: _shortCache,
);
} }
} }
/// Get cached auth status
AuthStatus? getCachedAuthStatus() { AuthStatus? getCachedAuthStatus() {
if (_isCacheValid('auth_status', _shortCache)) { final (hit: hit, value: status) = _cache.lookup<AuthStatus>(_authStatusKey);
return _cache['auth_status'] as AuthStatus?; return hit ? status : null;
}
return null;
} }
} }

View File

@@ -59,6 +59,10 @@ class OptimizedStorageService {
static const String _localFoldersKey = HiveStoreKeys.localFolders; static const String _localFoldersKey = HiveStoreKeys.localFolders;
static const String _onboardingSeenKey = PreferenceKeys.onboardingSeen; static const String _onboardingSeenKey = PreferenceKeys.onboardingSeen;
static const String _reviewerModeKey = PreferenceKeys.reviewerMode; 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) // Auth token APIs (secure storage + in-memory cache)
@@ -66,7 +70,7 @@ class OptimizedStorageService {
Future<void> saveAuthToken(String token) async { Future<void> saveAuthToken(String token) async {
try { try {
await _secureCredentialStorage.saveAuthToken(token); await _secureCredentialStorage.saveAuthToken(token);
_cacheManager.write(_authTokenKey, token); _cacheManager.write(_authTokenKey, token, ttl: _authTokenTtl);
DebugLogger.log( DebugLogger.log(
'Auth token saved and cached', 'Auth token saved and cached',
scope: 'storage/optimized', scope: 'storage/optimized',
@@ -91,7 +95,7 @@ class OptimizedStorageService {
try { try {
final token = await _secureCredentialStorage.getAuthToken(); final token = await _secureCredentialStorage.getAuthToken();
if (token != null) { if (token != null) {
_cacheManager.write(_authTokenKey, token); _cacheManager.write(_authTokenKey, token, ttl: _authTokenTtl);
} }
return token; return token;
} catch (error) { } catch (error) {
@@ -136,7 +140,7 @@ class OptimizedStorageService {
password: password, password: password,
); );
_cacheManager.write('has_credentials', true); _cacheManager.write('has_credentials', true, ttl: _credentialsFlagTtl);
DebugLogger.log( DebugLogger.log(
'Credentials saved via optimized storage', 'Credentials saved via optimized storage',
@@ -154,7 +158,11 @@ class OptimizedStorageService {
Future<Map<String, String>?> getSavedCredentials() async { Future<Map<String, String>?> getSavedCredentials() async {
try { try {
final credentials = await _secureCredentialStorage.getSavedCredentials(); final credentials = await _secureCredentialStorage.getSavedCredentials();
_cacheManager.write('has_credentials', credentials != null); _cacheManager.write(
'has_credentials',
credentials != null,
ttl: _credentialsFlagTtl,
);
return credentials; return credentials;
} catch (error) { } catch (error) {
DebugLogger.log( DebugLogger.log(
@@ -243,7 +251,7 @@ class OptimizedStorageService {
} else { } else {
await _preferencesBox.delete(_activeServerIdKey); await _preferencesBox.delete(_activeServerIdKey);
} }
_cacheManager.write(_activeServerIdKey, serverId); _cacheManager.write(_activeServerIdKey, serverId, ttl: _serverIdTtl);
} }
Future<String?> getActiveServerId() async { Future<String?> getActiveServerId() async {
@@ -254,7 +262,7 @@ class OptimizedStorageService {
return cachedId; return cachedId;
} }
final serverId = _preferencesBox.get(_activeServerIdKey) as String?; final serverId = _preferencesBox.get(_activeServerIdKey) as String?;
_cacheManager.write(_activeServerIdKey, serverId); _cacheManager.write(_activeServerIdKey, serverId, ttl: _serverIdTtl);
return serverId; return serverId;
} }