Salva telemetria client (OS, app, device, operatore) sulle sessioni per il debug admin.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-08-26 09:43:43 +02:00
co-authored by Cursor
parent 873e0ea55c
commit 9d8b35c06c
24 changed files with 408 additions and 18 deletions
@@ -1,6 +1,7 @@
import Foundation
import UIKit
import Network
import CoreTelephony
struct DeviceHealth: Sendable {
let batteryPercent: Int
@@ -8,6 +9,16 @@ struct DeviceHealth: Sendable {
let networkType: String
}
struct ClientInfoPayload: Encodable, Sendable {
let os: String
let appVersion: String?
let appBuild: String?
let deviceManufacturer: String?
let deviceModel: String?
let osVersion: String?
let carrier: String?
}
enum DeviceTelemetry {
static func snapshot(thermalState: ThermalState? = nil) -> DeviceHealth {
UIDevice.current.isBatteryMonitoringEnabled = true
@@ -20,6 +31,24 @@ enum DeviceTelemetry {
)
}
static func clientInfo() -> ClientInfoPayload {
let bundle = Bundle.main
let version = bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String
let build = bundle.object(forInfoDictionaryKey: "CFBundleVersion") as? String
let model = UIDevice.current.model
// Prefer machine identifier when available (e.g. iPhone15,2)
let machine = utsnameMachine()
return ClientInfoPayload(
os: "ios",
appVersion: version,
appBuild: build,
deviceManufacturer: "Apple",
deviceModel: machine ?? model,
osVersion: UIDevice.current.systemVersion,
carrier: carrierName()
)
}
private static func currentNetworkType() -> String {
let monitor = NWPathMonitor()
let semaphore = DispatchSemaphore(value: 0)
@@ -40,4 +69,27 @@ enum DeviceTelemetry {
monitor.cancel()
return result
}
private static func carrierName() -> String? {
let info = CTTelephonyNetworkInfo()
if let providers = info.serviceSubscriberCellularProviders {
for carrier in providers.values {
if let name = carrier.carrierName?.trimmingCharacters(in: .whitespacesAndNewlines),
!name.isEmpty {
return name
}
}
}
return nil
}
private static func utsnameMachine() -> String? {
var systemInfo = utsname()
uname(&systemInfo)
return withUnsafePointer(to: &systemInfo.machine) {
$0.withMemoryRebound(to: CChar.self, capacity: 1) {
String(validatingUTF8: $0)
}
}
}
}
@@ -435,6 +435,7 @@ struct CreateSessionRequest: Encodable {
let targetBitrate: Int
let targetFps: Int
let youtubeChannel: String?
let client: ClientInfoPayload?
}
struct AudioMuteRequest: Encodable {
@@ -531,6 +532,7 @@ struct TelemetryRequest: Encodable {
let targetBitrate: Int?
let fps: Int?
let thermalState: String?
let client: ClientInfoPayload?
}
extension ScoringRules {
@@ -25,7 +25,8 @@ final class SessionRepository {
qualityPreset: qualityPreset,
targetBitrate: targetBitrate,
targetFps: targetFps,
youtubeChannel: youtubeChannel
youtubeChannel: youtubeChannel,
client: DeviceTelemetry.clientInfo()
)
).toDomain()
}
@@ -97,7 +98,8 @@ final class SessionRepository {
currentBitrate: currentBitrate,
targetBitrate: targetBitrate,
fps: fps,
thermalState: health.thermalState.apiValue
thermalState: health.thermalState.apiValue,
client: DeviceTelemetry.clientInfo()
)
)
}