diff --git a/agent/main.go b/agent/main.go index 48391bd..5be3785 100644 --- a/agent/main.go +++ b/agent/main.go @@ -28,6 +28,31 @@ type Application struct { Online bool } +type Server struct { + ID int + Name string + Monitoring bool + MonitoringURL sql.NullString + Online bool + CpuUsage sql.NullFloat64 + RamUsage sql.NullFloat64 + DiskUsage sql.NullFloat64 +} + +type CPUResponse struct { + Total float64 `json:"total"` +} + +type MemoryResponse struct { + Percent float64 `json:"percent"` +} + +type FSResponse []struct { + DeviceName string `json:"device_name"` + MntPoint string `json:"mnt_point"` + Percent float64 `json:"percent"` +} + type Notification struct { ID int Enabled bool @@ -108,20 +133,34 @@ func main() { } }() - ticker := time.NewTicker(time.Second) - defer ticker.Stop() - - client := &http.Client{ + appClient := &http.Client{ Timeout: 4 * time.Second, } + // Server monitoring every 5 seconds + go func() { + serverClient := &http.Client{ + Timeout: 5 * time.Second, + } + serverTicker := time.NewTicker(5 * time.Second) + defer serverTicker.Stop() + + for range serverTicker.C { + servers := getServers(db) + checkAndUpdateServerStatus(db, serverClient, servers) + } + }() + + ticker := time.NewTicker(time.Second) + defer ticker.Stop() + for now := range ticker.C { if now.Second()%10 != 0 { continue } apps := getApplications(db) - checkAndUpdateStatus(db, client, apps) + checkAndUpdateStatus(db, appClient, apps) } } @@ -202,6 +241,32 @@ func getApplications(db *sql.DB) []Application { return apps } +func getServers(db *sql.DB) []Server { + rows, err := db.Query( + `SELECT id, name, monitoring, "monitoringURL", online, "cpuUsage", "ramUsage", "diskUsage" + FROM server WHERE monitoring = true`, + ) + if err != nil { + fmt.Printf("Error fetching servers: %v\n", err) + return nil + } + defer rows.Close() + + var servers []Server + for rows.Next() { + var server Server + if err := rows.Scan( + &server.ID, &server.Name, &server.Monitoring, &server.MonitoringURL, + &server.Online, &server.CpuUsage, &server.RamUsage, &server.DiskUsage, + ); err != nil { + fmt.Printf("Error scanning server row: %v\n", err) + continue + } + servers = append(servers, server) + } + return servers +} + func checkAndUpdateStatus(db *sql.DB, client *http.Client, apps []Application) { var notificationTemplate string err := db.QueryRow("SELECT notification_text FROM settings LIMIT 1").Scan(¬ificationTemplate) @@ -301,6 +366,114 @@ func checkAndUpdateStatus(db *sql.DB, client *http.Client, apps []Application) { dbCancel2() } } + +func checkAndUpdateServerStatus(db *sql.DB, client *http.Client, servers []Server) { + for _, server := range servers { + if !server.Monitoring || !server.MonitoringURL.Valid { + continue + } + + logPrefix := fmt.Sprintf("[Server %s]", server.Name) + fmt.Printf("%s Checking...\n", logPrefix) + + baseURL := strings.TrimSuffix(server.MonitoringURL.String, "/") + var cpuUsage, ramUsage, diskUsage float64 + var online = true + + // 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) + continue + } + 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) + continue + } + + 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) + continue + } + cpuUsage = cpuData.Total + + // 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) + continue + } + 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) + continue + } + + 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) + continue + } + ramUsage = memData.Percent + + // Get Disk usage + 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) + continue + } + 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) + continue + } + + 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) + continue + } + + // Use the first filesystem entry's percentage + if len(fsData) > 0 { + diskUsage = fsData[0].Percent + } + + // Update server status with metrics + updateServerStatus(db, server.ID, online, cpuUsage, ramUsage, diskUsage) + fmt.Printf("%s Updated - CPU: %.2f%%, RAM: %.2f%%, Disk: %.2f%%\n", + logPrefix, cpuUsage, ramUsage, diskUsage) + } +} + +func updateServerStatus(db *sql.DB, serverID int, online bool, cpuUsage, ramUsage, diskUsage float64) { + 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, + ) + if err != nil { + fmt.Printf("Failed to update server status (ID: %d): %v\n", serverID, err) + } +} + func sendNotifications(message string) { notifMutex.RLock() notifs := notifMutexCopy(notifications) diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..92f98cd --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ["@tailwindcss/postcss"], +}; + +export default config; \ No newline at end of file