package config import ( "encoding/json" "fmt" "os" ) type Config struct { BackendURL string `json:"backendUrl"` Identifier string `json:"identifier"` APIKey string `json:"apiKey"` ReportIntervalSec int `json:"reportIntervalSec"` LogLevel string `json:"logLevel"` } func Load(path string) (Config, error) { cfg := Config{ BackendURL: getenv("HF_MONITOR_BACKEND_URL", "https://monitor.hangman-lab.top"), Identifier: getenv("HF_MONITOR_IDENTIFIER", hostnameOr("unknown-host")), APIKey: os.Getenv("HF_MONITOR_API_KEY"), ReportIntervalSec: getenvInt("HF_MONITOR_REPORT_INTERVAL", 30), LogLevel: getenv("HF_MONITOR_LOG_LEVEL", "info"), } if path != "" { if fileCfg, err := loadFile(path); err == nil { merge(&cfg, fileCfg) } else if !os.IsNotExist(err) { return cfg, fmt.Errorf("load config file: %w", err) } } // env always wins over file cfg.BackendURL = getenv("HF_MONITOR_BACKEND_URL", cfg.BackendURL) cfg.Identifier = getenv("HF_MONITOR_IDENTIFIER", cfg.Identifier) if v := os.Getenv("HF_MONITOR_API_KEY"); v != "" { cfg.APIKey = v } cfg.ReportIntervalSec = getenvInt("HF_MONITOR_REPORT_INTERVAL", cfg.ReportIntervalSec) cfg.LogLevel = getenv("HF_MONITOR_LOG_LEVEL", cfg.LogLevel) if cfg.BackendURL == "" { return cfg, fmt.Errorf("backendUrl is required") } if cfg.Identifier == "" { return cfg, fmt.Errorf("identifier is required") } if cfg.ReportIntervalSec <= 0 { cfg.ReportIntervalSec = 30 } return cfg, nil } func loadFile(path string) (Config, error) { var cfg Config buf, err := os.ReadFile(path) if err != nil { return cfg, err } if err := json.Unmarshal(buf, &cfg); err != nil { return cfg, err } return cfg, nil } func merge(dst *Config, src Config) { if src.BackendURL != "" { dst.BackendURL = src.BackendURL } if src.Identifier != "" { dst.Identifier = src.Identifier } if src.APIKey != "" { dst.APIKey = src.APIKey } if src.ReportIntervalSec > 0 { dst.ReportIntervalSec = src.ReportIntervalSec } if src.LogLevel != "" { dst.LogLevel = src.LogLevel } } func getenv(key, fallback string) string { if v := os.Getenv(key); v != "" { return v } return fallback } func getenvInt(key string, fallback int) int { if v := os.Getenv(key); v != "" { var out int if _, err := fmt.Sscanf(v, "%d", &out); err == nil && out > 0 { return out } } return fallback } func hostnameOr(fallback string) string { name, err := os.Hostname() if err != nil || name == "" { return fallback } return name }