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:
@@ -7,18 +7,19 @@ public enum MessageServiceError: Error, Equatable {
|
||||
}
|
||||
|
||||
public struct MessageService: Sendable {
|
||||
private let client: any SimAdminClientProtocol
|
||||
private let sessions: InstanceSessionStore
|
||||
private let credentials: (any CredentialStore)?
|
||||
private let transport: any NodeTransport
|
||||
|
||||
public init(transport: any NodeTransport) {
|
||||
self.transport = transport
|
||||
}
|
||||
|
||||
/// Direct-mode construction, kept for the existing call sites and tests.
|
||||
public init(
|
||||
client: any SimAdminClientProtocol,
|
||||
sessions: InstanceSessionStore,
|
||||
credentials: (any CredentialStore)? = nil
|
||||
) {
|
||||
self.client = client
|
||||
self.sessions = sessions
|
||||
self.credentials = credentials
|
||||
self.init(transport: DirectTransport(client: client, sessions: sessions, credentials: credentials))
|
||||
}
|
||||
|
||||
public func messages(
|
||||
@@ -75,30 +76,7 @@ public struct MessageService: 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 MessageServiceError.unauthorized
|
||||
}
|
||||
let sessions = InstanceSessionService(store: sessions, client: client)
|
||||
let result = try await sessions.login(instance, password: password)
|
||||
guard result.authenticated,
|
||||
let cookie = await sessions.cookie(for: instance)
|
||||
else {
|
||||
throw MessageServiceError.unauthorized
|
||||
}
|
||||
return cookie
|
||||
try await transport.send(route, instanceID: instance.id, endpoint: instance.endpoint)
|
||||
}
|
||||
|
||||
static func parsePage(_ response: UpstreamResponse, _ instance: Instance) throws -> MessagePage {
|
||||
|
||||
Reference in New Issue
Block a user