package app import ( "context" "crypto/sha256" "encoding/json" "os" "time" ) // --------------------------------------------------------------------- // status file watcher // --------------------------------------------------------------------- func watchStatusFile(ctx context.Context, every time.Duration) { var last [32]byte have := false for { select { case <-ctx.Done(): return case <-time.After(every): } data, err := os.ReadFile(statusFilePath) if err != nil { continue } h := sha256.Sum256(data) if have && h == last { continue } last = h have = true var st Status if err := json.Unmarshal(data, &st); err != nil { events.push("status_error", map[string]any{"error": err.Error()}) continue } events.push("status_changed", st) } } // --------------------------------------------------------------------- // login file watcher // --------------------------------------------------------------------- func watchLoginFile(ctx context.Context, every time.Duration) { var last [32]byte have := false for { select { case <-ctx.Done(): return case <-time.After(every): } data, err := os.ReadFile(loginStatePath) if err != nil { continue } h := sha256.Sum256(data) if have && h == last { continue } last = h have = true var st VPNLoginState if err := json.Unmarshal(data, &st); err != nil { events.push("login_state_error", map[string]any{"error": err.Error()}) continue } events.push("login_state_changed", st) } } // --------------------------------------------------------------------- // generic file watcher // --------------------------------------------------------------------- func watchFileChange(ctx context.Context, path string, kind string, mode string, every time.Duration) { var lastMod time.Time var lastSize int64 = -1 for { select { case <-ctx.Done(): return case <-time.After(every): } info, err := os.Stat(path) if err != nil { continue } if info.ModTime() == lastMod && info.Size() == lastSize { continue } lastMod = info.ModTime() lastSize = info.Size() events.push(kind, map[string]any{ "path": path, "mode": mode, "size": info.Size(), "mtime": info.ModTime().UTC().Format(time.RFC3339Nano), }) } }