fix: EventBus panic on SSE disconnect + JWT secret persistence + recover() guards

- eventbus.go: Fix send-on-closed-channel panic in SubscribeGlobal by
  using a done channel; add recover() in fan-out goroutine; track active
  global subs for proper cleanup on unsubscribe
- config.go: Persist JWT secret to $DATA_DIR/.jwt_secret instead of
  regenerating a random one on every restart (which invalidated all sessions)
- handlers_ws.go: Replace time.After with time.Ticker to fix timer leak in
  SSE keepalive loop
- handlers_jobs.go: Add recover() in fire-and-forget job goroutine; fix nil
  pointer deref when GetByID fails after job creation
- handlers_machines.go: Add recover() in ProbeAllMachines goroutine
- scheduler.go: Add recover() in scheduled job run goroutine
- engine.go: Add recover() in per-machine probe goroutines
This commit is contained in:
2026-07-08 22:38:56 -04:00
parent 1798fc6804
commit 58e7f51ba7
7 changed files with 89 additions and 20 deletions
+13 -7
View File
@@ -1,6 +1,8 @@
package config
import (
"crypto/rand"
"encoding/hex"
"fmt"
"os"
"path/filepath"
@@ -80,19 +82,23 @@ func Load(configPath, dataDir, addr string) (*Config, error) {
}
}
secretPath := filepath.Join(cfg.DataDir, ".jwt_secret")
if cfg.Auth.JWTSecret == "" {
if data, err := os.ReadFile(secretPath); err == nil && len(data) >= 32 {
cfg.Auth.JWTSecret = strings.TrimSpace(string(data))
}
}
if cfg.Auth.JWTSecret == "" {
b := make([]byte, 32)
f, err := os.Open("/dev/urandom")
if err == nil {
defer f.Close()
n, _ := f.Read(b)
if n == 32 {
cfg.Auth.JWTSecret = fmt.Sprintf("%x", b)
}
if _, err := rand.Read(b); err == nil {
cfg.Auth.JWTSecret = hex.EncodeToString(b)
}
if cfg.Auth.JWTSecret == "" {
cfg.Auth.JWTSecret = "insecure-dev-secret-change-in-production"
}
if dirErr := os.MkdirAll(cfg.DataDir, 0700); dirErr == nil {
_ = os.WriteFile(secretPath, []byte(cfg.Auth.JWTSecret+"\n"), 0600)
}
}
if dataDir := os.Getenv("SYNCSERVER_DATA_DIR"); dataDir != "" {