package mining import ( "encoding/json" "fmt" "os" "path/filepath" "sync" "github.com/adrg/xdg" ) // configMu protects concurrent access to config file operations var configMu sync.RWMutex // MinerAutostartConfig{MinerType: "xmrig", Autostart: true, Config: &cfg} type MinerAutostartConfig struct { MinerType string `json:"minerType"` Autostart bool `json:"autostart"` Config *Config `json:"config,omitempty"` // Store the last used config } // DatabaseConfig{Enabled: true, RetentionDays: 30} type DatabaseConfig struct { // cfg.Enabled = false // disable hashrate persistence entirely Enabled bool `json:"enabled"` // cfg.RetentionDays = 90 // purge rows older than 90 days (0 → defaults to 30) RetentionDays int `json:"retentionDays,omitempty"` } // defaultDatabaseConfig() // DatabaseConfig{Enabled: true, RetentionDays: 30} func defaultDatabaseConfig() DatabaseConfig { return DatabaseConfig{ Enabled: true, RetentionDays: 30, } } // MinersConfig{Miners: []MinerAutostartConfig{}, Database: defaultDatabaseConfig()} type MinersConfig struct { Miners []MinerAutostartConfig `json:"miners"` Database DatabaseConfig `json:"database"` } // getMinersConfigPath() // "~/.config/lethean-desktop/miners/config.json" func getMinersConfigPath() (string, error) { return xdg.ConfigFile("lethean-desktop/miners/config.json") } // cfg, err := LoadMinersConfig() // if err != nil { return err } // cfg.Database.Enabled = false func LoadMinersConfig() (*MinersConfig, error) { configMu.RLock() defer configMu.RUnlock() configPath, err := getMinersConfigPath() if err != nil { return nil, fmt.Errorf("could not determine miners config path: %w", err) } data, err := os.ReadFile(configPath) if err != nil { if os.IsNotExist(err) { // Return empty config with defaults if file doesn't exist return &MinersConfig{ Miners: []MinerAutostartConfig{}, Database: defaultDatabaseConfig(), }, nil } return nil, fmt.Errorf("failed to read miners config file: %w", err) } var configuration MinersConfig if err := json.Unmarshal(data, &configuration); err != nil { return nil, fmt.Errorf("failed to unmarshal miners config: %w", err) } // Apply default database config if not set (for backwards compatibility) if configuration.Database.RetentionDays == 0 { configuration.Database = defaultDatabaseConfig() } return &configuration, nil } // cfg.Database.RetentionDays = 60 // if err := SaveMinersConfig(cfg); err != nil { return err } func SaveMinersConfig(configuration *MinersConfig) error { configMu.Lock() defer configMu.Unlock() configPath, err := getMinersConfigPath() if err != nil { return fmt.Errorf("could not determine miners config path: %w", err) } dir := filepath.Dir(configPath) if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("failed to create config directory: %w", err) } data, err := json.MarshalIndent(configuration, "", " ") if err != nil { return fmt.Errorf("failed to marshal miners config: %w", err) } return AtomicWriteFile(configPath, data, 0600) } // UpdateMinersConfig(func(c *MinersConfig) error { c.Miners = append(c.Miners, entry); return nil }) func UpdateMinersConfig(modifier func(*MinersConfig) error) error { configMu.Lock() defer configMu.Unlock() configPath, err := getMinersConfigPath() if err != nil { return fmt.Errorf("could not determine miners config path: %w", err) } // Load current config var configuration MinersConfig data, err := os.ReadFile(configPath) if err != nil { if os.IsNotExist(err) { configuration = MinersConfig{ Miners: []MinerAutostartConfig{}, Database: defaultDatabaseConfig(), } } else { return fmt.Errorf("failed to read miners config file: %w", err) } } else { if err := json.Unmarshal(data, &configuration); err != nil { return fmt.Errorf("failed to unmarshal miners config: %w", err) } if configuration.Database.RetentionDays == 0 { configuration.Database = defaultDatabaseConfig() } } // Apply the modification if err := modifier(&configuration); err != nil { return err } // Save atomically dir := filepath.Dir(configPath) if err := os.MkdirAll(dir, 0755); err != nil { return fmt.Errorf("failed to create config directory: %w", err) } newData, err := json.MarshalIndent(configuration, "", " ") if err != nil { return fmt.Errorf("failed to marshal miners config: %w", err) } return AtomicWriteFile(configPath, newData, 0600) }