132 lines
4.1 KiB
Swift
132 lines
4.1 KiB
Swift
import Foundation
|
|
import CryptoKit
|
|
import PushAPI
|
|
import SwiftUI
|
|
|
|
final class API {
|
|
|
|
@AppStorage("server")
|
|
var server: String = ""
|
|
|
|
var url: URL? {
|
|
URL(string: server)
|
|
}
|
|
|
|
init() {
|
|
}
|
|
|
|
init(server: URL) {
|
|
self.server = server.path
|
|
}
|
|
|
|
init(server: URL, application: ApplicationId) {
|
|
self.server = server.path
|
|
self.application = application
|
|
}
|
|
|
|
@AppStorage("application")
|
|
var application: ApplicationId = ""
|
|
|
|
private static let encoder = JSONEncoder()
|
|
private static let decoder = JSONDecoder()
|
|
|
|
func register(token: PushToken, name: String) async -> AuthenticationToken? {
|
|
let device = DeviceRegistration(
|
|
pushToken: token,
|
|
application: application,
|
|
name: name)
|
|
guard let token = await post(.registerNewDevice, body: device) else {
|
|
print("Failed to register")
|
|
return nil
|
|
}
|
|
guard token.count == 16 else {
|
|
print("Failed to register: Unexpected token length: \(token.count)")
|
|
return nil
|
|
}
|
|
return token
|
|
}
|
|
|
|
func getDeviceList(pushToken: PushToken, authToken: AuthenticationToken) async -> [DeviceRegistration] {
|
|
let device = DeviceAuthentication(pushToken: pushToken, authentication: authToken)
|
|
guard let data = await post(.listDevicesInApplication, body: device) else {
|
|
print("Devices: Failed")
|
|
return []
|
|
}
|
|
do {
|
|
return try API.decoder.decode([DeviceRegistration].self, from: data)
|
|
} catch {
|
|
print("Devices: Failed to decode response")
|
|
return []
|
|
}
|
|
}
|
|
|
|
func getUnconfirmedDevices(masterKey: String) async -> [DeviceRegistration] {
|
|
let hash = hash(masterKey)
|
|
guard let data = await post(.listUnapprovedDevices, bodyData: hash) else {
|
|
print("Devices: Failed")
|
|
return []
|
|
}
|
|
do {
|
|
return try API.decoder.decode([DeviceRegistration].self, from: data)
|
|
} catch {
|
|
print("Devices: Failed to decode response")
|
|
return []
|
|
}
|
|
}
|
|
|
|
private func hash(_ masterKey: String) -> Data {
|
|
Data(SHA256.hash(data: masterKey.data(using: .utf8)!))
|
|
}
|
|
|
|
func confirm(pushToken: PushToken, with masterKey: String) async -> Bool {
|
|
let hash = hash(masterKey)
|
|
let device = DeviceDecision(pushToken: pushToken, masterKeyHash: hash)
|
|
return await post(.approveDevice, body: device) != nil
|
|
}
|
|
|
|
func reject(pushToken: PushToken, with masterKey: String) async -> Bool {
|
|
let hash = hash(masterKey)
|
|
let device = DeviceDecision(pushToken: pushToken, masterKeyHash: hash)
|
|
return await post(.rejectDevice, body: device) != nil
|
|
}
|
|
|
|
func isConfirmed(token: PushToken, authentication: AuthenticationToken) async -> Bool {
|
|
let device = DeviceAuthentication(pushToken: token, authentication: authentication)
|
|
return await post(.isDeviceApproved, body: device) != nil
|
|
}
|
|
|
|
func send(push: AuthenticatedPushMessage) async -> Bool {
|
|
await post(.sendPushNotification, body: push) != nil
|
|
}
|
|
|
|
private func post<T>(_ route: Route, body: T) async -> Data? where T: Encodable {
|
|
let bodyData = try! API.encoder.encode(body)
|
|
return await post(route, bodyData: bodyData)
|
|
}
|
|
|
|
private func post(_ route: Route, bodyData: Data) async -> Data? {
|
|
guard let url = url else {
|
|
return nil
|
|
}
|
|
var request = URLRequest(url: url.appendingPathComponent(route.rawValue))
|
|
request.httpBody = bodyData
|
|
request.httpMethod = "POST"
|
|
do {
|
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
guard let httpResponse = response as? HTTPURLResponse else {
|
|
return nil
|
|
}
|
|
guard httpResponse.statusCode == 200 else {
|
|
print("Failed with code: \(httpResponse.statusCode)")
|
|
return nil
|
|
}
|
|
return data
|
|
} catch {
|
|
print("Failed with error: \(error)")
|
|
return nil
|
|
}
|
|
}
|
|
|
|
|
|
}
|