Files
swiftGrammar/AIGrammar/lib/NetworkManager.swift
2024-08-12 10:49:20 +08:00

326 lines
12 KiB
Swift
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// request.swift
// AIGrammar
//
// Created by oscar on 2024/6/4.
//
import Foundation
import Alamofire
import SwiftJWT
import SwiftyBeaver
// jwt
struct MyClaims: Claims {
var deviceID: String
var gid: Int
var exp1: Int
}
// {"ret":%d, "message":"%s", "data":jsonData}
struct APIResponse<T: Decodable>: Decodable {
let ret: Int
let message: String
let data: T?
}
//
enum NetworkError: Error {
case businessError(ret: Int, message: String)
case other(Error)
}
// 使
struct GrammarCheckRsp : Codable{
let data: [GrammarRes]
}
//
struct Translation: Identifiable, Codable, Equatable {
var id = UUID()
var input: String
var translation: String
}
//
struct TranslationResponse: Codable {
let translation: String
}
//
struct WordDetails {
var word: String = ""
var explanations: [String] = []
var phrases: [String] = []
var synonyms: [String] = []
}
//
struct WordDetailsResponse: Codable {
let word: String
let explain: [String]?
let phrase: [String]?
let sync: [String]?
// Mapping to WordDetails for use in UI
func toWordDetails() -> WordDetails {
return WordDetails(
word: word,
explanations: explain ?? [],
phrases: phrase ?? [],
synonyms: sync ?? []
)
}
}
// 使
struct VIPStatusResponse: Codable {
let id: Int
let userid: String
let username: String
let vip: Int
}
// 使
struct IAPVerifyRsp: Codable {
let ret: String
//let productType : String
}
//
struct NetworkManager {
static let shared = NetworkManager()
private let jwtSecret = globalEnvironment.jwtSecret
//
func getHeaderTimezoneInfo() -> [String: String] {
let timezone = TimeZone.current
let timezoneIdentifier = timezone.identifier // "America/New_York"
let secondsFromGMT = timezone.secondsFromGMT() // GMT
return [
"timezone": timezoneIdentifier,
"secondsfromgmt": String(secondsFromGMT)
]
}
//
func checkGrammar(inputText: String, completion: @escaping (Result<[GrammarRes], NetworkError>) -> Void) {
let url = globalEnvironment.grammarURL
var headers: HTTPHeaders = createAuthorizationHeader()
let timezoneHeaders = getHeaderTimezoneInfo()
headers.add(name: "timezone", value: timezoneHeaders["timezone"]!)
headers.add(name: "secondsfromgmt", value: timezoneHeaders["secondsfromgmt"]!)
let parameters: [String: Any] = [
"input": inputText,
"lang": "eng"
]
// 使
performRequest(
endpoint: url,
parameters: parameters,
method: .post,
encoding: URLEncoding.httpBody,
headers: headers,
completion: { (result: Result<[GrammarRes], NetworkError>) in
switch result {
case .success(let results):
completion(.success(results))
case .failure(let error):
//
completion(.failure(error))
}
}
)
}
//
func fetchWordDetails(inputText: String, lang: String = "eng", completion: @escaping (Result<WordDetails, NetworkError>) -> Void) {
guard !inputText.isEmpty else { return }
let parameters: [String: Any] = ["input": inputText, "lang": lang]
let url = globalEnvironment.dictURL
var headers: HTTPHeaders = createAuthorizationHeader()
let timezoneHeaders = getHeaderTimezoneInfo()
headers.add(name: "timezone", value: timezoneHeaders["timezone"]!)
headers.add(name: "secondsfromgmt", value: timezoneHeaders["secondsfromgmt"]!)
// 使
performRequest(
endpoint: url,
parameters: parameters,
method: .post,
encoding: URLEncoding.httpBody,
headers: headers,
completion: { (result: Result<WordDetailsResponse, NetworkError>) in
switch result {
case .success(let detailsResponse):
let details = detailsResponse.toWordDetails() // Convert here
completion(.success(details))
case .failure(let error):
//
completion(.failure(error))
}
}
)
}
//
func translate(inputText: String, lang: String = "chs", completion: @escaping (Result<Translation, NetworkError>) -> Void) {
guard !inputText.isEmpty else { return }
let url = globalEnvironment.translateURL
let parameters: [String: Any] = ["input": inputText, "lang": lang]
var headers: HTTPHeaders = createAuthorizationHeader()
let timezoneHeaders = getHeaderTimezoneInfo()
headers.add(name: "timezone", value: timezoneHeaders["timezone"]!)
headers.add(name: "secondsfromgmt", value: timezoneHeaders["secondsfromgmt"]!)
// 使
performRequest(
endpoint: url,
parameters: parameters,
method: .post,
encoding: URLEncoding.httpBody,
headers: headers,
completion: { (result: Result<TranslationResponse, NetworkError>) in
switch result {
case .success(let translationResponse):
let newTranslation = Translation(input: inputText, translation: translationResponse.translation)
completion(.success(newTranslation))
case .failure(let error):
//
completion(.failure(error))
}
}
)
}
//
func sendFeedback(input: String, output: String, isPositive: Bool) {
let url = globalEnvironment.feedbackURL
let parameters: [String: Any] = [
"product": "trans",
"input": input,
"output": output,
"res": isPositive ? "good" : "bad"
]
let headers: HTTPHeaders = createAuthorizationHeader()
AF.request(url, method: .post, parameters: parameters, encoding: URLEncoding.httpBody, headers: headers).response { response in
switch response.result {
case .success(let data):
logger.info("Feedback sent successfully.", context: ["input":input, "isPositive": isPositive])
case .failure(let error):
logger.error("Error sending feedback: \(error)", context: ["input":input, "isPositive": isPositive])
}
}
}
// VIP
func getUserProfile(completion: @escaping (Result<VIPStatusResponse, NetworkError>) -> Void) {
let url = globalEnvironment.userURL
let parameters: [String: Any] = [:]
let headers: HTTPHeaders = createAuthorizationHeader()
performRequest(
endpoint: url,
parameters: parameters,
method: .post,
encoding: URLEncoding.httpBody,
headers: headers, // 使JWT token
completion: { (result: Result<VIPStatusResponse, NetworkError>) in
switch result {
case .success(let vipData):
completion(.success(vipData))
case .failure(let error):
//
completion(.failure(error))
}
}
)
}
// VIP
func IapVerify(receiptData: Data, appAccountToken: UUID?, productId: String, transactionId: UInt64, env: String, completion: @escaping (Result<IAPVerifyRsp, NetworkError>) -> Void) {
let url = globalEnvironment.iapVerifyURL
let parameters: [String: Any] = ["transid":transactionId, "appaccounttoken": appAccountToken ?? "", "productid":productId, "receiptdata":receiptData, "env": env]
let headers: HTTPHeaders = createAuthorizationHeader()
performRequest(
endpoint: url,
parameters: parameters,
method: .post,
encoding: URLEncoding.httpBody,
headers: headers, // 使JWT token
completion: { (result: Result<IAPVerifyRsp, NetworkError>) in
switch result {
case .success(let rsp):
completion(.success(rsp))
case .failure(let error):
//
completion(.failure(error))
}
}
)
}
//
func performRequest<T: Decodable>(
endpoint: String,
parameters: Parameters, // 使 Alamofire Parameters
method: HTTPMethod = .post,
encoding: URLEncoding = .httpBody,
headers: HTTPHeaders? = nil,
completion: @escaping (Result<T, NetworkError>) -> Void
) {
let url = endpoint
let defaultHeaders: HTTPHeaders = [.contentType("application/x-www-form-urlencoded")]
let combinedHeaders = headers ?? defaultHeaders
//
AF.request(url, method: .post, parameters: parameters, encoding: encoding, headers: combinedHeaders).responseDecodable(of: APIResponse<T>.self) { response in
switch response.result {
case .success(let apiResponse):
if apiResponse.ret == 0, let data = apiResponse.data {
completion(.success(data))
} else {
completion(.failure(.businessError(ret: apiResponse.ret, message: apiResponse.message)))
}
case .failure(let error):
if let httpResponse = response.response {
logger.error("network erorr.", context: ["url":url, "StatusCode":httpResponse.statusCode])
}
completion(.failure(.other(error)))
}
}
}
// jwt token
private func createAuthorizationHeader() -> HTTPHeaders {
// Generate JWT and return headers
guard let jwtToken = generateJWT(deviceID: globalEnvironment.deviceID, gID: globalEnvironment.GID, jwtSecret: jwtSecret) else {
return [:]
}
return ["Authorization": "Bearer \(jwtToken)"]
}
private func generateJWT(deviceID: String, gID: Int, jwtSecret: String) -> String? {
let claims = MyClaims(deviceID: deviceID, gid: gID, exp1: Int(Date().timeIntervalSince1970) + 86400)
var jwt = JWT(claims: claims)
do {
let jwtSigner = JWTSigner.hs256(key: Data(jwtSecret.utf8))
let signedJWT = try jwt.sign(using: jwtSigner)
return signedJWT
} catch {
logger.error("JWT encoding failed with error: \(error)")
return nil
}
}
}