package config import ( "encoding/json" "fmt" "os" "sync" "time" ) // PlatformConfig holds persistent configuration for one platform adapter. type PlatformConfig struct { Name string `json:"name"` Enabled bool `json:"enabled"` Label string `json:"label"` Fields map[string]string `json:"fields"` UpdatedAt time.Time `json:"updated_at"` } // Store manages persistence of platform configs to a JSON file. type Store struct { mu sync.RWMutex path string configs map[string]*PlatformConfig } // NewStore creates a Store, creating the config file if it doesn't exist. func NewStore(path string) (*Store, error) { s := &Store{ path: path, configs: make(map[string]*PlatformConfig), } if err := s.load(); err != nil { return nil, err } return s, nil } func (s *Store) load() error { data, err := os.ReadFile(s.path) if err != nil { if os.IsNotExist(err) { // Initialize empty file. return s.save() } return fmt.Errorf("read config file: %w", err) } if len(data) == 0 { return nil } if err := json.Unmarshal(data, &s.configs); err != nil { return fmt.Errorf("parse config file: %w", err) } return nil } func (s *Store) save() error { data, err := json.MarshalIndent(s.configs, "", " ") if err != nil { return fmt.Errorf("marshal configs: %w", err) } tmpPath := s.path + ".tmp" if err := os.WriteFile(tmpPath, data, 0640); err != nil { return fmt.Errorf("write config file: %w", err) } return os.Rename(tmpPath, s.path) } // List returns all platform configs. func (s *Store) List() []PlatformConfig { s.mu.RLock() defer s.mu.RUnlock() result := make([]PlatformConfig, 0, len(s.configs)) for _, c := range s.configs { result = append(result, *c) } return result } // Get returns a single platform config. func (s *Store) Get(name string) (*PlatformConfig, error) { s.mu.RLock() defer s.mu.RUnlock() c, ok := s.configs[name] if !ok { return nil, fmt.Errorf("config not found: %s", name) } return c, nil } // Set upserts a platform config and persists. func (s *Store) Set(cfg PlatformConfig) error { s.mu.Lock() defer s.mu.Unlock() if cfg.Fields == nil { cfg.Fields = make(map[string]string) } cfg.UpdatedAt = time.Now() s.configs[cfg.Name] = &cfg return s.save() } // Delete removes a platform config and persists. func (s *Store) Delete(name string) error { s.mu.Lock() defer s.mu.Unlock() if _, ok := s.configs[name]; !ok { return fmt.Errorf("config not found: %s", name) } delete(s.configs, name) return s.save() } // HasConfig checks if a config exists for the given platform. func (s *Store) HasConfig(name string) bool { s.mu.RLock() defer s.mu.RUnlock() _, ok := s.configs[name] return ok }