From cad4ae65f7eda3c048142422087531c0143f8d2b Mon Sep 17 00:00:00 2001 From: Dottere Date: Tue, 6 Oct 2026 15:49:19 +0200 Subject: [PATCH] SSE impl done --- .gitignore | 2 - cmd/rpi5-health/main.go | 8 ++++ internal/http_internal/server.go | 22 ++++++++- internal/http_internal/ssehandler.go | 67 ++++++++++++++++++++++++++++ 4 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 internal/http_internal/ssehandler.go diff --git a/.gitignore b/.gitignore index 2d5b2eb..22d2122 100644 --- a/.gitignore +++ b/.gitignore @@ -24,5 +24,3 @@ go.work.sum # env file .env - -!lhm/* diff --git a/cmd/rpi5-health/main.go b/cmd/rpi5-health/main.go index b726265..5157bac 100644 --- a/cmd/rpi5-health/main.go +++ b/cmd/rpi5-health/main.go @@ -5,8 +5,16 @@ import ( "rpi5-health/internal/http_internal" ) +/* TODO: + * 1. Storage & Disk I/O: Disk capacity | Disk I/O rates | IOWAIT metrics + * 2. Network traffic: Bandwidth usage | Total network usage | Active connections + * 3. Process monitoring: Top consumers | Zombie processes */ + func main() { srv := http_internal.NewAppServer() + + srv.RegisterRoute("/api/events", http_internal.SSEHandler) + go srv.Start() cli.Listen() diff --git a/internal/http_internal/server.go b/internal/http_internal/server.go index ab66df3..e520383 100644 --- a/internal/http_internal/server.go +++ b/internal/http_internal/server.go @@ -1,12 +1,28 @@ package http_internal -import "net/http" +import ( + "net/http" + loadaverage "rpi5-health/internal/load_average" + "rpi5-health/internal/memory" + "rpi5-health/internal/processor" + "rpi5-health/internal/temperature" + "rpi5-health/internal/uptime" +) type AppServer struct { srv *http.Server mux *http.ServeMux } +// To be sent to the client +type ServerStatistics struct { + LoadAvgInfo loadaverage.LoadAverageInformation `json:"load_average"` + MemoryInfo memory.MemoryInformation `json:"memory_info"` + ProcessorInfo processor.ProcessorInformation `json:"processor_info"` + TemperatureInfo temperature.TemperatureInformation `json:"temperature_info"` + UptimeInfo uptime.UptimeInformation `json:"uptime_info"` +} + func NewAppServer() *AppServer { mux := http.NewServeMux() @@ -28,3 +44,7 @@ func NewAppServer() *AppServer { func (srv *AppServer) Start() { srv.srv.ListenAndServe() } + +func (srv *AppServer) RegisterRoute(endpoint string, handler http.HandlerFunc) { + srv.mux.HandleFunc(endpoint, handler) +} diff --git a/internal/http_internal/ssehandler.go b/internal/http_internal/ssehandler.go new file mode 100644 index 0000000..0cb6a88 --- /dev/null +++ b/internal/http_internal/ssehandler.go @@ -0,0 +1,67 @@ +package http_internal + +import ( + "encoding/json" + "fmt" + "log" + "net/http" + loadaverage "rpi5-health/internal/load_average" + "rpi5-health/internal/memory" + "rpi5-health/internal/processor" + "rpi5-health/internal/temperature" + "rpi5-health/internal/uptime" + "time" +) + +func SSEHandler(w http.ResponseWriter, r *http.Request) { + w.Header().Set("Content-Type", "text/event-stream") + w.Header().Set("Cache-Control", "no-cache") + w.Header().Set("Connection", "keep-alive") + + // For CORS requests + w.Header().Set("Access-Control-Allow-Origin", "*") + + clientGone := r.Context().Done() // Channel to signal client disconnection + + rc := http.NewResponseController(w) + t := time.NewTicker(time.Second * 5) + defer t.Stop() + for { + select { + case <-clientGone: + log.Println("Client disconnected") + return + case <-t.C: + resp := buildDashboardResponse() + _, err := fmt.Fprintf(w, "data: %s\n\n", resp) + if err != nil { + return + } + err = rc.Flush() + if err != nil { + return + } + } + } +} + +func buildDashboardResponse() []byte { + // TODO: proper error handling + loadavg, _ := loadaverage.GetLoadAverage() + memoryInfo, _ := memory.GetRamInformation() + processorInfo := processor.GetCPUInformation() + temperatureInfo, _ := temperature.GetTemperatureInformation() + uptimeInfo, _ := uptime.GetUptimeInformation() + + stats := ServerStatistics{ + LoadAvgInfo: loadavg, + MemoryInfo: memoryInfo, + ProcessorInfo: processorInfo, + TemperatureInfo: temperatureInfo, + UptimeInfo: uptimeInfo, + } + + payload, _ := json.Marshal(stats) + + return payload +}