package service import ( "context" "fmt" "sync" "time" "bugulma/backend/internal/domain" ) type MaintenanceSetting struct { Enabled bool `json:"enabled"` Message string `json:"message"` AllowedIPs []string `json:"allowed_ips,omitempty"` } type SettingsService struct { repo domain.SystemSettingsRepository // caching cacheTTL time.Duration cacheMutex sync.RWMutex cachedMaintenance *MaintenanceSetting cacheExpiry time.Time } func NewSettingsService(repo domain.SystemSettingsRepository) *SettingsService { return &SettingsService{repo: repo, cacheTTL: 5 * time.Second} } func (s *SettingsService) GetMaintenance(ctx context.Context) (*MaintenanceSetting, error) { m, err := s.repo.Get(ctx, "maintenance") if err != nil { return nil, fmt.Errorf("failed to get maintenance: %w", err) } if m == nil { return &MaintenanceSetting{Enabled: false, Message: ""}, nil } enabled, _ := m["enabled"].(bool) msg, _ := m["message"].(string) // allowed_ips may be stored as []interface{} from DB JSON decode var allowed []string if v, ok := m["allowed_ips"]; ok { switch vv := v.(type) { case []any: for _, it := range vv { if s, ok := it.(string); ok { allowed = append(allowed, s) } } case []string: allowed = vv } } return &MaintenanceSetting{Enabled: enabled, Message: msg, AllowedIPs: allowed}, nil } func (s *SettingsService) SetMaintenance(ctx context.Context, in *MaintenanceSetting) error { value := map[string]any{"enabled": in.Enabled, "message": in.Message} if in.AllowedIPs != nil { value["allowed_ips"] = in.AllowedIPs } if err := s.repo.Set(ctx, "maintenance", value); err != nil { return fmt.Errorf("failed to set maintenance: %w", err) } // Invalidate cache s.cacheMutex.Lock() s.cachedMaintenance = nil s.cacheExpiry = time.Time{} s.cacheMutex.Unlock() return nil } // GetMaintenanceCached returns a cached maintenance setting for low-latency checks. // Cache is refreshed after s.cacheTTL. func (s *SettingsService) GetMaintenanceCached(ctx context.Context) (*MaintenanceSetting, error) { s.cacheMutex.RLock() if s.cachedMaintenance != nil && time.Now().Before(s.cacheExpiry) { m := s.cachedMaintenance s.cacheMutex.RUnlock() return m, nil } s.cacheMutex.RUnlock() // Fetch fresh m, err := s.GetMaintenance(ctx) if err != nil { return nil, err } s.cacheMutex.Lock() s.cachedMaintenance = m s.cacheExpiry = time.Now().Add(s.cacheTTL) s.cacheMutex.Unlock() return m, nil }