improve porkbun credentials loading
This commit is contained in:
parent
ec94e6ad46
commit
4b92d2e7c4
3 changed files with 112 additions and 77 deletions
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func main() {
|
func main() {
|
||||||
a := app.New()
|
a := app.New()
|
||||||
|
|
||||||
p := tea.NewProgram(a)
|
p := tea.NewProgram(&a)
|
||||||
if _, err := p.Run(); err != nil {
|
if _, err := p.Run(); err != nil {
|
||||||
fmt.Println("Error:", err)
|
fmt.Println("Error:", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.juancwu.dev/juancwu/porkbacon/internal/pass"
|
"git.juancwu.dev/juancwu/porkbacon/internal/pass"
|
||||||
|
"git.juancwu.dev/juancwu/porkbacon/internal/porkbun"
|
||||||
"github.com/charmbracelet/bubbles/spinner"
|
"github.com/charmbracelet/bubbles/spinner"
|
||||||
"github.com/charmbracelet/bubbles/textinput"
|
"github.com/charmbracelet/bubbles/textinput"
|
||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
|
@ -11,18 +12,23 @@ import (
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StateInit uint = iota
|
StateInit uint = iota
|
||||||
StateRetrivePorkbunCredentials
|
StateLoadingCredentials
|
||||||
StateIdle
|
StateIdle
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type CredentialsLoadedMsg struct {
|
||||||
|
APIKey string
|
||||||
|
SecretKey string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ErrMsg error
|
||||||
|
|
||||||
type App struct {
|
type App struct {
|
||||||
state uint
|
state uint
|
||||||
|
|
||||||
apiKeyName string
|
apiKeyName string
|
||||||
secretApiKeyName string
|
secretApiKeyName string
|
||||||
apiKey string
|
client *porkbun.Client
|
||||||
secretKey string
|
|
||||||
loading bool
|
|
||||||
|
|
||||||
textInput textinput.Model
|
textInput textinput.Model
|
||||||
spinner spinner.Model
|
spinner spinner.Model
|
||||||
|
|
@ -32,7 +38,7 @@ type App struct {
|
||||||
|
|
||||||
func New() App {
|
func New() App {
|
||||||
ti := textinput.New()
|
ti := textinput.New()
|
||||||
ti.Placeholder = "Pass/Name"
|
ti.Placeholder = "Pass Name"
|
||||||
ti.Focus()
|
ti.Focus()
|
||||||
ti.Width = 20
|
ti.Width = 20
|
||||||
|
|
||||||
|
|
@ -50,115 +56,117 @@ func (a App) Init() tea.Cmd {
|
||||||
return tea.Sequence(textinput.Blink, a.spinner.Tick)
|
return tea.Sequence(textinput.Blink, a.spinner.Tick)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
var cmds []tea.Cmd
|
var cmds []tea.Cmd
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
var key string
|
|
||||||
|
|
||||||
switch msg := msg.(type) {
|
switch msg := msg.(type) {
|
||||||
case tea.KeyMsg:
|
case tea.KeyMsg:
|
||||||
switch msg.String() {
|
switch msg.String() {
|
||||||
case "ctrl+c":
|
case "ctrl+c":
|
||||||
return a, tea.Quit
|
return a, tea.Quit
|
||||||
default:
|
case "enter":
|
||||||
key = msg.String()
|
if a.state == StateInit {
|
||||||
|
return a.handleEnter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.err != nil {
|
case CredentialsLoadedMsg:
|
||||||
|
a.client = porkbun.New(msg.APIKey, msg.SecretKey)
|
||||||
|
a.state = StateIdle
|
||||||
|
return a, nil
|
||||||
|
|
||||||
|
case ErrMsg:
|
||||||
|
a.err = msg
|
||||||
|
a.state = StateIdle // Or stay in error state
|
||||||
return a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.state == StateInit && key == "enter" {
|
if a.state == StateInit {
|
||||||
return a.handleEnter()
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.state == StateRetrivePorkbunCredentials && !a.loading {
|
|
||||||
return a.retrievePorkbunCredentials()
|
|
||||||
}
|
|
||||||
|
|
||||||
a.textInput, cmd = a.textInput.Update(msg)
|
a.textInput, cmd = a.textInput.Update(msg)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.state == StateLoadingCredentials {
|
||||||
a.spinner, cmd = a.spinner.Update(msg)
|
a.spinner, cmd = a.spinner.Update(msg)
|
||||||
cmds = append(cmds, cmd)
|
cmds = append(cmds, cmd)
|
||||||
|
}
|
||||||
|
|
||||||
return a, tea.Sequence(cmds...)
|
return a, tea.Sequence(cmds...)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a App) View() string {
|
func (a *App) View() string {
|
||||||
if a.err != nil {
|
if a.err != nil {
|
||||||
return a.err.Error()
|
return fmt.Sprintf("Error: %v\n\nPress Ctrl+C to quit.", a.err)
|
||||||
}
|
|
||||||
|
|
||||||
if a.loading {
|
|
||||||
return fmt.Sprintf("%s Loading...", a.spinner.View())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch a.state {
|
switch a.state {
|
||||||
case StateInit:
|
case StateInit:
|
||||||
return a.renderPorkbunCredentialForm()
|
return a.renderPorkbunCredentialForm()
|
||||||
|
case StateLoadingCredentials:
|
||||||
|
return fmt.Sprintf("%s Loading credentials...", a.spinner.View())
|
||||||
case StateIdle:
|
case StateIdle:
|
||||||
return fmt.Sprintf("API key: %s\nSecret Key: %s\n\n", a.apiKey, a.secretKey)
|
return fmt.Sprintf("Ready!\nAPI Key: %s...\nSecret: %s...\n",
|
||||||
|
mask(a.client.APIKey), mask(a.client.SecretAPIKey))
|
||||||
}
|
}
|
||||||
return "Invalid state"
|
return "Invalid state"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mask(s string) string {
|
||||||
|
if len(s) > 4 {
|
||||||
|
return s[:4] + "...."
|
||||||
|
}
|
||||||
|
return "...."
|
||||||
|
}
|
||||||
|
|
||||||
func (a *App) renderPorkbunCredentialForm() string {
|
func (a *App) renderPorkbunCredentialForm() string {
|
||||||
if a.apiKeyName == "" {
|
title := "Enter Porkbun API Key Name (pass entry):"
|
||||||
return fmt.Sprintf("Enter Porkbun API Key Name:\n%s\n\n", a.textInput.View())
|
if a.apiKeyName != "" {
|
||||||
|
title = "Enter Porkbun Secret API Key Name (pass entry):"
|
||||||
}
|
}
|
||||||
|
|
||||||
if a.secretApiKeyName == "" {
|
return fmt.Sprintf("%s\n%s\n\n(Press Enter to confirm)", title, a.textInput.View())
|
||||||
return fmt.Sprintf("Enter Porkbun Secret API Key Name:\n%s\n\n", a.textInput.View())
|
|
||||||
}
|
|
||||||
|
|
||||||
return "Invalid state"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) handleEnter() (tea.Model, tea.Cmd) {
|
func (a *App) handleEnter() (tea.Model, tea.Cmd) {
|
||||||
switch a.state {
|
val := a.textInput.Value()
|
||||||
case StateInit:
|
if val == "" {
|
||||||
if a.apiKeyName == "" && a.textInput.Value() != "" {
|
return a, nil
|
||||||
a.apiKeyName = a.textInput.Value()
|
}
|
||||||
|
|
||||||
|
if a.apiKeyName == "" {
|
||||||
|
a.apiKeyName = val
|
||||||
a.textInput.Reset()
|
a.textInput.Reset()
|
||||||
} else if a.secretApiKeyName == "" && a.textInput.Value() != "" {
|
a.textInput.Placeholder = "Secret Key Name"
|
||||||
a.secretApiKeyName = a.textInput.Value()
|
return a, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.secretApiKeyName == "" {
|
||||||
|
a.secretApiKeyName = val
|
||||||
a.textInput.Reset()
|
a.textInput.Reset()
|
||||||
a.state = StateRetrivePorkbunCredentials
|
a.state = StateLoadingCredentials
|
||||||
}
|
return a, retrievePorkbunCredentialsCmd(a.apiKeyName, a.secretApiKeyName)
|
||||||
}
|
}
|
||||||
|
|
||||||
return *a, nil
|
return a, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *App) retrievePorkbunCredentials() (tea.Model, tea.Cmd) {
|
func retrievePorkbunCredentialsCmd(apiKeyName, secretKeyName string) tea.Cmd {
|
||||||
if a.apiKeyName == "" || a.secretApiKeyName == "" {
|
return func() tea.Msg {
|
||||||
panic(fmt.Errorf("Porkbun credentials incomplete. Either API key or secret API key not defined."))
|
apiKey, err := pass.Get(apiKeyName)
|
||||||
}
|
|
||||||
|
|
||||||
a.loading = true
|
|
||||||
|
|
||||||
apiKey, err := pass.Get(a.apiKeyName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.err = err
|
return ErrMsg(err)
|
||||||
a.state = StateIdle
|
|
||||||
a.loading = false
|
|
||||||
return *a, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
secretKey, err := pass.Get(a.secretApiKeyName)
|
secretKey, err := pass.Get(secretKeyName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
a.err = err
|
return ErrMsg(err)
|
||||||
a.state = StateIdle
|
|
||||||
a.loading = false
|
|
||||||
return *a, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
a.apiKey = apiKey
|
return CredentialsLoadedMsg{
|
||||||
a.secretKey = secretKey
|
APIKey: apiKey,
|
||||||
a.state = StateIdle
|
SecretKey: secretKey,
|
||||||
a.loading = false
|
|
||||||
|
|
||||||
return *a, nil
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
||||||
27
internal/porkbun/client.go
Normal file
27
internal/porkbun/client.go
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
package porkbun
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const BaseURL = "https://api.porkbun.com/api/json/v3"
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
APIKey string
|
||||||
|
SecretAPIKey string
|
||||||
|
HTTPClient *http.Client
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(apiKey, secretKey string) *Client {
|
||||||
|
return &Client{
|
||||||
|
APIKey: apiKey,
|
||||||
|
SecretAPIKey: secretKey,
|
||||||
|
HTTPClient: &http.Client{
|
||||||
|
Timeout: 10 * time.Second,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue