maidn-cli/internal/ui/prompts.go

184 lines
4.7 KiB
Go

package ui
import (
"bufio"
"fmt"
"os"
"strings"
"syscall"
"golang.org/x/term"
)
// GetUserInputForGhcr prompts the user for GitHub-related credentials for GHCR.
func GetUserInputForGhcr() (username, registryToken, orgToken string, err error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter your GitHub username: ")
username, err = reader.ReadString('\n')
if err != nil {
return
}
username = strings.TrimSpace(username)
if username == "" {
err = fmt.Errorf("username cannot be empty")
return
}
registryToken, err = promptForToken(reader, "Enter your GitHub PAT for container registry")
if err != nil {
return
}
orgToken, err = promptForToken(reader, "Enter your GitHub PAT for organization management")
if err != nil {
return
}
return
}
// GetUserInputForDockerHub prompts the user for Docker Hub credentials.
func GetUserInputForDockerHub() (username, password string, err error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter your Docker Hub username: ")
username, err = reader.ReadString('\n')
if err != nil {
return
}
username = strings.TrimSpace(username)
fmt.Print("Enter your Docker Hub password or access token: ")
passwordBytes, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println()
if err != nil {
return "", "", err
}
password = string(passwordBytes)
if username == "" || password == "" {
err = fmt.Errorf("username and password cannot be empty")
}
return
}
// GetUserInputForGithubAuth prompts the user for a general GitHub PAT.
func GetUserInputForGithubAuth() (username, token string, err error) {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter your GitHub username: ")
username, err = reader.ReadString('\n')
if err != nil {
return
}
username = strings.TrimSpace(username)
if username == "" {
err = fmt.Errorf("username cannot be empty")
return
}
token, err = promptForToken(reader, "Enter your GitHub PAT for repository access")
if err != nil {
return
}
return
}
// PromptForgejoRegistryToken collects credentials used only to create a registry token.
func PromptForgejoRegistryToken() (password, otp, name string, err error) {
password, err = promptHiddenRequired("Forgejo account password")
if err != nil {
return "", "", "", err
}
if otp, err = promptHiddenOptional("Forgejo OTP (optional)"); err != nil {
return "", "", "", err
}
name = prompt(bufio.NewReader(os.Stdin), "Forgejo registry token name", "maidn-registry")
return password, otp, name, nil
}
// PromptForgejoDeliveryStatusToken collects credentials used only to create
// the fixed-purpose delivery-status token.
func PromptForgejoDeliveryStatusToken() (password, otp string, err error) {
password, err = promptHiddenRequired("Forgejo account password")
if err != nil {
return "", "", err
}
otp, err = promptHiddenOptional("Forgejo OTP (optional)")
return password, otp, err
}
func promptForToken(reader *bufio.Reader, prompt string) (string, error) {
token := promptSecret(reader, prompt, "")
if token == "" {
return "", fmt.Errorf("token cannot be empty")
}
validPrefixes := []string{"ghp_", "gho_", "ghu_", "ghs_", "ghr_", "github_pat_"}
hasValidPrefix := false
for _, prefix := range validPrefixes {
if strings.HasPrefix(token, prefix) {
hasValidPrefix = true
break
}
}
if !hasValidPrefix {
fmt.Printf("[WARNING] The token does not appear to be a valid GitHub PAT (should start with one of %v)\n", validPrefixes)
fmt.Print("Do you want to continue anyway? (y/N): ")
confirm, _ := reader.ReadString('\n')
if strings.TrimSpace(strings.ToLower(confirm)) != "y" {
return "", fmt.Errorf("operation cancelled by user")
}
}
return token, nil
}
func promptSecret(reader *bufio.Reader, prompt, fallback string) string {
if fallback != "" {
fmt.Printf("%s [saved]: ", prompt)
} else {
fmt.Printf("%s: ", prompt)
}
secretBytes, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println()
if err != nil {
return fallback
}
secret := strings.TrimSpace(string(secretBytes))
if secret == "" {
return fallback
}
return secret
}
func promptHiddenRequired(prompt string) (string, error) {
value, err := promptHiddenOptional(prompt)
if err != nil {
return "", err
}
if value == "" {
return "", fmt.Errorf("%s cannot be empty", strings.ToLower(prompt))
}
return value, nil
}
func promptHiddenOptional(prompt string) (string, error) {
fmt.Printf("%s: ", prompt)
if !term.IsTerminal(int(syscall.Stdin)) {
value, err := bufio.NewReader(os.Stdin).ReadString('\n')
if err != nil && len(value) == 0 {
return "", nil
}
return strings.TrimSpace(value), nil
}
value, err := term.ReadPassword(int(syscall.Stdin))
fmt.Println()
if err != nil {
return "", err
}
return strings.TrimSpace(string(value)), nil
}