feat(ios): 中继通道 P4/P5——端点抽象、信封、传输、配对与分层错误
- InstanceEndpoint 取代 Instance.origin(旧数据 init(from:) 读成 .direct, 写盘只输出 endpoint;dedupKey 直通用 origin、中继用 relay:<nodeID>) - RelayWire:Swift 侧重算 contracts/test-vectors.json(CanonicalJSON 手写解析器, 因为 Foundation 的解码器会抹掉'拼写',而 intent 摘要定义的正是拼写) - NodeTransport 抽象 + DirectTransport / RelayTransport / NodeTransportRouter; DirectTransport 是薄适配,直通路径行为不变 - RelayTransport:信封构造、签名、加密、验签、解密;响应验签失败即丢弃, 四类以上失败各有独立 UpstreamError 分支与用户文案 - DeviceIdentity(沙盒文件 0600,配对时由节点分配 deviceID)、RelayPairing、配对 sheet - 卡片通道徽标 + freshness 副标题;危险写 intent 在发送瞬间生成 - scripts/export-app-routes.swift 生成 vendored 路由表,AppRoutesContractTests 反向守卫 - 测试 78→92:向量互算、迁移(字面量旧 JSON)、配对四类失败、路由器、 传输往返与各类拒绝路径
This commit is contained in:
@@ -19,21 +19,25 @@ public struct LogCenterService: Sendable {
|
||||
var diagnostic: InstanceDiagnostic?
|
||||
}
|
||||
|
||||
private let client: any SimAdminClientProtocol
|
||||
private let sessions: InstanceSessionStore
|
||||
private let credentials: (any CredentialStore)?
|
||||
private let transport: any NodeTransport
|
||||
private let store: any LogStore
|
||||
|
||||
public init(transport: any NodeTransport, store: any LogStore) {
|
||||
self.transport = transport
|
||||
self.store = store
|
||||
}
|
||||
|
||||
/// Direct-mode construction, kept for the existing call sites and tests.
|
||||
public init(
|
||||
client: any SimAdminClientProtocol,
|
||||
sessions: InstanceSessionStore,
|
||||
credentials: (any CredentialStore)? = nil,
|
||||
store: any LogStore
|
||||
) {
|
||||
self.client = client
|
||||
self.sessions = sessions
|
||||
self.credentials = credentials
|
||||
self.store = store
|
||||
self.init(
|
||||
transport: DirectTransport(client: client, sessions: sessions, credentials: credentials),
|
||||
store: store
|
||||
)
|
||||
}
|
||||
|
||||
public func refresh(_ instances: [Instance]) async throws -> LogSnapshot {
|
||||
@@ -115,7 +119,11 @@ public struct LogCenterService: Sendable {
|
||||
private func probe(_ instance: Instance) async -> ProbeOutcome {
|
||||
let startedAt = Date()
|
||||
do {
|
||||
let response = try await client.send(.health, baseURL: instance.origin, cookie: nil)
|
||||
let response = try await transport.send(
|
||||
.health,
|
||||
instanceID: instance.id,
|
||||
endpoint: instance.endpoint
|
||||
)
|
||||
let duration = elapsed(startedAt)
|
||||
let root = (try? JSONSerialization.jsonObject(with: response.data)) as? [String: Any] ?? [:]
|
||||
return ProbeOutcome(
|
||||
@@ -207,7 +215,7 @@ public struct LogCenterService: Sendable {
|
||||
return InstanceDiagnostic(
|
||||
id: instance.id,
|
||||
name: instance.name,
|
||||
origin: instance.origin.absoluteString,
|
||||
origin: instance.endpoint.displayAddress,
|
||||
tags: instance.tags,
|
||||
state: probe.status,
|
||||
healthStatus: Self.healthLabel(probe),
|
||||
@@ -226,30 +234,7 @@ public struct LogCenterService: Sendable {
|
||||
}
|
||||
|
||||
private func request(_ route: UpstreamRoute, _ instance: Instance) async throws -> UpstreamResponse {
|
||||
var cookie = await sessions.cookie(for: instance.id, origin: instance.origin)
|
||||
do {
|
||||
return try await client.send(route, baseURL: instance.origin, cookie: cookie)
|
||||
} catch UpstreamError.unauthorized, UpstreamError.forbidden {
|
||||
cookie = try await login(instance)
|
||||
return try await client.send(route, baseURL: instance.origin, cookie: cookie)
|
||||
}
|
||||
}
|
||||
|
||||
private func login(_ instance: Instance) async throws -> String {
|
||||
guard let credentials,
|
||||
let password = try credentials.get(instanceID: instance.id),
|
||||
!password.isEmpty
|
||||
else {
|
||||
throw UpstreamError.unauthorized
|
||||
}
|
||||
let service = InstanceSessionService(store: sessions, client: client)
|
||||
let result = try await service.login(instance, password: password)
|
||||
guard result.authenticated,
|
||||
let cookie = await sessions.cookie(for: instance.id, origin: instance.origin)
|
||||
else {
|
||||
throw UpstreamError.unauthorized
|
||||
}
|
||||
return cookie
|
||||
try await transport.send(route, instanceID: instance.id, endpoint: instance.endpoint)
|
||||
}
|
||||
|
||||
private func elapsed(_ startedAt: Date) -> Int {
|
||||
|
||||
Reference in New Issue
Block a user