feat: add summary dashboard history charts

This commit is contained in:
2026-04-21 18:03:51 +02:00
parent 0bb4be861c
commit 44f4206f34
12 changed files with 790 additions and 81 deletions

View File

@@ -37,6 +37,7 @@ protocol AnyServerAPI {
func fetchLoadData() async throws -> Any
func fetchMemoryData() async throws -> Any
func fetchUtilizationData() async throws -> Any
func fetchCPUUtilizationPercent(apiKey: String) async throws -> Double
func fetchServerSummary(apiKey: String) async throws -> ServerInfo
func restartServer(apiKey: String) async throws
}
@@ -64,6 +65,10 @@ private struct AnyServerAPIWrapper<T: ServerAPIProtocol>: AnyServerAPI {
return try await wrapped.fetchUtilization()
}
func fetchCPUUtilizationPercent(apiKey: String) async throws -> Double {
return try await wrapped.fetchCPUUtilizationPercent(apiKey: apiKey)
}
func fetchServerSummary(apiKey: String) async throws -> ServerInfo {
return try await wrapped.fetchServerSummary(apiKey: apiKey)
}

View File

@@ -17,6 +17,7 @@ protocol ServerAPIProtocol {
func fetchLoad() async throws -> LoadType
func fetchMemory() async throws -> MemoryType
func fetchUtilization() async throws -> UtilizationType
func fetchCPUUtilizationPercent(apiKey: String) async throws -> Double
func fetchServerSummary(apiKey: String) async throws -> ServerInfo
func restartServer(apiKey: String) async throws
}

View File

@@ -19,6 +19,16 @@ struct ServerInfo: Codable, Hashable, Equatable {
self.cpuCount = cpuCount
self.level = level
}
var displayPercent: Double {
let clampedPercent = min(max(percent, 0), 100)
guard clampedPercent != percent else {
return clampedPercent
}
let normalized = (minute1 / Double(max(cpuCount, 1))) * 100
return min(max(normalized, 0), 100)
}
}
struct Memory: Codable, Hashable, Equatable {
@@ -155,6 +165,7 @@ struct ServerInfo: Codable, Hashable, Equatable {
var memory: Memory
var swap: Memory
var diskSpace: DiskSpace
var cpuUtilizationPercent: Double?
var panelVersion: String
var panelBuild: String
var apiVersion: String
@@ -189,6 +200,13 @@ struct ServerInfo: Codable, Hashable, Equatable {
var supportsRestartCommand: Bool {
ServerInfo.version(apiVersion, isAtLeast: "2.14")
}
var summaryCPUPercent: Double {
if let cpuUtilizationPercent {
return min(max(cpuUtilizationPercent, 0), 100)
}
return load.displayPercent
}
}
// MARK: - Helpers & Sample Data
@@ -284,6 +302,7 @@ extension ServerInfo {
memory: Memory(free: 8_000_000_000, used: 4_000_000_000, total: 12_000_000_000, percent: 33.3),
swap: Memory(free: 4_000_000_000, used: 1_000_000_000, total: 5_000_000_000, percent: 20.0),
diskSpace: DiskSpace(free: 100_000_000_000, used: 50_000_000_000, total: 150_000_000_000, percent: 33.3),
cpuUtilizationPercent: 12.5,
panelVersion: "25.0",
panelBuild: "3394",
apiVersion: "2",

View File

@@ -168,6 +168,17 @@ class APIv2_12: BaseAPIClient, ServerAPIProtocol {
return try await performRequest(request, responseType: UtilizationType.self)
}
func fetchCPUUtilizationPercent(apiKey: String) async throws -> Double {
let url = Endpoint.utilization.url(baseURL: baseURL)
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY")
request.timeoutInterval = 30
let utilization = try await performRequest(request, responseType: UtilizationType.self)
return utilization.cpu.overall
}
func fetchServerSummary(apiKey: String) async throws -> ServerInfo {
let summaryURL = baseURL.appendingPathComponent("api/v2/server")
var request = URLRequest(url: summaryURL)
@@ -397,6 +408,7 @@ private extension APIv2_12 {
total: disk.total,
percent: disk.percent
),
cpuUtilizationPercent: nil,
panelVersion: meta.panelVersion,
panelBuild: String(meta.panelBuild),
apiVersion: meta.apiVersion,

View File

@@ -168,6 +168,17 @@ class APIv2_13: BaseAPIClient, ServerAPIProtocol {
return try await performRequest(request, responseType: UtilizationType.self)
}
func fetchCPUUtilizationPercent(apiKey: String) async throws -> Double {
let url = Endpoint.utilization.url(baseURL: baseURL)
var request = URLRequest(url: url)
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue(apiKey, forHTTPHeaderField: "X-API-KEY")
request.timeoutInterval = 30
let utilization = try await performRequest(request, responseType: UtilizationType.self)
return utilization.cpu.overall
}
func fetchServerSummary(apiKey: String) async throws -> ServerInfo {
let summaryURL = baseURL.appendingPathComponent("api/v2/server")
var request = URLRequest(url: summaryURL)
@@ -397,6 +408,7 @@ private extension APIv2_13 {
total: disk.total,
percent: disk.percent
),
cpuUtilizationPercent: nil,
panelVersion: meta.panelVersion,
panelBuild: String(meta.panelBuild),
apiVersion: meta.apiVersion,