New SDK components extracted from product apps: - BLBlobClient — Azure Blob Storage upload via SAS tokens (from LysnrAI BlobService) - BLKillSwitchClient — Kill switch check from platform-service (from LysnrAI KillSwitchService) - BLLicenseClient — License key activation + status (from LysnrAI LicenseService) - BLBiometricAuth — Face ID / Touch ID wrapper (from LysnrAI BiometricAuth) - BLCrashReporter — MetricKit crash reporting (from ChronoMind CrashReporter) - BLAuditLogger — Local rotating JSON audit log (from LysnrAI AuditLogger) SDK now has 13 source files. Updated README with full component table and migration status (3 apps fully migrated, 18 wrappers total).
104 lines
3.5 KiB
Swift
104 lines
3.5 KiB
Swift
// ── License Client ──────────────────────────────────────────
|
|
// Generic license key activation via platform-service.
|
|
// Flow: enter key → POST /api/licenses/activate → receive tokens.
|
|
// Product apps configure with BLPlatformConfig.
|
|
|
|
import Foundation
|
|
|
|
/// License information returned from platform-service.
|
|
public struct BLLicenseInfo: Codable, Sendable {
|
|
public let key: String
|
|
public let plan: String
|
|
public let status: String
|
|
public let devicesUsed: Int
|
|
public let maxDevices: Int
|
|
public let expiresAt: String?
|
|
|
|
public init(key: String, plan: String, status: String, devicesUsed: Int, maxDevices: Int, expiresAt: String?) {
|
|
self.key = key
|
|
self.plan = plan
|
|
self.status = status
|
|
self.devicesUsed = devicesUsed
|
|
self.maxDevices = maxDevices
|
|
self.expiresAt = expiresAt
|
|
}
|
|
}
|
|
|
|
/// Activation result containing tokens + license info.
|
|
public struct BLActivationResult: Sendable {
|
|
public let accessToken: String
|
|
public let refreshToken: String
|
|
public let license: BLLicenseInfo
|
|
}
|
|
|
|
/// Generic license client for all ByteLyst iOS apps.
|
|
/// Handles license key activation and status checking via platform-service.
|
|
public final class BLLicenseClient {
|
|
|
|
private let config: BLPlatformConfig
|
|
private let client: BLPlatformClient
|
|
|
|
public init(config: BLPlatformConfig, client: BLPlatformClient) {
|
|
self.config = config
|
|
self.client = client
|
|
}
|
|
|
|
// MARK: - Activate
|
|
|
|
/// Activate a license key on this device.
|
|
/// Returns activation result with tokens and license info.
|
|
public func activate(key: String, deviceId: String, deviceName: String) async throws -> BLActivationResult {
|
|
let body: [String: String] = [
|
|
"key": key.uppercased().trimmingCharacters(in: .whitespaces),
|
|
"deviceId": deviceId,
|
|
"deviceName": deviceName,
|
|
"platform": config.platform,
|
|
]
|
|
|
|
let (data, _) = try await client.rawRequest(path: "/api/licenses/activate", method: "POST", body: body)
|
|
|
|
struct ActivateResponse: Codable {
|
|
let accessToken: String
|
|
let refreshToken: String
|
|
let license: LicenseDoc
|
|
}
|
|
struct LicenseDoc: Codable {
|
|
let key: String
|
|
let plan: String
|
|
let status: String
|
|
let deviceIds: [String]
|
|
let maxDevices: Int
|
|
let expiresAt: String?
|
|
let userId: String
|
|
}
|
|
|
|
let result = try JSONDecoder().decode(ActivateResponse.self, from: data)
|
|
|
|
let info = BLLicenseInfo(
|
|
key: result.license.key,
|
|
plan: result.license.plan,
|
|
status: result.license.status,
|
|
devicesUsed: result.license.deviceIds.count,
|
|
maxDevices: result.license.maxDevices,
|
|
expiresAt: result.license.expiresAt
|
|
)
|
|
|
|
return BLActivationResult(
|
|
accessToken: result.accessToken,
|
|
refreshToken: result.refreshToken,
|
|
license: info
|
|
)
|
|
}
|
|
|
|
// MARK: - Status
|
|
|
|
/// Check license status without activating.
|
|
public func checkStatus(key: String) async throws -> BLLicenseInfo {
|
|
let encodedKey = key.uppercased().trimmingCharacters(in: .whitespaces)
|
|
return try await client.request(
|
|
path: "/api/licenses/status/\(encodedKey)",
|
|
responseType: BLLicenseInfo.self
|
|
)
|
|
}
|
|
}
|