package befehle import ( "bufio" "fmt" "net/url" "os" "strings" "github.com/spf13/cobra" "golang.org/x/term" "git.wilde-server.de/florian.wilde/proxdeploy/cli/internal/api" "git.wilde-server.de/florian.wilde/proxdeploy/cli/internal/konfig" ) func (u *umgebung) loginBefehl() *cobra.Command { var token, benutzer, name, server string var tage int befehl := &cobra.Command{ Use: "login ", Short: "An einer ProxDeploy-Instanz anmelden und das API-Token speichern", Long: "Fragt Benutzername und Passwort ab, legt ein API-Token an und speichert es in\n" + "~/.config/proxdeploy/config.toml (Rechte 0600). Mit --token wird ein vorhandenes\n" + "Token (Web-GUI → Einstellungen → API-Tokens) geprüft und gespeichert.", Example: " proxdeploy login https://proxdeploy.example\n proxdeploy login https://proxdeploy.labor --instance labor --token pd_…", Args: func(_ *cobra.Command, argumente []string) error { if len(argumente) != 1 { return bedienfehler("URL fehlt: proxdeploy login ") } return nil }, RunE: func(cmd *cobra.Command, argumente []string) error { adresse := strings.TrimRight(strings.TrimSpace(argumente[0]), "/") if !strings.Contains(adresse, "://") { adresse = "https://" + adresse } geprueft, err := url.Parse(adresse) if err != nil || geprueft.Host == "" || (geprueft.Scheme != "https" && geprueft.Scheme != "http") { return bedienfehler("keine gültige URL: %s", argumente[0]) } ctx := cmd.Context() c := api.Neu(adresse, "", Version) if token == "" { if !term.IsTerminal(int(os.Stdin.Fd())) { return bedienfehler("ohne Terminal kein Passwort-Dialog – --token angeben") } leser := bufio.NewReader(os.Stdin) if benutzer == "" { fmt.Fprint(u.fehler, "Benutzername: ") zeile, err := leser.ReadString('\n') if err != nil { return bedienfehler("Eingabe abgebrochen") } benutzer = strings.TrimSpace(zeile) } fmt.Fprint(u.fehler, "Passwort: ") passwort, err := term.ReadPassword(int(os.Stdin.Fd())) fmt.Fprintln(u.fehler) if err != nil { return bedienfehler("Eingabe abgebrochen") } if name == "" { rechner, _ := os.Hostname() name = "cli@" + rechner } anmeldung := api.TokenAnmeldung{Benutzername: benutzer, Passwort: string(passwort), Name: name} if tage > 0 { anmeldung.Tage = &tage } neu, err := c.TokenAnmelden(ctx, anmeldung) if err != nil { return err } token = neu.Token } c.Token = token ich, err := c.Ich(ctx) if err != nil { return err } datei, pfad, err := konfig.Lesen() if err != nil { return err } instanz := konfig.Instanz{URL: adresse, Token: token, Server: server} if alt, ok := datei.Instanzen[u.opt.instanz]; ok && server == "" { instanz.Server = alt.Server } else if u.opt.instanz == "" && server == "" { instanz.Server = datei.Server } datei.Setzen(u.opt.instanz, instanz) if err := konfig.Schreiben(pfad, datei); err != nil { return fmt.Errorf("Konfiguration nicht geschrieben: %w", err) } if u.opt.json { return u.jsonAusgeben(map[string]any{"benutzername": ich.Benutzername, "rolle": ich.Rolle, "version": ich.Version, "konfiguration": pfad}) } s := u.stil() symbol := "✓" if s.ASCII { symbol = "[OK]" } rolle := map[api.IchRolle]string{api.ADMIN: "Admin", api.OPERATOR: "Operator"}[ich.Rolle] fmt.Fprintf(u.aus, "%s Angemeldet als %s %s\n", s.Teal(symbol), s.Fett(ich.Benutzername), s.Muted(fmt.Sprintf("(%s) · ProxDeploy v%s · Token in %s", rolle, ich.Version, pfad))) return nil }, } f := befehl.Flags() f.StringVar(&token, "token", "", "vorhandenes API-Token (pd_…) statt Passwort") f.StringVar(&benutzer, "user", "", "Benutzername (sonst Abfrage)") f.StringVar(&name, "name", "", "Name des Tokens (Standard: cli@)") f.IntVar(&tage, "days", 0, "Token läuft nach so vielen Tagen ab (Standard: unbefristet)") f.StringVar(&server, "default-server", "", "Standard für --server speichern") return befehl }