From 00a628031ec1d2222d0abb1bc916823b5bc686de Mon Sep 17 00:00:00 2001 From: headlessdev Date: Sat, 26 Apr 2025 15:38:39 +0200 Subject: [PATCH] =?UTF-8?q?Finish=20=C3=BAptime=20display?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- agent/main.go | 124 ++++++++++++++++--- app/api/servers/monitoring/route.ts | 2 +- app/dashboard/servers/Servers.tsx | 4 +- app/dashboard/servers/[server_id]/Server.tsx | 17 ++- 4 files changed, 126 insertions(+), 21 deletions(-) diff --git a/agent/main.go b/agent/main.go index 7108e7b..0a858d8 100644 --- a/agent/main.go +++ b/agent/main.go @@ -7,6 +7,7 @@ import ( "encoding/json" "errors" "fmt" + "io" "net" "net/http" "net/url" @@ -36,6 +37,7 @@ type Server struct { CpuUsage sql.NullFloat64 RamUsage sql.NullFloat64 DiskUsage sql.NullFloat64 + Uptime sql.NullString } type CPUResponse struct { @@ -61,6 +63,10 @@ type FSResponse []struct { Percent float64 `json:"percent"` } +type UptimeResponse struct { + Value string `json:"value"` +} + type Notification struct { ID int Enabled bool @@ -403,25 +409,26 @@ func checkAndUpdateServerStatus(db *sql.DB, client *http.Client, servers []Serve baseURL := strings.TrimSuffix(server.MonitoringURL.String, "/") var cpuUsage, ramUsage, diskUsage float64 var online = true + var uptimeStr string // Get CPU usage cpuResp, err := client.Get(fmt.Sprintf("%s/api/4/cpu", baseURL)) if err != nil { fmt.Printf("%s CPU request failed: %v\n", logPrefix, err) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else { defer cpuResp.Body.Close() if cpuResp.StatusCode != http.StatusOK { fmt.Printf("%s Bad CPU status code: %d\n", logPrefix, cpuResp.StatusCode) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else { var cpuData CPUResponse if err := json.NewDecoder(cpuResp.Body).Decode(&cpuData); err != nil { fmt.Printf("%s Failed to parse CPU JSON: %v\n", logPrefix, err) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else { cpuUsage = cpuData.Total @@ -429,25 +436,59 @@ func checkAndUpdateServerStatus(db *sql.DB, client *http.Client, servers []Serve } } + // Get uptime if server is online + if online { + uptimeResp, err := client.Get(fmt.Sprintf("%s/api/4/uptime", baseURL)) + if err == nil && uptimeResp.StatusCode == http.StatusOK { + defer uptimeResp.Body.Close() + + // Read the response body as a string first + uptimeBytes, err := io.ReadAll(uptimeResp.Body) + if err == nil { + uptimeStr = strings.Trim(string(uptimeBytes), "\"") + + // Try to parse as JSON object first, then fallback to direct string if that fails + var uptimeData UptimeResponse + if jsonErr := json.Unmarshal(uptimeBytes, &uptimeData); jsonErr == nil && uptimeData.Value != "" { + uptimeStr = formatUptime(uptimeData.Value) + } else { + // Use the string directly + uptimeStr = formatUptime(uptimeStr) + } + + fmt.Printf("%s Uptime: %s (formatted: %s)\n", logPrefix, string(uptimeBytes), uptimeStr) + } else { + fmt.Printf("%s Failed to read uptime response: %v\n", logPrefix, err) + } + } else { + if err != nil { + fmt.Printf("%s Uptime request failed: %v\n", logPrefix, err) + } else { + fmt.Printf("%s Bad uptime status code: %d\n", logPrefix, uptimeResp.StatusCode) + uptimeResp.Body.Close() + } + } + } + if online { // Get Memory usage memResp, err := client.Get(fmt.Sprintf("%s/api/4/mem", baseURL)) if err != nil { fmt.Printf("%s Memory request failed: %v\n", logPrefix, err) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else { defer memResp.Body.Close() if memResp.StatusCode != http.StatusOK { fmt.Printf("%s Bad memory status code: %d\n", logPrefix, memResp.StatusCode) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else { var memData MemoryResponse if err := json.NewDecoder(memResp.Body).Decode(&memData); err != nil { fmt.Printf("%s Failed to parse memory JSON: %v\n", logPrefix, err) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else { ramUsage = memData.Percent @@ -461,20 +502,20 @@ func checkAndUpdateServerStatus(db *sql.DB, client *http.Client, servers []Serve fsResp, err := client.Get(fmt.Sprintf("%s/api/4/fs", baseURL)) if err != nil { fmt.Printf("%s Filesystem request failed: %v\n", logPrefix, err) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else { defer fsResp.Body.Close() if fsResp.StatusCode != http.StatusOK { fmt.Printf("%s Bad filesystem status code: %d\n", logPrefix, fsResp.StatusCode) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else { var fsData FSResponse if err := json.NewDecoder(fsResp.Body).Decode(&fsData); err != nil { fmt.Printf("%s Failed to parse filesystem JSON: %v\n", logPrefix, err) - updateServerStatus(db, server.ID, false, 0, 0, 0) + updateServerStatus(db, server.ID, false, 0, 0, 0, "") online = false } else if len(fsData) > 0 { diskUsage = fsData[0].Percent @@ -498,7 +539,7 @@ func checkAndUpdateServerStatus(db *sql.DB, client *http.Client, servers []Serve } // Update server status with metrics - updateServerStatus(db, server.ID, online, cpuUsage, ramUsage, diskUsage) + updateServerStatus(db, server.ID, online, cpuUsage, ramUsage, diskUsage, uptimeStr) // Add entry to server history ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) @@ -513,19 +554,70 @@ func checkAndUpdateServerStatus(db *sql.DB, client *http.Client, servers []Serve fmt.Printf("%s Failed to insert history: %v\n", logPrefix, err) } - fmt.Printf("%s Updated - CPU: %.2f%%, RAM: %.2f%%, Disk: %.2f%%\n", - logPrefix, cpuUsage, ramUsage, diskUsage) + fmt.Printf("%s Updated - CPU: %.2f%%, RAM: %.2f%%, Disk: %.2f%%, Uptime: %s\n", + logPrefix, cpuUsage, ramUsage, diskUsage, uptimeStr) } } -func updateServerStatus(db *sql.DB, serverID int, online bool, cpuUsage, ramUsage, diskUsage float64) { +func formatUptime(uptimeStr string) string { + // Example input: "3 days, 3:52:36" + // Target output: "28.6 13:52" + + now := time.Now() + + // Parse the uptime components + parts := strings.Split(uptimeStr, ", ") + + var days int + var timeStr string + + if len(parts) == 2 { + // Has days part and time part + _, err := fmt.Sscanf(parts[0], "%d days", &days) + if err != nil { + // Try singular "day" + _, err = fmt.Sscanf(parts[0], "%d day", &days) + if err != nil { + return uptimeStr // Return original if parsing fails + } + } + timeStr = parts[1] + } else if len(parts) == 1 { + // Only has time part (less than a day) + days = 0 + timeStr = parts[0] + } else { + return uptimeStr // Return original if format is unexpected + } + + // Parse the time component (hours:minutes:seconds) + var hours, minutes, seconds int + _, err := fmt.Sscanf(timeStr, "%d:%d:%d", &hours, &minutes, &seconds) + if err != nil { + return uptimeStr // Return original if parsing fails + } + + // Calculate the total duration + duration := time.Duration(days)*24*time.Hour + + time.Duration(hours)*time.Hour + + time.Duration(minutes)*time.Minute + + time.Duration(seconds)*time.Second + + // Calculate the start time by subtracting the duration from now + startTime := now.Add(-duration) + + // Format the result in the required format (day.month hour:minute) + return startTime.Format("2.1 15:04") +} + +func updateServerStatus(db *sql.DB, serverID int, online bool, cpuUsage, ramUsage, diskUsage float64, uptime string) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() _, err := db.ExecContext(ctx, - `UPDATE server SET online = $1, "cpuUsage" = $2::float8, "ramUsage" = $3::float8, "diskUsage" = $4::float8 - WHERE id = $5`, - online, cpuUsage, ramUsage, diskUsage, serverID, + `UPDATE server SET online = $1, "cpuUsage" = $2::float8, "ramUsage" = $3::float8, "diskUsage" = $4::float8, "uptime" = $5 + WHERE id = $6`, + online, cpuUsage, ramUsage, diskUsage, uptime, serverID, ) if err != nil { fmt.Printf("Failed to update server status (ID: %d): %v\n", serverID, err) diff --git a/app/api/servers/monitoring/route.ts b/app/api/servers/monitoring/route.ts index d24f100..a6b3d7d 100644 --- a/app/api/servers/monitoring/route.ts +++ b/app/api/servers/monitoring/route.ts @@ -28,7 +28,7 @@ export async function GET() { cpuUsage: server.cpuUsage ? parseInt(server.cpuUsage) : 0, ramUsage: server.ramUsage ? parseInt(server.ramUsage) : 0, diskUsage: server.diskUsage ? parseInt(server.diskUsage) : 0, - uptime: server.uptime ? parseInt(server.uptime) : 0 + uptime: server.uptime || "" })); return NextResponse.json(monitoringData) diff --git a/app/dashboard/servers/Servers.tsx b/app/dashboard/servers/Servers.tsx index 2cf657a..029b76f 100644 --- a/app/dashboard/servers/Servers.tsx +++ b/app/dashboard/servers/Servers.tsx @@ -99,7 +99,7 @@ interface Server { diskUsage: number; history?: ServerHistory; port: number; - uptime: number; + uptime: string; } interface GetServersResponse { @@ -1027,7 +1027,7 @@ export default function Dashboard() { {server.online && server.uptime && ( - {server.online ? `since ${server.uptime}` : 'offline'} + since {server.uptime} )} diff --git a/app/dashboard/servers/[server_id]/Server.tsx b/app/dashboard/servers/[server_id]/Server.tsx index 3f00582..f93076f 100644 --- a/app/dashboard/servers/[server_id]/Server.tsx +++ b/app/dashboard/servers/[server_id]/Server.tsx @@ -56,6 +56,7 @@ interface Server { diskUsage: number; history?: ServerHistory; port: number; + uptime?: string; } interface GetServersResponse { @@ -439,8 +440,13 @@ export default function ServerDetail() { {server.monitoring && ( -
+
+ {server.online && server.uptime && ( + + since {server.uptime} + + )}
)} @@ -600,7 +606,14 @@ export default function ServerDetail() {
{hostedVM.monitoring && ( - +
+ + {hostedVM.online && hostedVM.uptime && ( + + since {hostedVM.uptime} + + )} +
)}