118 lines
3.6 KiB
Swift
118 lines
3.6 KiB
Swift
//
|
|
// ApiFactory.swift
|
|
// iKeyMon
|
|
//
|
|
// Created by tracer on 13.11.25.
|
|
//
|
|
|
|
import Foundation
|
|
|
|
enum APIVersion: String, CaseIterable {
|
|
case v2_12 = "2.12"
|
|
case v2_13 = "2.13"
|
|
|
|
static func from(versionString: String) -> APIVersion? {
|
|
if let version = APIVersion(rawValue: versionString) {
|
|
return version
|
|
}
|
|
|
|
let components = versionString.split(separator: ".").compactMap { Int($0) }
|
|
guard components.count >= 2 else { return nil }
|
|
|
|
let major = components[0]
|
|
let minor = components[1]
|
|
|
|
switch (major, minor) {
|
|
case (2, 12): return .v2_12
|
|
case (2, 13...): return .v2_13
|
|
default: return nil
|
|
}
|
|
}
|
|
}
|
|
|
|
protocol AnyServerAPI {
|
|
func fetchSystemInfo() async throws -> SystemInfo
|
|
func fetchLoadData() async throws -> Any
|
|
func fetchMemoryData() async throws -> Any
|
|
func fetchUtilizationData() async throws -> Any
|
|
func fetchServerSummary(apiKey: String) async throws -> ServerInfo
|
|
}
|
|
|
|
private struct AnyServerAPIWrapper<T: ServerAPIProtocol>: AnyServerAPI {
|
|
private let wrapped: T
|
|
|
|
init(_ wrapped: T) {
|
|
self.wrapped = wrapped
|
|
}
|
|
|
|
func fetchSystemInfo() async throws -> SystemInfo {
|
|
return try await wrapped.fetchSystemInfo()
|
|
}
|
|
|
|
func fetchLoadData() async throws -> Any {
|
|
return try await wrapped.fetchLoad()
|
|
}
|
|
|
|
func fetchMemoryData() async throws -> Any {
|
|
return try await wrapped.fetchMemory()
|
|
}
|
|
|
|
func fetchUtilizationData() async throws -> Any {
|
|
return try await wrapped.fetchUtilization()
|
|
}
|
|
|
|
func fetchServerSummary(apiKey: String) async throws -> ServerInfo {
|
|
return try await wrapped.fetchServerSummary(apiKey: apiKey)
|
|
}
|
|
}
|
|
|
|
class APIFactory {
|
|
static func createAPI(baseURL: URL, version: APIVersion) -> AnyServerAPI {
|
|
switch version {
|
|
case .v2_12:
|
|
return AnyServerAPIWrapper(APIv2_12(baseURL: baseURL))
|
|
case .v2_13:
|
|
return AnyServerAPIWrapper(APIv2_13(baseURL: baseURL))
|
|
}
|
|
}
|
|
|
|
static func createAPI(baseURL: URL, versionString: String) -> AnyServerAPI? {
|
|
guard let version = APIVersion.from(versionString: versionString) else { return nil }
|
|
return createAPI(baseURL: baseURL, version: version)
|
|
}
|
|
|
|
static func detectAndCreateAPI(baseURL: URL, apiKey: String? = nil) async throws -> AnyServerAPI {
|
|
if let apiKey, !apiKey.isEmpty {
|
|
do {
|
|
let versionURL = baseURL.appendingPathComponent("api/v2/server")
|
|
var request = URLRequest(url: versionURL)
|
|
request.httpMethod = "GET"
|
|
request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY")
|
|
request.timeoutInterval = 15
|
|
|
|
let (data, response) = try await URLSession.shared.data(for: request)
|
|
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 {
|
|
let decoder = JSONDecoder()
|
|
decoder.keyDecodingStrategy = .convertFromSnakeCase
|
|
let probe = try decoder.decode(ServerMetaProbe.self, from: data)
|
|
if let api = createAPI(baseURL: baseURL, versionString: probe.meta.apiVersion) {
|
|
return api
|
|
}
|
|
}
|
|
} catch {
|
|
// Fall back to default version below
|
|
}
|
|
}
|
|
|
|
return AnyServerAPIWrapper(APIv2_13(baseURL: baseURL))
|
|
}
|
|
}
|
|
|
|
private struct ServerMetaProbe: Decodable {
|
|
struct Meta: Decodable {
|
|
let apiVersion: String
|
|
}
|
|
|
|
let meta: Meta
|
|
}
|