diff --git a/backend.log b/backend.log deleted file mode 100644 index c5ff168..0000000 --- a/backend.log +++ /dev/null @@ -1,9 +0,0 @@ -2025/04/02 06:06:40 {90 90 90} -2025/04/02 06:06:40 Starting backend server on port 8080... -2025/04/02 06:06:41 Alerts enabled: true -2025/04/02 06:06:43 Alerts enabled: false -2025/04/02 06:07:00 Updated limits - CPU: 5, Memory: 100, Disk: 100 -2025/04/02 06:07:01 Alerts enabled: true -2025/04/02 06:07:16 Received shutdown request -2025/04/02 06:07:16 Shutting down the server... -2025/04/02 06:07:16 Server stopped. diff --git a/backend/image b/backend/image index e6eb045..dad12fb 100755 Binary files a/backend/image and b/backend/image differ diff --git a/backend/main_test.go b/backend/main_test.go index 0be3970..7a11a25 100644 --- a/backend/main_test.go +++ b/backend/main_test.go @@ -6,95 +6,83 @@ import ( "net/http" "net/http/httptest" "testing" + + "resource-monitor/backend/handlers" + "resource-monitor/backend/monitor" + "resource-monitor/backend/types" ) -// Test toggleAlertHandler func TestToggleAlertHandler(t *testing.T) { payload := []byte(`{"enable_alerts": true}`) req := httptest.NewRequest(http.MethodPost, "/toggle-alerts", bytes.NewBuffer(payload)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() - toggleAlertHandler(w, req) + handlers.ToggleAlertHandler(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status code 200, got %v", w.Code) } - if !alertEnabled { - t.Errorf("Expected alertEnabled to be true, got %v", alertEnabled) + monitor.Mu.Lock() + defer monitor.Mu.Unlock() + if !monitor.AlertEnabled { + t.Errorf("Expected AlertEnabled to be true, got %v", monitor.AlertEnabled) } } -// Test toggleLimitHandler func TestToggleLimitHandler(t *testing.T) { payload := []byte(`{"cpu_threshold": 80, "memory_threshold": 70, "disk_threshold": 75}`) req := httptest.NewRequest(http.MethodPost, "/limit-changer", bytes.NewBuffer(payload)) req.Header.Set("Content-Type", "application/json") w := httptest.NewRecorder() - toggleLimitHandler(w, req) + handlers.ToggleLimitHandler(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status code 200, got %v", w.Code) } - expectedLimits := SetLimit{ - CPUThreshold: 80, - MemoryThreshold: 70, - DiskThreshold: 75, - } - - if defaultLimit != expectedLimits { - t.Errorf("Expected limits %+v, got %+v", expectedLimits, defaultLimit) + monitor.Mu.Lock() + defer monitor.Mu.Unlock() + expected := types.SetLimit{CPUThreshold: 80, MemoryThreshold: 70, DiskThreshold: 75} + if monitor.DefaultLimit != expected { + t.Errorf("Expected %+v, got %+v", expected, monitor.DefaultLimit) } } -// Test resourceUsageHandler func TestResourceUsageHandler(t *testing.T) { req := httptest.NewRequest(http.MethodGet, "/resource-usage", nil) w := httptest.NewRecorder() - resourceUsageHandler(w, req) + handlers.ResourceUsageHandler(w, req) if w.Code != http.StatusOK { t.Errorf("Expected status code 200, got %v", w.Code) } - var usage ResourceUsage - err := json.NewDecoder(w.Body).Decode(&usage) - if err != nil { + var usage types.ResourceUsage + if err := json.NewDecoder(w.Body).Decode(&usage); err != nil { t.Errorf("Error decoding response: %v", err) } if usage.CPUUsage < 0 || usage.MemoryUsage < 0 || usage.DiskUsage < 0 { - t.Errorf("Expected positive resource usage values, got %+v", usage) + t.Errorf("Expected positive resource values, got %+v", usage) } } -// Test sendAlert functionality (mock example) -func TestSendAlert(t *testing.T) { - alertEnabled = true - defaultLimit = SetLimit{ - CPUThreshold: 50, - MemoryThreshold: 50, - DiskThreshold: 50, - } - - tests := []struct { - resource string - usage float64 - alert bool - }{ - {"CPU", 60, true}, - {"Memory", 40, false}, - {"Disk", 55, true}, - } - - for _, test := range tests { - mu.Lock() - alertEnabled = false // Mocking disabled alert notifications - mu.Unlock() - sendAlert(test.resource, test.usage) - } -} +// func TestSendAlertLogic(t *testing.T) { +// monitor.Mu.Lock() +// monitor.AlertEnabled = true +// monitor.DefaultLimit = types.SetLimit{ +// CPUThreshold: 50, +// MemoryThreshold: 50, +// DiskThreshold: 50, +// } +// monitor.Mu.Unlock() + +// // These will trigger alerts (but we don't test the actual notification pop-up) +// monitor.SendAlert("CPU", 55) +// monitor.SendAlert("Memory", 30) // Should not trigger alert +// monitor.SendAlert("Disk", 70) +// } diff --git a/backend/output b/backend/output deleted file mode 100755 index 2380e58..0000000 Binary files a/backend/output and /dev/null differ diff --git a/backend/server.go b/backend/server.go deleted file mode 100644 index 981e126..0000000 --- a/backend/server.go +++ /dev/null @@ -1,220 +0,0 @@ -package main - -import ( - "encoding/json" - "fmt" - "log" - "net/http" - "sync" - "time" - - "github.com/gen2brain/beeep" - "github.com/shirou/gopsutil/cpu" - "github.com/shirou/gopsutil/disk" - "github.com/shirou/gopsutil/mem" -) - -type ResourceUsage struct { - CPUUsage float64 `json:"cpu_usage"` - MemoryUsage float64 `json:"memory_usage"` - DiskUsage float64 `json:"disk_usage"` -} - -type SetLimit struct { - CPUThreshold float64 - MemoryThreshold float64 - DiskThreshold float64 -} - -var ( - alertEnabled bool - mu sync.Mutex - defaultLimit = setDefaultLimit() - shutdownChan = make(chan struct{}) -) - -func setDefaultLimit() SetLimit { - var cpu float64 = 90 - var memory float64 = 90 - var disk float64 = 90 - - limit := SetLimit{ - CPUThreshold: cpu, - MemoryThreshold: memory, - DiskThreshold: disk, - } - return limit -} - -// Get CPU usage -func getCPUUsage() float64 { - percent, err := cpu.Percent(0, false) - if err != nil { - log.Printf("Error getting CPU usage: %v", err) - return -1 - } - return percent[0] -} - -// Get Memory usage -func getMemoryUsage() float64 { - v, err := mem.VirtualMemory() - if err != nil { - log.Printf("Error getting memory usage: %v", err) - return -1 - } - return v.UsedPercent -} - -// Get Disk usage -func getDiskUsage() float64 { - d, err := disk.Usage("/") - if err != nil { - log.Printf("Error getting disk usage: %v", err) - return -1 - } - return d.UsedPercent -} - -// Send macOS notification if usage exceeds threshold -func sendAlert(resource string, usage float64) { - mu.Lock() - defer mu.Unlock() - - switch resource { - case "CPU": - if alertEnabled && usage > defaultLimit.CPUThreshold { - err := beeep.Alert(fmt.Sprintf("%s Alert", resource), fmt.Sprintf("%s usage is at %.2f%%!", resource, usage), "") - if err != nil { - log.Println("Error sending macOS notification:", err) - } - } - case "Disk": - if alertEnabled && usage > defaultLimit.DiskThreshold { - err := beeep.Alert(fmt.Sprintf("%s Alert", resource), fmt.Sprintf("%s usage is at %.2f%%!", resource, usage), "") - if err != nil { - log.Println("Error sending macOS notification:", err) - } - } - case "Memory": - if alertEnabled && usage > defaultLimit.MemoryThreshold { - err := beeep.Alert(fmt.Sprintf("%s Alert", resource), fmt.Sprintf("%s usage is at %.2f%%!", resource, usage), "") - if err != nil { - log.Println("Error sending macOS notification:", err) - } - } - } -} - -func shutdownHandler(w http.ResponseWriter, r *http.Request) { - if r.Method == http.MethodPost { - log.Println("Received shutdown request") - close(shutdownChan) // Trigger shutdown - w.WriteHeader(http.StatusOK) - } else { - http.Error(w, "Invalid request method", http.StatusMethodNotAllowed) - } -} - -func toggleAlertHandler(w http.ResponseWriter, r *http.Request) { - mu.Lock() - defer mu.Unlock() - - if r.Method == http.MethodPost { - var data struct { - EnableAlerts bool `json:"enable_alerts"` - } - if err := json.NewDecoder(r.Body).Decode(&data); err != nil { - http.Error(w, "Invalid request", http.StatusBadRequest) - return - } - alertEnabled = data.EnableAlerts - log.Printf("Alerts enabled: %v", alertEnabled) - } -} - -func toggleLimitHandler(w http.ResponseWriter, r *http.Request) { - mu.Lock() - defer mu.Unlock() - - if r.Method == http.MethodPost { - var data struct { - CPUThreshold float64 `json:"cpu_threshold"` - MemoryThreshold float64 `json:"memory_threshold"` - DiskThreshold float64 `json:"disk_threshold"` - } - - if err := json.NewDecoder(r.Body).Decode(&data); err != nil { - http.Error(w, "Invalid request", http.StatusBadRequest) - return - } - - defaultLimit.CPUThreshold = data.CPUThreshold - defaultLimit.MemoryThreshold = data.MemoryThreshold - defaultLimit.DiskThreshold = data.DiskThreshold - - log.Printf("Updated limits - CPU: %v, Memory: %v, Disk: %v", defaultLimit.CPUThreshold, defaultLimit.MemoryThreshold, defaultLimit.DiskThreshold) - w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(defaultLimit); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } - } -} - -// Resource usage handler -func resourceUsageHandler(w http.ResponseWriter, r *http.Request) { - usage := ResourceUsage{ - CPUUsage: getCPUUsage(), - MemoryUsage: getMemoryUsage(), - DiskUsage: getDiskUsage(), - } - - w.Header().Set("Content-Type", "application/json") - if err := json.NewEncoder(w).Encode(usage); err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - } -} - -func monitorResources() { - for { - cpuUsage := getCPUUsage() - memUsage := getMemoryUsage() - diskUsage := getDiskUsage() - sendAlert("CPU", cpuUsage) - sendAlert("Memory", memUsage) - sendAlert("Disk", diskUsage) - - time.Sleep(5 * time.Second) - } -} -func main() { - alertEnabled = true - limit := setDefaultLimit() - log.Println(limit) - server := &http.Server{ - Addr: ":8080", - } - - http.HandleFunc("/toggle-alerts", toggleAlertHandler) - http.HandleFunc("/resource-usage", resourceUsageHandler) - http.HandleFunc("/limit-changer", toggleLimitHandler) - http.HandleFunc("/shutdown", shutdownHandler) - - go monitorResources() - - go func() { - log.Println("Starting backend server on port 8080...") - if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed { - log.Fatalf("Error starting server: %v", err) - } - }() - - <-shutdownChan - - log.Println("Shutting down the server...") - if err := server.Close(); err != nil { - log.Fatalf("Server shutdown failed: %v", err) - } - - log.Println("Server stopped.") -} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..d9c0435 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,10 @@ +version: "3.8" + +services: + app: + build: + context: . + ports: + - "8080:8080" + container_name: go-app + restart: unless-stopped \ No newline at end of file diff --git a/dockerfile b/dockerfile new file mode 100644 index 0000000..ef4d82f --- /dev/null +++ b/dockerfile @@ -0,0 +1,13 @@ +FROM alpine:latest + +WORKDIR /app + +# Copy backend and frontend binaries into the container +COPY backend/image /app/backend +COPY frontend/image /app/frontend + +# Expose backend port +EXPOSE 8080 + +# Start both processes +CMD ["/bin/sh", "-c", "/app/backend & /app/frontend"] \ No newline at end of file diff --git a/makefile b/makefile index ea4daed..ca0c090 100644 --- a/makefile +++ b/makefile @@ -6,7 +6,7 @@ GOFMT = gofmt BACKEND_DIR = backend FRONTEND_DIR = frontend BACKEND_LOG = backend.log -BACKEND_PORT = 8080 # Update this if needed +BACKEND_PORT = 8080 deps: @echo "Installing Go dependencies..." @@ -14,7 +14,7 @@ deps: build-backend: @echo "Building the backend application..." - $(GO) build -o $(BACKEND_DIR)/$(BINARY_NAME) $(BACKEND_DIR)/server.go + $(GO) build -o $(BACKEND_DIR)/$(BINARY_NAME) $(BACKEND_DIR)/main.go build-frontend: @echo "Building the frontend application..." @@ -40,6 +40,14 @@ run-all: deps build run-backend wait-backend run-frontend run-built: run-backend wait-backend run-frontend +docker-run: + @echo "Building binaries..." + $(MAKE) build + @echo "Building Docker image..." + docker-compose build + @echo "Starting container..." + docker-compose up -d + clean: @echo "Cleaning up..." rm -f $(BACKEND_DIR)/$(BINARY_NAME) $(FRONTEND_DIR)/$(BINARY_NAME) $(BACKEND_LOG) diff --git a/readme.md b/readme.md index cabfa58..83fdd62 100644 --- a/readme.md +++ b/readme.md @@ -86,18 +86,6 @@ The project includes robust error handling, ensuring that system failures or inv 4. **Frontend Setup**: - Open a separate terminal, navigate to the frontend directory, and run the frontend application. -## Using the Makefile - -You can use the Makefile to simplify the build and run process. Available commands include: - -- **Install dependencies**: Install the required dependencies. -- **Build the backend**: Build the backend. -- **Build the frontend**: Build the frontend. -- **Run the backend**: Start the backend server. -- **Run the frontend**: Start the frontend GUI. -- **Clean up**: Remove generated binaries. -- **Help**: View available commands. - ## Technologies Used - **Go (Golang)**: Used for both the backend API and frontend GUI.