import Foundation /// Authentifizierter HTTP-Transport. Setzt automatisch den `Authorization: Bearer`- /// Header, refreshed bei `401` einmal proaktiv und wiederholt den Request. /// /// Eine Instanz pro App-Backend (z.B. memoro-api, cards-api). Beim Init /// die Basis-URL des jeweiligen App-Servers übergeben, nicht die mana-auth-URL. public actor AuthenticatedTransport { private let baseURL: URL private let session: URLSession private let auth: AuthClient public init(baseURL: URL, auth: AuthClient, session: URLSession = .shared) { self.baseURL = baseURL self.session = session self.auth = auth } /// Führt einen authentifizierten Request aus. Bei `401` wird der /// Access-Token einmal refreshed und der Call wiederholt. public func request( path: String, method: String = "GET", body: Data? = nil, contentType: String = "application/json" ) async throws -> (Data, HTTPURLResponse) { let token = try await auth.freshAccessToken() let response = try await send(path: path, method: method, body: body, contentType: contentType, token: token) if response.1.statusCode == 401 { let refreshed = try await auth.refreshAccessToken() return try await send(path: path, method: method, body: body, contentType: contentType, token: refreshed) } return response } private func send( path: String, method: String, body: Data?, contentType: String, token: String ) async throws -> (Data, HTTPURLResponse) { // String-Konkatenation statt `baseURL.appending(path:)`, weil // letzteres das `?` in Query-Strings als Path-Component // behandelt und URL-encoded (`?` → `%3F`) — der Server-Routes- // Match scheitert dann mit 404. Caller liefert path inkl. // führendem `/` und optionaler Query. guard let url = URL(string: baseURL.absoluteString + path) else { throw AuthError.networkFailure("Ungültige URL: \(path)") } var request = URLRequest(url: url) request.httpMethod = method request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization") if let body { request.httpBody = body request.setValue(contentType, forHTTPHeaderField: "Content-Type") } do { let (data, response) = try await session.data(for: request) guard let http = response as? HTTPURLResponse else { throw AuthError.networkFailure("Keine HTTP-Antwort") } return (data, http) } catch let error as URLError { throw AuthError.networkFailure(error.localizedDescription) } } }