package config import ( "fmt" "os" "path/filepath" "time" "gopkg.in/yaml.v3" ) const defaultRefreshInterval = 30 * time.Second type Config struct { ServerURL string Username string // optional, prefills the sign-in form RefreshInterval time.Duration Theme string // LegacyAPIKey is set when the file still has an `api_key`. The TUI signs in // with a user account now and ignores it; this is only so it can say so. LegacyAPIKey bool // Team is the team to start on, by name or id. Empty shows every team the // key's user belongs to. Team string // Auth is how to sign in when the server offers a choice: "sso" starts a // single sign-on login straight away, "password" (or empty) shows the // password form. The server decides what is on offer; this only picks the // default among it. Auth string } type rawConfig struct { ServerURL string `yaml:"server_url"` Username string `yaml:"username,omitempty"` APIKey string `yaml:"api_key,omitempty"` // no longer used; see Config.LegacyAPIKey RefreshInterval int `yaml:"refresh_interval,omitempty"` // seconds Theme string `yaml:"theme,omitempty"` Team string `yaml:"team,omitempty"` Auth string `yaml:"auth,omitempty"` } func Load() (*Config, error) { dir, err := os.UserConfigDir() if err != nil { return nil, fmt.Errorf("cannot determine config directory: %w", err) } path := filepath.Join(dir, "terdut-tui", "config.yaml") data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return nil, fmt.Errorf("config file not found at %s\n\nCreate it with:\n server_url: https://terdut.example.com\n username: # optional, prefills the sign-in form\n theme: gruvbox-dark # optional\n team: Ops # optional, team to start on\n auth: sso # optional, sso or password: how to sign in by default", path) } return nil, fmt.Errorf("cannot read config file: %w", err) } var raw rawConfig if err := yaml.Unmarshal(data, &raw); err != nil { return nil, fmt.Errorf("invalid config file: %w", err) } if raw.ServerURL == "" { return nil, fmt.Errorf("config: 'server_url' is required") } switch raw.Auth { case "", "password", "sso": default: return nil, fmt.Errorf("config: 'auth' must be sso or password, not %q", raw.Auth) } interval := defaultRefreshInterval if raw.RefreshInterval > 0 { interval = time.Duration(raw.RefreshInterval) * time.Second } return &Config{ ServerURL: raw.ServerURL, Username: raw.Username, LegacyAPIKey: raw.APIKey != "", RefreshInterval: interval, Theme: raw.Theme, Team: raw.Team, Auth: raw.Auth, }, nil }