package cli import ( "os" "path/filepath" "gopkg.in/yaml.v3" ) // Config holds resolved CLI defaults. Precedence (highest to lowest): // command-line flag > environment variable > ~/.kmsvc/config.yaml > built-in default. type Config struct { Server string Token string Output string Insecure bool ClientID string ClientSecret string TokenURL string } type fileConfig struct { Server string `yaml:"server"` Output string `yaml:"output"` Insecure bool `yaml:"insecure"` ClientID string `yaml:"clientId"` ClientSecret string `yaml:"clientSecret"` TokenURL string `yaml:"tokenUrl"` } // LoadConfig resolves defaults from ~/.kmsvc/config.yaml and environment // variables (KMSVC_SERVER, KMSVC_TOKEN, KMSVC_OUTPUT, KMSVC_INSECURE, // KMSVC_CLIENT_ID, KMSVC_CLIENT_SECRET, KMSVC_TOKEN_URL). ClientID/ClientSecret // additionally fall back to AUTHENTIK_KAFAKA_CLIENT_ID/AUTHENTIK_KAFAKA_CLIENT_SECRET // (the homelab's Authentik app credentials, typically exported into the shell // via `vsource` from Vault) when the KMSVC_-prefixed vars aren't set, so this // CLI doesn't need its own separately-exported copy. Flags are applied on top // of this by the caller (root.go), so this function never reads flags. func LoadConfig() Config { cfg := Config{Output: "table"} if path, err := configFilePath(); err == nil { if fc, err := readFileConfig(path); err == nil { if fc.Server != "" { cfg.Server = fc.Server } if fc.Output != "" { cfg.Output = fc.Output } cfg.Insecure = fc.Insecure if fc.ClientID != "" { cfg.ClientID = fc.ClientID } if fc.ClientSecret != "" { cfg.ClientSecret = fc.ClientSecret } if fc.TokenURL != "" { cfg.TokenURL = fc.TokenURL } } } if v := os.Getenv("KMSVC_SERVER"); v != "" { cfg.Server = v } if v := os.Getenv("KMSVC_TOKEN"); v != "" { cfg.Token = v } if v := os.Getenv("KMSVC_OUTPUT"); v != "" { cfg.Output = v } if v := os.Getenv("KMSVC_INSECURE"); v != "" { cfg.Insecure = v == "1" || v == "true" } if v := os.Getenv("AUTHENTIK_KAFAKA_CLIENT_ID"); v != "" { cfg.ClientID = v } if v := os.Getenv("KMSVC_CLIENT_ID"); v != "" { cfg.ClientID = v } if v := os.Getenv("AUTHENTIK_KAFAKA_CLIENT_SECRET"); v != "" { cfg.ClientSecret = v } if v := os.Getenv("KMSVC_CLIENT_SECRET"); v != "" { cfg.ClientSecret = v } if v := os.Getenv("KMSVC_TOKEN_URL"); v != "" { cfg.TokenURL = v } return cfg } func configFilePath() (string, error) { home, err := os.UserHomeDir() if err != nil { return "", err } return filepath.Join(home, ".kmsvc", "config.yaml"), nil } func readFileConfig(path string) (fileConfig, error) { var fc fileConfig data, err := os.ReadFile(path) if err != nil { return fc, err } if err := yaml.Unmarshal(data, &fc); err != nil { return fc, err } return fc, nil }