Files
multi-simadmin/apps/ios/Sources/MultiSimAdmin/Services/ConnectionProbe.swift
T
chick 2ff23d1fb0 fix(ios): improve fleet overview and app identity
- Show stable group health in a fixed 2x2 metric grid
- Add link-chain state to fleet cards and parse hardware/control/SIM/cellular
- Replace the wrapped icon with a full-bleed app icon
- Bump the IPA build number
2026-09-13 00:46:26 +08:00

98 lines
3.1 KiB
Swift

import Foundation
public protocol SimAdminClientProtocol: Sendable {
func send(_ route: UpstreamRoute, baseURL: URL, cookie: String?) async throws -> UpstreamResponse
}
extension SimAdminClient: SimAdminClientProtocol {}
public enum ConnectionStatus: String, Equatable, Sendable {
case online
case offline
case authRequired
case degraded
case unknown
public var label: String {
switch self {
case .online: return "在线"
case .offline: return "离线"
case .authRequired: return "需登录"
case .degraded: return "降级"
case .unknown: return "未知"
}
}
}
public struct ConnectionResult: Equatable, Sendable {
public let status: ConnectionStatus
public let requestLatencyMilliseconds: Int?
public let checkedAt: Date
public init(
status: ConnectionStatus,
requestLatencyMilliseconds: Int? = nil,
checkedAt: Date
) {
self.status = status
self.requestLatencyMilliseconds = requestLatencyMilliseconds
self.checkedAt = checkedAt
}
}
public struct ConnectionProbe: Sendable {
private let client: any SimAdminClientProtocol
public init(client: any SimAdminClientProtocol) {
self.client = client
}
public func test(_ instance: Instance) async throws -> ConnectionResult {
let startedAt = DispatchTime.now()
do {
let response = try await client.send(.health, baseURL: instance.origin, cookie: nil)
let latency = Self.elapsedMilliseconds(since: startedAt)
if response.status == 401 || response.status == 403 {
return ConnectionResult(
status: .authRequired,
requestLatencyMilliseconds: latency,
checkedAt: Date()
)
}
return ConnectionResult(
status: .online,
requestLatencyMilliseconds: latency,
checkedAt: Date()
)
} catch {
let latency = Self.elapsedMilliseconds(since: startedAt)
switch error {
case UpstreamError.unauthorized:
return ConnectionResult(
status: .authRequired,
requestLatencyMilliseconds: latency,
checkedAt: Date()
)
case UpstreamError.timeout, is URLError:
return ConnectionResult(
status: .offline,
requestLatencyMilliseconds: latency,
checkedAt: Date()
)
default:
return ConnectionResult(
status: .degraded,
requestLatencyMilliseconds: latency,
checkedAt: Date()
)
}
}
}
private static func elapsedMilliseconds(since startedAt: DispatchTime) -> Int? {
let nanoseconds = DispatchTime.now().uptimeNanoseconds &- startedAt.uptimeNanoseconds
let milliseconds = Double(nanoseconds) / 1_000_000
return milliseconds.isFinite ? Int(milliseconds.rounded()) : nil
}
}