Kordophone client for iOS and macOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

114 lines
3.5 KiB

import Foundation
public class Connection {
private var endpoint: URL?
private var token: String?
@Published public var authenticated = false
public init() { }
private func authenticatedGET(service: String,
queryItems: [String : String] = .init())
async -> Data? {
guard let token = token else {
return nil
}
let url = apiURL(service)
var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false)!
urlComponents.queryItems = queryItems.map({ (key, string) in
URLQueryItem(name: key, value: string)
})
let finalURL = urlComponents.url!
var request = URLRequest(url: finalURL)
request.httpMethod = "GET"
request.setValue("Bearer: \(token)", forHTTPHeaderField: "Authorization")
return try? await URLSession.shared.data(for: request).0
}
public func connect(info: ConnectionInfo) async {
self.token = nil
self.endpoint = URL(string: info.serverAddress)
self.authenticated = false
let body = try? JSONEncoder().encode(info.credentials)
var request = URLRequest(url: apiURL("authenticate"))
request.httpMethod = "POST"
request.httpBody = body
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
if let result = try? await URLSession.shared.data(for: request) {
if let response = result.1 as? HTTPURLResponse {
if let token = response.value(forHTTPHeaderField: "Set-Cookie") {
self.token = token.replacingOccurrences(of: "auth_token=", with: "")
DispatchQueue.main.async {
self.authenticated = true
}
}
}
}
}
public func conversations() async -> [Conversation]? {
if let data = await authenticatedGET(service: "conversations") {
if let conversations = try? JSONDecoder().decode([RawConversation].self, from: data) {
return conversations.map {
$0.conversation
}
}
}
return nil
}
public func messages(in conversation: Conversation) async -> [Message]? {
if let data = await authenticatedGET(service: "messages", queryItems: ["guid" : conversation.guid]) {
if let messages = try? JSONDecoder().decode([RawMessage].self, from: data) {
return messages.map {
$0.message
}
}
}
return nil
}
}
extension Connection {
private func apiURL(_ suffix: String) -> URL {
endpoint?
.appendingPathComponent("api")
.appendingPathComponent(suffix)
??
.init(fileURLWithPath: "")
}
}
extension Connection : ObservableObject {
}
public struct ConnectionInfo : Hashable, Codable {
public var serverAddress: String
public var credentials: Credentials
public init(serverAddress: String, credentials: Credentials) {
self.serverAddress = serverAddress
self.credentials = credentials
}
}
public struct Credentials : Hashable, Codable {
public var username: String = ""
public var password: String = ""
public init(username: String, password: String) {
self.username = username
self.password = password
}
}