Files
wehub-resource-sync a789495a98
FreeBSD Smoke / FreeBSD Smoke (x86_64) (push) Has been cancelled
CI / Quality Guardrails (push) Has been cancelled
CI / Build & Test (macos-latest) (push) Has been cancelled
CI / Build & Test (ubuntu-latest) (push) Has been cancelled
CI / Build & Test (windows-latest) (push) Has been cancelled
CI / Format (push) Has been cancelled
CI / PowerShell Syntax (push) Has been cancelled
CI / Windows Cross-Target Check (Linux) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:10:34 +08:00

78 lines
2.7 KiB
Swift

import Foundation
/// Exchanges a pairing code for a long-lived auth token via `POST /pair`.
public struct PairingClient: Sendable {
public struct Response: Equatable, Sendable {
public var token: String
public var serverName: String
public var serverVersion: String
public init(token: String, serverName: String, serverVersion: String) {
self.token = token
self.serverName = serverName
self.serverVersion = serverVersion
}
}
public enum PairingError: Error, Equatable {
case invalidCode(String)
case serverError(statusCode: Int, message: String)
case invalidResponse
}
private let session: URLSession
public init(session: URLSession = .shared) {
self.session = session
}
public func pair(
gateway: Gateway,
code: String,
deviceID: String,
deviceName: String
) async throws -> Response {
var request = URLRequest(url: gateway.pairURL)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.timeoutInterval = 15
let body: [String: String] = [
"code": code,
"device_id": deviceID,
"device_name": deviceName,
]
request.httpBody = try JSONSerialization.data(withJSONObject: body)
let (data, response) = try await session.data(for: request)
guard let http = response as? HTTPURLResponse else {
throw PairingError.invalidResponse
}
let object = (try? JSONSerialization.jsonObject(with: data)) as? [String: Any] ?? [:]
guard http.statusCode == 200 else {
let message = object["error"] as? String ?? "HTTP \(http.statusCode)"
if http.statusCode == 401 {
throw PairingError.invalidCode(message)
}
throw PairingError.serverError(statusCode: http.statusCode, message: message)
}
guard let token = object["token"] as? String, !token.isEmpty else {
throw PairingError.invalidResponse
}
return Response(
token: token,
serverName: object["server_name"] as? String ?? "jcode",
serverVersion: object["server_version"] as? String ?? "unknown"
)
}
/// Probes `GET /health`. Returns true when the gateway is reachable.
public func checkHealth(gateway: Gateway) async -> Bool {
var request = URLRequest(url: gateway.healthURL)
request.timeoutInterval = 5
guard let (_, response) = try? await session.data(for: request),
let http = response as? HTTPURLResponse
else { return false }
return http.statusCode == 200
}
}