""" Fingerprint Persistence Service - Handles fingerprint storage and retrieval. """ import logging from typing import Optional, List, Dict, Any from datetime import datetime, timedelta from domain.entities.browser_fingerprint import BrowserFingerprint from domain.repositories.fingerprint_repository import IFingerprintRepository logger = logging.getLogger("fingerprint_persistence_service") class FingerprintPersistenceService: """Service for fingerprint persistence operations.""" def __init__(self, repository: IFingerprintRepository): self.repository = repository self._cache = {} # Simple in-memory cache self._cache_ttl = 300 # 5 minutes def save_fingerprint(self, fingerprint: BrowserFingerprint) -> str: """Save a fingerprint and return its ID.""" try: fingerprint_id = self.repository.save(fingerprint) # Update cache self._cache[fingerprint_id] = { 'fingerprint': fingerprint, 'timestamp': datetime.now() } logger.info(f"Saved fingerprint: {fingerprint_id}") return fingerprint_id except Exception as e: logger.error(f"Failed to save fingerprint: {e}") raise def load_fingerprint(self, fingerprint_id: str) -> Optional[BrowserFingerprint]: """Load a fingerprint by ID.""" # Check cache first if fingerprint_id in self._cache: cache_entry = self._cache[fingerprint_id] if (datetime.now() - cache_entry['timestamp']).seconds < self._cache_ttl: logger.debug(f"Loaded fingerprint from cache: {fingerprint_id}") return cache_entry['fingerprint'] # Load from repository try: fingerprint = self.repository.find_by_id(fingerprint_id) if fingerprint: # Update cache self._cache[fingerprint_id] = { 'fingerprint': fingerprint, 'timestamp': datetime.now() } logger.info(f"Loaded fingerprint from repository: {fingerprint_id}") else: logger.warning(f"Fingerprint not found: {fingerprint_id}") return fingerprint except Exception as e: logger.error(f"Failed to load fingerprint {fingerprint_id}: {e}") return None def load_fingerprint_for_account(self, account_id: str) -> Optional[BrowserFingerprint]: """Load fingerprint associated with an account.""" try: fingerprint = self.repository.find_by_account_id(account_id) if fingerprint: # Update cache self._cache[fingerprint.fingerprint_id] = { 'fingerprint': fingerprint, 'timestamp': datetime.now() } logger.info(f"Loaded fingerprint for account {account_id}") else: logger.warning(f"No fingerprint found for account {account_id}") return fingerprint except Exception as e: logger.error(f"Failed to load fingerprint for account {account_id}: {e}") return None def update_fingerprint(self, fingerprint: BrowserFingerprint) -> bool: """Update an existing fingerprint.""" try: success = self.repository.update(fingerprint) if success: # Update cache self._cache[fingerprint.fingerprint_id] = { 'fingerprint': fingerprint, 'timestamp': datetime.now() } logger.info(f"Updated fingerprint: {fingerprint.fingerprint_id}") else: logger.warning(f"Failed to update fingerprint: {fingerprint.fingerprint_id}") return success except Exception as e: logger.error(f"Failed to update fingerprint {fingerprint.fingerprint_id}: {e}") return False def delete_fingerprint(self, fingerprint_id: str) -> bool: """Delete a fingerprint.""" try: success = self.repository.delete(fingerprint_id) if success: # Remove from cache self._cache.pop(fingerprint_id, None) logger.info(f"Deleted fingerprint: {fingerprint_id}") else: logger.warning(f"Failed to delete fingerprint: {fingerprint_id}") return success except Exception as e: logger.error(f"Failed to delete fingerprint {fingerprint_id}: {e}") return False def list_fingerprints(self, limit: int = 100) -> List[BrowserFingerprint]: """List all fingerprints.""" try: fingerprints = self.repository.find_all() # Limit results if len(fingerprints) > limit: fingerprints = fingerprints[:limit] logger.info(f"Listed {len(fingerprints)} fingerprints") return fingerprints except Exception as e: logger.error(f"Failed to list fingerprints: {e}") return [] def list_recent_fingerprints(self, limit: int = 10) -> List[BrowserFingerprint]: """List recently created fingerprints.""" try: fingerprints = self.repository.find_recent(limit) logger.info(f"Listed {len(fingerprints)} recent fingerprints") return fingerprints except Exception as e: logger.error(f"Failed to list recent fingerprints: {e}") return [] def list_fingerprints_by_platform(self, platform: str) -> List[BrowserFingerprint]: """List fingerprints for a specific platform.""" try: fingerprints = self.repository.find_by_platform(platform) logger.info(f"Listed {len(fingerprints)} fingerprints for platform {platform}") return fingerprints except Exception as e: logger.error(f"Failed to list fingerprints for platform {platform}: {e}") return [] def get_fingerprint_pool(self, size: int = 10) -> List[BrowserFingerprint]: """Get a pool of random fingerprints.""" try: # Get more than needed to filter candidates = self.repository.find_recent(size * 3) # Filter for quality quality_fingerprints = [] for fp in candidates: # Skip if too old if fp.created_at: age_days = (datetime.now() - fp.created_at).days if age_days > 30: continue # Skip if account-bound if fp.account_bound: continue quality_fingerprints.append(fp) if len(quality_fingerprints) >= size: break logger.info(f"Created fingerprint pool of size {len(quality_fingerprints)}") return quality_fingerprints except Exception as e: logger.error(f"Failed to create fingerprint pool: {e}") return [] def cleanup_old_fingerprints(self, days_to_keep: int = 90) -> int: """Clean up fingerprints older than specified days.""" try: # Calculate cutoff date cutoff = datetime.now() - timedelta(days=days_to_keep) # Get all fingerprints to check all_fingerprints = self.repository.find_all() deleted_count = 0 for fp in all_fingerprints: if fp.created_at and fp.created_at < cutoff: # Skip if account-bound if fp.account_bound: continue if self.repository.delete(fp.fingerprint_id): deleted_count += 1 # Remove from cache self._cache.pop(fp.fingerprint_id, None) logger.info(f"Cleaned up {deleted_count} old fingerprints") return deleted_count except Exception as e: logger.error(f"Failed to cleanup old fingerprints: {e}") return 0 def clear_cache(self) -> None: """Clear the in-memory cache.""" self._cache.clear() logger.info("Cleared fingerprint cache") def get_statistics(self) -> Dict[str, Any]: """Get fingerprint statistics.""" try: total = self.repository.count() recent = len(self.repository.find_recent(100)) # Platform breakdown platforms = {} for platform in ['instagram', 'facebook', 'tiktok', 'twitter']: count = len(self.repository.find_by_platform(platform)) if count > 0: platforms[platform] = count return { 'total_fingerprints': total, 'recent_fingerprints': recent, 'platforms': platforms, 'cache_size': len(self._cache) } except Exception as e: logger.error(f"Failed to get statistics: {e}") return { 'total_fingerprints': 0, 'recent_fingerprints': 0, 'platforms': {}, 'cache_size': len(self._cache) }