docs: Thêm tài liệu kiến trúc ARCHITECTURE.md và cập nhật README.md.
This commit is contained in:
@@ -14,19 +14,28 @@ import SwiftUI
|
||||
/// API configuration constants
|
||||
/// Các hằng số cấu hình API
|
||||
enum APIConfig {
|
||||
/// Base URL for API requests
|
||||
/// URL gốc cho các request API
|
||||
static let baseURL = "https://api.goodgo.vn"
|
||||
/// Base URL for API requests (Traefik gateway)
|
||||
/// URL gốc cho các request API (Traefik gateway)
|
||||
static let baseURL = "http://localhost"
|
||||
|
||||
/// API version prefix
|
||||
/// Tiền tố phiên bản API
|
||||
static let apiVersion = "/api/v1"
|
||||
|
||||
/// OAuth2 token endpoint (no version prefix)
|
||||
/// OAuth2 token endpoint (không có version prefix)
|
||||
static let tokenEndpoint = "/connect/token"
|
||||
|
||||
/// OAuth2 scope for authentication
|
||||
/// OAuth2 scope cho xác thực
|
||||
static let oauthScope = "openid profile email offline_access"
|
||||
|
||||
/// Request timeout in seconds
|
||||
/// Thời gian timeout request (giây)
|
||||
static let timeout: TimeInterval = 30.0
|
||||
}
|
||||
|
||||
|
||||
// MARK: - App Constants
|
||||
// Hằng số ứng dụng
|
||||
|
||||
|
||||
@@ -51,6 +51,39 @@ enum APIError: Error, LocalizedError {
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - OAuth2 Response Models
|
||||
// Models response OAuth2
|
||||
|
||||
/// OAuth2 token response
|
||||
/// Response token OAuth2
|
||||
struct OAuthTokenResponse: Decodable {
|
||||
let accessToken: String
|
||||
let tokenType: String
|
||||
let expiresIn: Int
|
||||
let refreshToken: String?
|
||||
let scope: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case accessToken = "access_token"
|
||||
case tokenType = "token_type"
|
||||
case expiresIn = "expires_in"
|
||||
case refreshToken = "refresh_token"
|
||||
case scope
|
||||
}
|
||||
}
|
||||
|
||||
/// OAuth2 error response
|
||||
/// Response lỗi OAuth2
|
||||
struct OAuthErrorResponse: Decodable {
|
||||
let error: String
|
||||
let errorDescription: String?
|
||||
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case error
|
||||
case errorDescription = "error_description"
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - HTTP Method
|
||||
// Phương thức HTTP
|
||||
|
||||
@@ -232,4 +265,61 @@ final class APIService: APIServiceProtocol {
|
||||
func delete<T: Decodable>(endpoint: String) async throws -> T {
|
||||
try await request(endpoint: endpoint, method: .delete, body: nil as String?, headers: nil)
|
||||
}
|
||||
|
||||
// MARK: - OAuth2 Methods
|
||||
// Các phương thức OAuth2
|
||||
|
||||
/// POST form-urlencoded request (for OAuth2 token endpoint)
|
||||
/// Request POST dạng form-urlencoded (cho OAuth2 token endpoint)
|
||||
/// - Parameters:
|
||||
/// - endpoint: Token endpoint path / Đường dẫn token endpoint
|
||||
/// - formData: Form data dictionary / Dictionary form data
|
||||
/// - Returns: Decoded response / Response đã giải mã
|
||||
func postForm<T: Decodable>(endpoint: String, formData: [String: String]) async throws -> T {
|
||||
guard let url = URL(string: APIConfig.baseURL + endpoint) else {
|
||||
throw APIError.invalidURL
|
||||
}
|
||||
|
||||
var request = URLRequest(url: url)
|
||||
request.httpMethod = "POST"
|
||||
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
|
||||
request.timeoutInterval = APIConfig.timeout
|
||||
|
||||
// Encode form data
|
||||
// Mã hóa form data
|
||||
let formBody = formData.map { key, value in
|
||||
let escapedValue = value.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? ""
|
||||
return "\(key)=\(escapedValue)"
|
||||
}.joined(separator: "&")
|
||||
request.httpBody = formBody.data(using: .utf8)
|
||||
|
||||
// Perform request
|
||||
// Thực hiện request
|
||||
let (data, response) = try await session.data(for: request)
|
||||
|
||||
guard let httpResponse = response as? HTTPURLResponse else {
|
||||
throw APIError.unknown
|
||||
}
|
||||
|
||||
// Handle OAuth2 error responses
|
||||
// Xử lý OAuth2 error responses
|
||||
if httpResponse.statusCode != 200 {
|
||||
if let errorResponse = try? decoder.decode(OAuthErrorResponse.self, from: data) {
|
||||
throw APIError.serverError(
|
||||
statusCode: httpResponse.statusCode,
|
||||
message: errorResponse.errorDescription ?? errorResponse.error
|
||||
)
|
||||
}
|
||||
let message = String(data: data, encoding: .utf8)
|
||||
throw APIError.serverError(statusCode: httpResponse.statusCode, message: message)
|
||||
}
|
||||
|
||||
do {
|
||||
return try decoder.decode(T.self, from: data)
|
||||
} catch {
|
||||
throw APIError.decodingError(error)
|
||||
}
|
||||
}
|
||||
try await request(endpoint: endpoint, method: .delete, body: nil as String?, headers: nil)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user