// Package konfig liest und schreibt ~/.config/proxdeploy/config.toml. // // url = "https://proxdeploy.example" // token = "pd_…" // server = "rz1" # optional: Standard für --server // // [instances.labor] # weitere Instanzen, Auswahl mit --instance labor // url = "https://proxdeploy.labor" // token = "pd_…" // // PROXDEPLOY_URL, PROXDEPLOY_TOKEN und PROXDEPLOY_INSTANCE gehen der Datei vor. package konfig import ( "errors" "fmt" "io/fs" "os" "path/filepath" "runtime" "github.com/BurntSushi/toml" ) type Instanz struct { URL string `toml:"url"` Token string `toml:"token"` Server string `toml:"server,omitempty"` } type Datei struct { Instanz Instanzen map[string]Instanz `toml:"instances,omitempty"` } // Pfad: $XDG_CONFIG_HOME/proxdeploy/config.toml, sonst ~/.config/…; unter Windows %AppData%. func Pfad() (string, error) { if eigener := os.Getenv("PROXDEPLOY_CONFIG"); eigener != "" { return eigener, nil } basis := os.Getenv("XDG_CONFIG_HOME") if basis == "" { if runtime.GOOS == "windows" { var err error if basis, err = os.UserConfigDir(); err != nil { return "", err } } else { heim, err := os.UserHomeDir() if err != nil { return "", err } basis = filepath.Join(heim, ".config") } } return filepath.Join(basis, "proxdeploy", "config.toml"), nil } // Lesen liefert die Datei oder eine leere, wenn es sie noch nicht gibt. func Lesen() (Datei, string, error) { pfad, err := Pfad() if err != nil { return Datei{}, "", err } var d Datei if _, err := toml.DecodeFile(pfad, &d); err != nil { if errors.Is(err, fs.ErrNotExist) { return Datei{}, pfad, nil } return Datei{}, pfad, fmt.Errorf("%s: %w", pfad, err) } return d, pfad, nil } // Waehlen: die gewünschte Instanz (leer = Hauptabschnitt), überlagert von der Umgebung. func (d Datei) Waehlen(name string) (Instanz, error) { if name == "" { name = os.Getenv("PROXDEPLOY_INSTANCE") } i := d.Instanz if name != "" { gefunden, ok := d.Instanzen[name] if !ok { return Instanz{}, fmt.Errorf("Instanz %q steht nicht in der Konfiguration", name) } i = gefunden } if url := os.Getenv("PROXDEPLOY_URL"); url != "" { i.URL = url } if token := os.Getenv("PROXDEPLOY_TOKEN"); token != "" { i.Token = token } return i, nil } // Setzen trägt eine Instanz ein (leer = Hauptabschnitt). func (d *Datei) Setzen(name string, i Instanz) { if name == "" { d.Instanz = i return } if d.Instanzen == nil { d.Instanzen = map[string]Instanz{} } d.Instanzen[name] = i } // Schreiben legt die Datei mit Rechten 0600 an – sie enthält das Token. func Schreiben(pfad string, d Datei) error { if err := os.MkdirAll(filepath.Dir(pfad), 0o700); err != nil { return err } tmp, err := os.CreateTemp(filepath.Dir(pfad), ".config-*.toml") if err != nil { return err } defer os.Remove(tmp.Name()) if err := tmp.Chmod(0o600); err != nil { tmp.Close() return err } if err := toml.NewEncoder(tmp).Encode(d); err != nil { tmp.Close() return err } if err := tmp.Close(); err != nil { return err } return os.Rename(tmp.Name(), pfad) }