keys-for-all/docs/components/KeyManager.md
2025-07-22 18:27:21 -07:00

13 KiB

KeyManager Component

The KeyManager is the central coordinator for the Keys for All system. It manages key activation, inventory, and coordinates with other components. Based on the comprehensive architecture, it includes advanced features like undo mechanisms, bulk operations, and enhanced security.

Pseudocode

class KeyManager {
    // Singleton instance
    static shared = KeyManager()
    
    // Current state
    @Published currentLicenseLevel: LicenseLevel = .free
    @Published keyInventory: [String] = []
    @Published isActivating: Bool = false
    
    // Dependencies
    private validator: KeyValidator
    private storage: KeyStorage
    private featureGating: FeatureGating
    private analytics: KeyAnalytics
    private undoManager: KeyUndoManager
    
    // MARK: - Key Activation
    
    func activate(key: String) async -> Result<License, KeyError> {
        isActivating = true
        defer { isActivating = false }
        
        // Step 1: Validate key format
        let validationResult = await validator.validate(key)
        
        if !validationResult.isValid {
            analytics.trackKeyEvent(KeyEvent(
                type: .activation,
                level: nil,
                success: false,
                error: validationResult.error
            ))
            return .failure(validationResult.error)
        }
        
        // Step 2: Check if key already used
        if storage.isKeyUsed(key) {
            analytics.trackKeyEvent(KeyEvent(
                type: .activation,
                level: validationResult.level,
                success: false,
                error: .alreadyActivated
            ))
            return .failure(.alreadyActivated)
        }
        
        // Step 3: Extract license level from key
        guard let level = validationResult.level else {
            return .failure(.invalidLevel)
        }
        
        // Step 4: Store previous state for undo
        let previousState = LicenseState(
            level: currentLicenseLevel,
            activatedDate: Date()
        )
        
        // Step 5: Store the key securely
        do {
            try storage.storeKey(key, level: level)
        } catch {
            return .failure(.storageFailed)
        }
        
        // Step 6: Update current license level
        currentLicenseLevel = level
        
        // Step 7: Update feature gating
        featureGating.updateAvailableFeatures(for: level)
        
        // Step 8: Setup undo mechanism
        undoManager.registerUndo(
            key: key,
            previousState: previousState,
            duration: 3600 // 1 hour
        )
        
        // Step 9: Track successful activation
        analytics.trackKeyEvent(KeyEvent(
            type: .activation,
            level: level,
            success: true,
            error: nil
        ))
        
        // Step 10: Post notification for UI updates
        NotificationCenter.post("KeysForAllLicenseChanged", level)
        
        return .success(License(key: key, level: level))
    }
    
    // MARK: - Key Inventory Management
    
    func addToInventory(keys: [String]) {
        keyInventory.append(contentsOf: keys)
        storage.saveInventory(keyInventory)
    }
    
    func shareKey(at index: Int) -> String? {
        guard index < keyInventory.count else { return nil }
        
        let key = keyInventory.remove(at: index)
        storage.saveInventory(keyInventory)
        
        return key
    }
    
    // MARK: - License Queries
    
    func hasFeature(_ feature: Feature) -> Bool {
        return currentLicenseLevel.rawValue >= feature.requiredLevel.rawValue
    }
    
    func keysNeededFor(_ feature: Feature) -> Int {
        let current = currentLicenseLevel.rawValue
        let required = feature.requiredLevel.rawValue
        return max(0, required - current)
    }
    
    // MARK: - Demo Mode
    
    func enableDemoMode(for feature: Feature, duration: TimeInterval = 30) {
        guard currentLicenseLevel < feature.requiredLevel else { return }
        
        featureGating.temporarilyUnlock(feature, for: duration)
        
        // Track demo usage
        analytics.trackKeyEvent(KeyEvent(
            type: .demo,
            level: currentLicenseLevel,
            success: true,
            error: nil
        ))
    }
    
    // MARK: - Undo System
    
