diff --git a/internal/auth/auth.go b/internal/auth/auth.go index bf54922..aacaec5 100644 --- a/internal/auth/auth.go +++ b/internal/auth/auth.go @@ -147,6 +147,7 @@ func (m *Manager) SetSessionCookie(w http.ResponseWriter) string { MaxAge: int(SessionDuration.Seconds()), HttpOnly: true, SameSite: http.SameSiteStrictMode, + Secure: true, }) return token } @@ -160,6 +161,7 @@ func (m *Manager) ClearSessionCookie(w http.ResponseWriter) { MaxAge: -1, HttpOnly: true, SameSite: http.SameSiteStrictMode, + Secure: true, }) } diff --git a/internal/daemon/constants.go b/internal/daemon/constants.go new file mode 100644 index 0000000..09eb638 --- /dev/null +++ b/internal/daemon/constants.go @@ -0,0 +1,4 @@ +package daemon + +// StatusError represents a generic error status across the daemon components +const StatusError = "error" diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 0a4ac8a..a7b8ea6 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -133,7 +133,7 @@ func (p *Program) Start() error { Uptime: int(time.Since(p.startTime).Seconds()), } - if response.Printers.Status == "error" { + if response.Printers.Status == StatusError { response.Status = "degraded" } @@ -318,7 +318,6 @@ func serveDashboard(tmpl *template.Template) http.HandlerFunc { return } w.Header().Set("Content-Type", "text/html; charset=utf-8") - //nolint:gosec // False positive: passing token to internal template data := struct{ AuthToken string }{AuthToken: config.AuthToken} if err := tmpl.Execute(w, data); err != nil { log.Printf("[X] Error rendering dashboard: %v", err) diff --git a/internal/daemon/printer_discovery.go b/internal/daemon/printer_discovery.go index 7a6c7b4..f8e1b7d 100644 --- a/internal/daemon/printer_discovery.go +++ b/internal/daemon/printer_discovery.go @@ -56,7 +56,7 @@ func (pd *PrinterDiscovery) GetPrinters(forceRefresh bool) ([]connection.Printer func (pd *PrinterDiscovery) GetSummary() posprinter.Summary { printers, err := pd.GetPrinters(false) if err != nil { - return posprinter.Summary{Status: "error", DetectedCount: 0} + return posprinter.Summary{Status: StatusError, DetectedCount: 0} } thermal := connection.FilterThermalPrinters(printers) @@ -74,7 +74,7 @@ func (pd *PrinterDiscovery) GetSummary() posprinter.Summary { if len(thermal) == 0 && len(physical) > 0 { status = "warning" } else if len(physical) == 0 { - status = "error" + status = StatusError } return posprinter.Summary{ diff --git a/internal/server/clients_test.go b/internal/server/clients_test.go new file mode 100644 index 0000000..82fcb53 --- /dev/null +++ b/internal/server/clients_test.go @@ -0,0 +1,133 @@ +package server + +import ( + "sync" + "testing" + + "github.com/coder/websocket" +) + +func TestClientRegistry_BasicOperations(t *testing.T) { + registry := NewClientRegistry() + + // Create dummy connections + // We use an array to make sure they have different addresses + conns := make([]websocket.Conn, 3) + conn1 := &conns[0] + conn2 := &conns[1] + conn3 := &conns[2] + + // Initial state + if count := registry.Count(); count != 0 { + t.Errorf("Expected 0 clients, got %d", count) + } + + // Add clients + registry.Add(conn1) + registry.Add(conn2) + + if count := registry.Count(); count != 2 { + t.Errorf("Expected 2 clients, got %d", count) + } + if !registry.Contains(conn1) { + t.Error("Expected registry to contain conn1") + } + if registry.Contains(conn3) { + t.Error("Expected registry to not contain conn3") + } + + // Remove client + registry.Remove(conn1) + + if count := registry.Count(); count != 1 { + t.Errorf("Expected 1 client, got %d", count) + } + if registry.Contains(conn1) { + t.Error("Expected registry to not contain conn1 after removal") + } + if !registry.Contains(conn2) { + t.Error("Expected registry to contain conn2") + } +} + +func TestClientRegistry_ForEachAndBroadcast(t *testing.T) { + registry := NewClientRegistry() + + conns := make([]websocket.Conn, 2) + conn1 := &conns[0] + conn2 := &conns[1] + + registry.Add(conn1) + registry.Add(conn2) + + // Test ForEach + visited := make(map[*websocket.Conn]bool) + registry.ForEach(func(c *websocket.Conn) { + visited[c] = true + }) + + if len(visited) != 2 || !visited[conn1] || !visited[conn2] { + t.Errorf("ForEach did not visit all clients correctly: %v", visited) + } + + // Test Broadcast + visitedBroadcast := make(map[*websocket.Conn]bool) + registry.Broadcast(func(c *websocket.Conn) error { + visitedBroadcast[c] = true + return nil + }) + + if len(visitedBroadcast) != 2 || !visitedBroadcast[conn1] || !visitedBroadcast[conn2] { + t.Errorf("Broadcast did not visit all clients correctly: %v", visitedBroadcast) + } +} + +func TestClientRegistry_Concurrency(t *testing.T) { + registry := NewClientRegistry() + var wg sync.WaitGroup + + numGoroutines := 100 + conns := make([]websocket.Conn, numGoroutines) + + // Concurrent Add + for i := 0; i < numGoroutines; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + registry.Add(&conns[i]) + }(i) + } + wg.Wait() + + if count := registry.Count(); count != numGoroutines { + t.Errorf("Expected %d clients, got %d", numGoroutines, count) + } + + // Concurrent Contains, ForEach, Broadcast + for i := 0; i < numGoroutines; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + _ = registry.Contains(&conns[i]) + if i%10 == 0 { + registry.ForEach(func(_ *websocket.Conn) {}) + registry.Broadcast(func(_ *websocket.Conn) error { return nil }) + } + }(i) + } + wg.Wait() + + // Concurrent Remove + for i := 0; i < numGoroutines; i++ { + wg.Add(1) + go func(i int) { + defer wg.Done() + registry.Remove(&conns[i]) + }(i) + } + wg.Wait() + + if count := registry.Count(); count != 0 { + t.Errorf("Expected 0 clients after removal, got %d", count) + } +} diff --git a/internal/server/server.go b/internal/server/server.go index 84ab1c9..6e6b188 100644 --- a/internal/server/server.go +++ b/internal/server/server.go @@ -46,7 +46,6 @@ type Message struct { Tipo string `json:"tipo"` ID string `json:"id,omitempty"` Datos json.RawMessage `json:"datos,omitempty"` - //nolint:gosec // Required JSON key for client payload AuthToken string `json:"auth_token,omitempty"` }