Pulse/internal/monitoring/temperature_service.go
rcourtman d52ac6d8b5 Fix CSRF token validation and improve token management
- Add Access-Control-Expose-Headers to allow frontend to read X-CSRF-Token response header
- Implement proactive CSRF token issuance on GET requests when session exists but CSRF cookie is missing
- Ensures frontend always has valid CSRF token before making POST requests
- Fixes 403 Forbidden errors when toggling system settings

This resolves CSRF validation failures that occurred when CSRF tokens expired or were missing while valid sessions existed.
2025-11-05 09:23:44 +00:00

99 lines
2.2 KiB
Go

package monitoring
import (
"context"
stderrors "errors"
"sync"
"github.com/rcourtman/pulse-go-rewrite/internal/models"
)
var (
// ErrTemperatureMonitoringDisabled indicates that temperature polling is disabled globally.
ErrTemperatureMonitoringDisabled = stderrors.New("temperature monitoring disabled")
// ErrTemperatureCollectorUnavailable indicates the collector could not be created or is misconfigured.
ErrTemperatureCollectorUnavailable = stderrors.New("temperature collector unavailable")
)
// TemperatureService defines the contract used by the monitor to collect temperature data.
type TemperatureService interface {
Enabled() bool
Collect(ctx context.Context, host, nodeName string) (*models.Temperature, error)
Enable()
Disable()
}
type temperatureService struct {
mu sync.RWMutex
enabled bool
user string
keyPath string
collector *TemperatureCollector
}
func newTemperatureService(enabled bool, user, keyPath string) TemperatureService {
return &temperatureService{
enabled: enabled,
user: user,
keyPath: keyPath,
}
}
func (s *temperatureService) Enabled() bool {
s.mu.RLock()
defer s.mu.RUnlock()
return s.enabled
}
func (s *temperatureService) Enable() {
s.mu.Lock()
defer s.mu.Unlock()
if s.enabled {
return
}
s.enabled = true
if s.collector == nil && s.user != "" && s.keyPath != "" {
s.collector = NewTemperatureCollector(s.user, s.keyPath)
}
}
func (s *temperatureService) Disable() {
s.mu.Lock()
defer s.mu.Unlock()
s.enabled = false
s.collector = nil
}
func (s *temperatureService) Collect(ctx context.Context, host, nodeName string) (*models.Temperature, error) {
s.mu.RLock()
enabled := s.enabled
collector := s.collector
s.mu.RUnlock()
if !enabled {
return nil, ErrTemperatureMonitoringDisabled
}
if collector == nil {
s.mu.Lock()
if s.enabled && s.collector == nil && s.user != "" && s.keyPath != "" {
s.collector = NewTemperatureCollector(s.user, s.keyPath)
}
collector = s.collector
enabled = s.enabled
s.mu.Unlock()
if !enabled {
return nil, ErrTemperatureMonitoringDisabled
}
}
if collector == nil {
return nil, ErrTemperatureCollectorUnavailable
}
return collector.CollectTemperature(ctx, host, nodeName)
}