macsync/@packages/imail/Sources/IMailSync/APIClient.swift

114 lines
3.6 KiB
Swift

import Alamofire
import Foundation
import LilithAgentCore
import LilithLogging
import MacSyncShared
import SwiftyJSON
private let log = AppLogger.logger(for: "IMail.API")
// MARK: - Payload Types
public struct EmailAddressPayload {
public let address: String
public let name: String?
var dictionary: [String: Any?] { ["address": address, "name": name] }
}
public struct SyncEmailPayload {
public let messageId: String
public let threadId: String?
public let subject: String?
public let fromAddress: String
public let fromName: String?
public let to: [EmailAddressPayload]
public let cc: [EmailAddressPayload]
public let folder: String
public let direction: String
public let textBody: String?
public let htmlBody: String?
public let hasAttachments: Bool?
public let attachmentCount: Int?
public let sentAt: String
public let receivedAt: String?
public let isRead: Bool?
var dictionary: [String: Any?] {
[
"messageId": messageId,
"threadId": threadId,
"subject": subject,
"fromAddress": fromAddress,
"fromName": fromName,
"to": to.map { $0.dictionary },
"cc": cc.map { $0.dictionary },
"folder": folder,
"direction": direction,
"textBody": textBody,
"htmlBody": htmlBody,
"hasAttachments": hasAttachments,
"attachmentCount": attachmentCount,
"sentAt": sentAt,
"receivedAt": receivedAt,
"isRead": isRead,
]
}
}
public struct IMailStatsResponse {
public let totalEmails: Int
public let totalFolders: Int
public let lastSyncAt: Date?
}
// MARK: - Protocol
public protocol IMailAPIClientProtocol: AnyObject {
var isAuthenticated: Bool { get }
func syncMail(_ payloads: [SyncEmailPayload]) async throws -> Int
func getStats() async throws -> IMailStatsResponse
}
// MARK: - APIClient
public final class APIClient: BaseAPIClient, IMailAPIClientProtocol, @unchecked Sendable {
public static let shared: APIClient = {
APIClient(baseURL: macSyncResolveServerURL(), keychain: macSyncSharedKeychain)
}()
public var isAuthenticated: Bool {
(try? getAuthToken()) != nil
}
// MARK: - Mail Sync
public func syncMail(_ payloads: [SyncEmailPayload]) async throws -> Int {
let params: [String: Any] = ["emails": payloads.map { $0.dictionary }]
let data = try await authenticatedRequest("/client/imail/sync", method: .post, parameters: params)
let json = JSON(data)
guard json["success"].boolValue else {
let msg = json["error"]["message"].stringValue
throw APIError.serverError(statusCode: json["statusCode"].intValue,
message: msg.isEmpty ? "Server error" : msg)
}
let synced = json["data"]["synced"].intValue
log.info("syncMail success synced=\(synced)")
return synced
}
// MARK: - Stats
public func getStats() async throws -> IMailStatsResponse {
let data = try await authenticatedRequest("/client/imail/stats", method: .get)
let json = JSON(data)
guard json["success"].boolValue else {
throw APIError.serverError(statusCode: 0, message: json["error"]["message"].stringValue)
}
return IMailStatsResponse(
totalEmails: json["data"]["totalEmails"].intValue,
totalFolders: json["data"]["totalFolders"].intValue,
lastSyncAt: json["data"]["lastSyncAt"].string.flatMap { ISO8601DateFormatter().date(from: $0) }
)
}
}