448 lines
No EOL
13 KiB
Markdown
448 lines
No EOL
13 KiB
Markdown
# 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
|
|
|
|
```swift
|
|
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 |