diff --git a/cmd/init/apikey.go b/cmd/init/apikey.go index b2a383928a26434e65cca7fbaa37491e8d87efb8..2ce799c8930ac9e1867ecafa792b42d97854c8ab 100644 --- a/cmd/init/apikey.go +++ b/cmd/init/apikey.go @@ -22,15 +22,20 @@ import ( const tokenValidationTimeout = 10 * time.Second +// handleKeyringError prints keyring error messages and returns a wrapped error. +func handleKeyringError(out io.Writer, err error) error { + fmt.Fprintln(out, ui.Error.Render("Failed to access system keyring: "+err.Error())) + fmt.Fprintln(out, "Please resolve the keyring issue and try again.") + + return fmt.Errorf("keyring access failed: %w", err) +} + func configureAccessToken(cmd *cobra.Command) error { out := cmd.OutOrStdout() hasToken, keyringErr := client.HasKeyringToken() if keyringErr != nil { - fmt.Fprintln(out, ui.Error.Render("Failed to access system keyring: "+keyringErr.Error())) - fmt.Fprintln(out, "Please resolve the keyring issue and try again.") - - return fmt.Errorf("keyring access failed: %w", keyringErr) + return handleKeyringError(out, keyringErr) } if hasToken { @@ -47,6 +52,55 @@ func configureAccessToken(cmd *cobra.Command) error { return promptForToken(out) } +// ensureAccessToken checks for a token in the keyring and prompts for one if +// missing or invalid. Used at the start of reconfigure mode so users copying +// config to a new device are immediately prompted for their token. +func ensureAccessToken(cmd *cobra.Command) error { + out := cmd.OutOrStdout() + + hasToken, keyringErr := client.HasKeyringToken() + if keyringErr != nil { + return handleKeyringError(out, keyringErr) + } + + if !hasToken { + fmt.Fprintln(out, ui.Warning.Render("No access token found in system keyring.")) + + return promptForToken(out) + } + + existingToken, err := client.GetToken() + if err != nil { + return fmt.Errorf("reading access token from keyring: %w", err) + } + + if err := validateWithSpinner(existingToken); err != nil { + fmt.Fprintln(out, ui.Warning.Render("Existing access token failed validation: "+err.Error())) + + var replace bool + + confirmErr := huh.NewConfirm(). + Title("Would you like to provide a new access token?"). + Affirmative("Yes"). + Negative("No"). + Value(&replace). + Run() + if confirmErr != nil { + if errors.Is(confirmErr, huh.ErrUserAborted) { + return errQuit + } + + return confirmErr + } + + if replace { + return promptForToken(out) + } + } + + return nil +} + func handleExistingToken(out io.Writer) (bool, error) { existingToken, err := client.GetToken() if err != nil { diff --git a/cmd/init/init.go b/cmd/init/init.go index bff532057681813b746a7430fcadca693f377651..558029b67e7ec783d4eb7f1f06b04cd0714fdaa0 100644 --- a/cmd/init/init.go +++ b/cmd/init/init.go @@ -131,6 +131,10 @@ func runReconfigure(cmd *cobra.Command, cfg *config.Config) error { fmt.Fprintln(cmd.OutOrStdout(), ui.Bold.Render("lune configuration")) fmt.Fprintln(cmd.OutOrStdout()) + if err := ensureAccessToken(cmd); err != nil { + return err + } + handlers := map[string]func() error{ "areas": func() error { return manageAreas(cfg) }, "notebooks": func() error { return manageNotebooks(cfg) },