    func canUndo() -> Bool {
        return undoManager.canUndo()
    }
    
    func timeRemainingForUndo() -> TimeInterval? {
        return undoManager.timeRemaining()
    }
    
    func undoLastActivation() async -> Result<Void, KeyError> {
        guard let undoAction = undoManager.getUndoAction() else {
            return .failure(.noUndoAvailable)
        }
        
        // Restore previous state
        currentLicenseLevel = undoAction.previousState.level
        
        // Update feature gating
        featureGating.updateAvailableFeatures(for: currentLicenseLevel)
        
        // Remove key from storage
        try? storage.removeKey(undoAction.key)
        
        // Track undo event
        analytics.trackKeyEvent(KeyEvent(
            type: .undo,
            level: currentLicenseLevel,
            success: true,
            error: nil
        ))
        
        // Post notification
        NotificationCenter.post("KeysForAllLicenseChanged", currentLicenseLevel)
        
        return .success(())
    }
    
    // MARK: - Bulk Operations
    
    func activateMultipleKeys(_ keys: [String]) async -> [Result<License, KeyError>] {
        var results: [Result<License, KeyError>] = []
        
        for key in keys {
            let result = await activate(key: key)
            results.append(result)
            
            // Brief delay to prevent overwhelming the system
            try? await Task.sleep(nanoseconds: 100_000_000) // 0.1 second
        }
        
        return results
    }
}

// Supporting Types
enum LicenseLevel: Int {
    case free = 0
    case level1 = 1
    case level2 = 2
}

struct License {
    let key: String
    let level: LicenseLevel
    let activatedDate: Date = Date()
}

enum KeyError: Error {
    case invalidFormat
    case checksumMismatch
    case alreadyActivated
    case unsupportedLevel
    case invalidLevel
    case storageFailed
    case noUndoAvailable
    case networkError
    case temporaryFailure
}

struct LicenseState {
    let level: LicenseLevel
    let activatedDate: Date
}

struct KeyEvent {
    let type: KeyEventType
    let level: LicenseLevel?
    let success: Bool
    let error: KeyError?
    let timestamp: Date = Date()
}

enum KeyEventType {
    case activation
    case demo
    case undo
    case sharing
    case validation
}

// MARK: - Key Undo Manager

class KeyUndoManager {
    private var undoAction: UndoAction?
    private var undoTimer: Timer?
    
    struct UndoAction {
        let key: String
        let previousState: LicenseState
        let expiryDate: Date
    }
    
    func registerUndo(key: String, previousState: LicenseState, duration: TimeInterval) {
        // Cancel any existing undo
        undoTimer?.invalidate()
        
        // Create new undo action
        undoAction = UndoAction(
            key: key,
            previousState: previousState,
            expiryDate: Date().addingTimeInterval(duration)
        )
        
        // Setup expiry timer
        undoTimer = Timer.scheduledTimer(withTimeInterval: duration, repeats: false) { _ in
            self.undoAction = nil
        }
    }
    
    func canUndo() -> Bool {
        guard let action = undoAction else { return false }
        return Date() < action.expiryDate
    }
    
    func timeRemaining() -> TimeInterval? {
        guard let action = undoAction else { return nil }
        let remaining = action.expiryDate.timeIntervalSince(Date())
        return remaining > 0 ? remaining : nil
    }
    
    func getUndoAction() -> UndoAction? {
        guard canUndo() else { return nil }
        let action = undoAction
        undoAction = nil
        undoTimer?.invalidate()
        return action
    }
}

// MARK: - Key Analytics

class KeyAnalytics {
    private let telemetry = TelemetryService()
    
    func trackKeyEvent(_ event: KeyEvent) {
        // Anonymous tracking only
        let anonymizedEvent = AnonymizedKeyEvent(
            type: event.type,
            level: event.level,
            timestamp: event.timestamp.timeIntervalSince1970,
            success: event.success
        )
        
        telemetry.record(anonymizedEvent)
    }
    
