feat: add remote reboot support

This commit is contained in:
2026-04-19 16:53:17 +02:00
parent 92040e5c5e
commit afbb425e3b
11 changed files with 381 additions and 19 deletions

View File

@@ -18,6 +18,7 @@ protocol ServerAPIProtocol {
func fetchMemory() async throws -> MemoryType
func fetchUtilization() async throws -> UtilizationType
func fetchServerSummary(apiKey: String) async throws -> ServerInfo
func restartServer(apiKey: String) async throws
}
struct SystemInfo: Codable {
@@ -36,6 +37,15 @@ class BaseAPIClient {
}
func performRequest<T: Codable>(_ request: URLRequest, responseType: T.Type) async throws -> T {
let (data, _) = try await performDataRequest(request)
return try JSONDecoder().decode(T.self, from: data)
}
func performRequestWithoutBody(_ request: URLRequest) async throws {
_ = try await performDataRequest(request)
}
private func performDataRequest(_ request: URLRequest) async throws -> (Data, HTTPURLResponse) {
let (data, response) = try await session.data(for: request)
guard let httpResponse = response as? HTTPURLResponse else {
@@ -43,25 +53,61 @@ class BaseAPIClient {
}
guard 200...299 ~= httpResponse.statusCode else {
throw APIError.httpError(httpResponse.statusCode)
throw APIError.httpError(
httpResponse.statusCode,
BaseAPIClient.extractErrorMessage(from: data)
)
}
return try JSONDecoder().decode(T.self, from: data)
return (data, httpResponse)
}
private static func extractErrorMessage(from data: Data) -> String? {
guard !data.isEmpty else { return nil }
if let envelope = try? JSONDecoder().decode(APIErrorEnvelope.self, from: data) {
let parts = [envelope.code, envelope.message]
.compactMap { $0?.trimmingCharacters(in: .whitespacesAndNewlines) }
.filter { !$0.isEmpty }
if !parts.isEmpty {
return parts.joined(separator: " ")
}
}
if let text = String(data: data, encoding: .utf8)?
.trimmingCharacters(in: .whitespacesAndNewlines),
!text.isEmpty {
return text
}
return nil
}
}
enum APIError: Error, LocalizedError {
case invalidURL
case invalidResponse
case httpError(Int)
case httpError(Int, String?)
case decodingError(Error)
case unsupportedFeature(String)
var errorDescription: String? {
switch self {
case .invalidURL: return "Invalid URL"
case .invalidResponse: return "Invalid response"
case .httpError(let code): return "HTTP Error: \(code)"
case .httpError(let code, let message):
if let message, !message.isEmpty {
return "HTTP Error: \(code)\n\(message)"
}
return "HTTP Error: \(code)"
case .decodingError(let error): return "Decoding error: \(error.localizedDescription)"
case .unsupportedFeature(let feature): return "\(feature) is not supported by this host"
}
}
}
private struct APIErrorEnvelope: Decodable {
let code: String?
let message: String?
}