login flow
This commit is contained in:
@@ -39,6 +39,11 @@ enum NetworkError: Error, LocalizedError {
|
||||
}
|
||||
}
|
||||
|
||||
private struct ApiErrorDescriptor {
|
||||
let code: String?
|
||||
let message: String?
|
||||
}
|
||||
|
||||
struct ApiRequest: Sendable {
|
||||
let path: String
|
||||
let method: String
|
||||
@@ -166,6 +171,10 @@ private extension ApiClient {
|
||||
|
||||
let nsError = error as NSError
|
||||
let apiMessage = serverMessage(from: nsError)
|
||||
let payload = serverPayload(from: nsError)
|
||||
if isSessionExpiredPayload(code: payload?.code, message: payload?.message ?? apiMessage) {
|
||||
return .unauthorized(payload?.message ?? apiMessage)
|
||||
}
|
||||
|
||||
switch nsError.code {
|
||||
case 401, 403:
|
||||
@@ -228,6 +237,11 @@ private extension ApiClient {
|
||||
throw NetworkError.invalidResponse
|
||||
}
|
||||
|
||||
let payload = serverPayload(from: data)
|
||||
if isSessionExpiredPayload(code: payload?.code, message: payload?.message) {
|
||||
throw NetworkError.unauthorized(payload?.message)
|
||||
}
|
||||
|
||||
if http.statusCode == 429 {
|
||||
let retryAfter = Int(http.value(forHTTPHeaderField: "Retry-After") ?? "")
|
||||
throw NetworkError.rateLimited(retryAfter)
|
||||
@@ -283,19 +297,44 @@ private extension ApiClient {
|
||||
}
|
||||
|
||||
func serverMessage(from data: Data) -> String? {
|
||||
let payload = serverPayload(from: data)
|
||||
if let message = payload?.message, message.isEmpty == false {
|
||||
return message
|
||||
}
|
||||
if let code = payload?.code, code.isEmpty == false {
|
||||
return "Erro: \(code)"
|
||||
}
|
||||
return String(data: data, encoding: .utf8)
|
||||
}
|
||||
|
||||
func serverPayload(from data: Data) -> ApiErrorDescriptor? {
|
||||
if let envelope = try? JSONDecoder().decode(ApiEnvelope<EmptyResult>.self, from: data) {
|
||||
return envelope.message
|
||||
return ApiErrorDescriptor(code: envelope.code, message: envelope.message)
|
||||
}
|
||||
|
||||
if let object = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
|
||||
if let message = object["message"] as? String {
|
||||
return message
|
||||
}
|
||||
if let code = object["code"] as? String {
|
||||
return "Erro: \(code)"
|
||||
let code = object["code"] as? String
|
||||
let message = object["message"] as? String
|
||||
if code != nil || message != nil {
|
||||
return ApiErrorDescriptor(code: code, message: message)
|
||||
}
|
||||
}
|
||||
return String(data: data, encoding: .utf8)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func isSessionExpiredPayload(code: String?, message: String?) -> Bool {
|
||||
let normalizedCode = (code ?? "").lowercased()
|
||||
let normalizedMessage = (message ?? "").lowercased()
|
||||
|
||||
if normalizedCode.contains("auth") || normalizedCode.contains("token") || normalizedCode.contains("unauthorized") {
|
||||
return true
|
||||
}
|
||||
if normalizedMessage.contains("token") && (normalizedMessage.contains("expir") || normalizedMessage.contains("invalid") || normalizedMessage.contains("sess")) {
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
#if canImport(LCEssentials) && os(iOS)
|
||||
@@ -318,6 +357,15 @@ private extension ApiClient {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func serverPayload(from error: NSError) -> ApiErrorDescriptor? {
|
||||
if let reason = error.userInfo[NSLocalizedFailureReasonErrorKey] as? String,
|
||||
let data = reason.data(using: .utf8),
|
||||
let payload = serverPayload(from: data) {
|
||||
return payload
|
||||
}
|
||||
return nil
|
||||
}
|
||||
#endif
|
||||
|
||||
func buildURL(path: String, query: [URLQueryItem]) throws -> URL {
|
||||
|
||||
Reference in New Issue
Block a user