    func generateUsageReport() async -> KeyUsageReport {
        let events = await telemetry.fetchEvents()
        
        return KeyUsageReport(
            totalActivations: countActivations(events),
            activationsByLevel: groupByLevel(events),
            successRate: calculateSuccessRate(events),
            demoUsage: countDemoUsage(events),
            undoUsage: countUndoUsage(events)
        )
    }
    
    private func countActivations(_ events: [AnonymizedKeyEvent]) -> Int {
        return events.filter { $0.type == .activation && $0.success }.count
    }
    
    private func groupByLevel(_ events: [AnonymizedKeyEvent]) -> [LicenseLevel: Int] {
        var counts: [LicenseLevel: Int] = [:]
        
        for event in events {
            if event.type == .activation && event.success,
               let level = event.level {
                counts[level, default: 0] += 1
            }
        }
        
        return counts
    }
    
    private func calculateSuccessRate(_ events: [AnonymizedKeyEvent]) -> Double {
        let activations = events.filter { $0.type == .activation }
        let successful = activations.filter { $0.success }
        
        return activations.isEmpty ? 0 : Double(successful.count) / Double(activations.count)
    }
    
    private func countDemoUsage(_ events: [AnonymizedKeyEvent]) -> Int {
        return events.filter { $0.type == .demo }.count
    }
    
    private func countUndoUsage(_ events: [AnonymizedKeyEvent]) -> Int {
        return events.filter { $0.type == .undo }.count
    }
}

struct AnonymizedKeyEvent {
    let type: KeyEventType
    let level: LicenseLevel?
    let timestamp: TimeInterval
    let success: Bool
}

struct KeyUsageReport {
    let totalActivations: Int
    let activationsByLevel: [LicenseLevel: Int]
    let successRate: Double
    let demoUsage: Int
    let undoUsage: Int
}

// MARK: - Telemetry Service

class TelemetryService {
    func record(_ event: AnonymizedKeyEvent) {
        // Store events locally for analytics
        var events = loadEvents()
        events.append(event)
        
        // Keep only last 1000 events
        if events.count > 1000 {
            events = Array(events.suffix(1000))
        }
        
        saveEvents(events)
    }
    
    func fetchEvents() async -> [AnonymizedKeyEvent] {
        return loadEvents()
    }
    
    private func loadEvents() -> [AnonymizedKeyEvent] {
        guard let data = UserDefaults.standard.data(forKey: "telemetry.events"),
              let events = try? JSONDecoder().decode([AnonymizedKeyEvent].self, from: data) else {
            return []
        }
        return events
    }
    
    private func saveEvents(_ events: [AnonymizedKeyEvent]) {
        let data = try? JSONEncoder().encode(events)
        UserDefaults.standard.set(data, forKey: "telemetry.events")
    }
}

Component Responsibilities

  1. Central Coordination

    • Acts as the main entry point for all key operations
    • Coordinates between validator, storage, and UI components
    • Manages complex workflows and state transitions
  2. State Management

    • Maintains current license level
    • Tracks key inventory
    • Manages activation state
    • Handles undo state and timers
  3. Business Logic

    • Determines feature availability
    • Calculates keys needed for features
    • Handles demo mode timing
    • Manages bulk operations
  4. Event Broadcasting

    • Notifies UI of license changes
    • Updates feature gating system
    • Tracks analytics events
  5. Advanced Features

    • 1-hour undo mechanism
    • Bulk key activation
    • Usage analytics
    • Error recovery
  6. Security & Privacy

    • Secure key handling
    • Anonymous telemetry
    • Proper error handling
    • Timing attack prevention

Integration Points

  • Inputs: Raw key strings from UI, purchase receipts, bulk operations
  • Outputs: License status, feature availability, UI updates, analytics
  • Dependencies: KeyValidator, KeyStorage, FeatureGating, KeyAnalytics, KeyUndoManager
  • Observers: UI components, feature-gated views, analytics dashboard
  • Timers: Undo expiration, demo mode expiration
  • Notifications: License changes, undo availability, error states