13 KiB
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
-
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
-
State Management
- Maintains current license level
- Tracks key inventory
- Manages activation state
- Handles undo state and timers
-
Business Logic
- Determines feature availability
- Calculates keys needed for features
- Handles demo mode timing
- Manages bulk operations
-
Event Broadcasting
- Notifies UI of license changes
- Updates feature gating system
- Tracks analytics events
-
Advanced Features
- 1-hour undo mechanism
- Bulk key activation
- Usage analytics
- Error recovery
-
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