seperate dns retrieval into its own model

This commit is contained in:
juancwu 2026-01-23 19:52:01 +00:00
commit a6bcb9be38
5 changed files with 179 additions and 72 deletions

View file

@ -8,6 +8,7 @@ const (
PageLogin Page = iota
PageMenu
PageListDomains
PageDNSRetrieve
)
type SwitchPageMsg struct {
@ -18,7 +19,10 @@ type SessionReadyMsg struct {
Client *porkbun.Client
}
type ListDomainsMsg struct {
type ListDomainsMsg struct{}
type DNSRetrieveMsg struct {
Domain string
}
type ErrorMsg error

View file

@ -6,6 +6,7 @@ import (
"git.juancwu.dev/juancwu/porkbacon/internal/config"
"git.juancwu.dev/juancwu/porkbacon/internal/ui/messages"
"git.juancwu.dev/juancwu/porkbacon/internal/ui/pages/dns"
"git.juancwu.dev/juancwu/porkbacon/internal/ui/pages/listdomains"
"git.juancwu.dev/juancwu/porkbacon/internal/ui/pages/login"
"git.juancwu.dev/juancwu/porkbacon/internal/ui/pages/menu"
@ -17,6 +18,7 @@ type MainModel struct {
login *login.Model
menu *menu.Model
listDomains *listdomains.Model
dnsRetrieve dns.RetrieveModel
isMenuInit bool
width int
height int
@ -57,17 +59,21 @@ func (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case messages.SessionReadyMsg:
m.menu = menu.New(msg.Client)
m.listDomains = listdomains.New(msg.Client)
m.dnsRetrieve = dns.NewRetrieveModel(msg.Client)
if m.width > 0 && m.height > 0 {
newMenu, _ := m.menu.Update(tea.WindowSizeMsg{Width: m.width, Height: m.height})
m.menu = newMenu.(*menu.Model)
}
m.currentPage = messages.PageMenu
m.isMenuInit = true
return m, tea.Batch(m.menu.Init(), m.listDomains.Init())
return m, tea.Batch(m.menu.Init(), m.listDomains.Init(), m.dnsRetrieve.Init())
case messages.ListDomainsMsg:
m.currentPage = messages.PageListDomains
case messages.DNSRetrieveMsg:
m.currentPage = messages.PageDNSRetrieve
}
var newModel tea.Model
switch m.currentPage {
case messages.PageLogin:
var newLogin tea.Model
@ -78,9 +84,11 @@ func (m MainModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
newMenu, cmd = m.menu.Update(msg)
m.menu = newMenu.(*menu.Model)
case messages.PageListDomains:
var newModel tea.Model
newModel, cmd = m.listDomains.Update(msg)
m.listDomains = newModel.(*listdomains.Model)
case messages.PageDNSRetrieve:
newModel, cmd = m.dnsRetrieve.Update(msg)
m.dnsRetrieve = newModel.(dns.RetrieveModel)
}
cmds = append(cmds, cmd)
@ -95,6 +103,8 @@ func (m MainModel) View() string {
return m.menu.View()
case messages.PageListDomains:
return m.listDomains.View()
case messages.PageDNSRetrieve:
return m.dnsRetrieve.View()
default:
return "Unknown Page"
}

View file

@ -0,0 +1,157 @@
package dns
import (
"fmt"
"strings"
"github.com/charmbracelet/bubbles/paginator"
"github.com/charmbracelet/bubbles/spinner"
"github.com/charmbracelet/bubbles/textinput"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
"git.juancwu.dev/juancwu/porkbacon/internal/porkbun"
"git.juancwu.dev/juancwu/porkbacon/internal/ui/messages"
"git.juancwu.dev/juancwu/porkbacon/internal/ui/utils"
)
type RetrieveModel struct {
client *porkbun.Client
loading bool
records []string
spinner spinner.Model
paginator paginator.Model
textinput textinput.Model
stderr string
}
func NewRetrieveModel(client *porkbun.Client) RetrieveModel {
p := paginator.New()
p.Type = paginator.Dots
p.PerPage = 1
p.ActiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "235", Dark: "252"}).Render("•")
p.InactiveDot = lipgloss.NewStyle().Foreground(lipgloss.AdaptiveColor{Light: "250", Dark: "238"}).Render("•")
s := spinner.New()
s.Spinner = spinner.Dot
s.Style = lipgloss.NewStyle().Foreground(lipgloss.Color("205"))
ti := textinput.New()
ti.Placeholder = "Enter domain"
ti.Width = 80
return RetrieveModel{
client: client,
spinner: s,
paginator: p,
textinput: ti,
}
}
func (m RetrieveModel) Init() tea.Cmd {
return tea.Batch(m.spinner.Tick, textinput.Blink)
}
func (m RetrieveModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
var cmd tea.Cmd
switch msg := msg.(type) {
case tea.KeyMsg:
if m.loading {
return m, nil
}
if msg.String() == "esc" {
hadRecords := len(m.records) > 0
m.loading = false
m.records = nil
return m, func() tea.Msg {
if hadRecords {
return messages.DNSRetrieveMsg{}
}
return messages.SwitchPageMsg{Page: messages.PageMenu}
}
}
if msg.String() == "enter" {
m.loading = true
m.records = nil
return m, retrieveRecords(m.client, m.textinput.Value())
}
if len(m.records) > 0 {
m.paginator, cmd = m.paginator.Update(msg)
return m, cmd
}
m.textinput, cmd = m.textinput.Update(msg)
return m, cmd
case messages.DNSRetrieveMsg:
m.textinput.Reset()
m.textinput.Focus()
case *porkbun.DNSRecordsResponse:
m.loading = false
for _, record := range msg.Records {
m.records = append(m.records, renderRecord(&record))
}
m.paginator.SetTotalPages(len(m.records))
m.paginator.Page = 0
case messages.ErrorMsg:
m.stderr = fmt.Sprintf("Error: %v", msg)
}
if m.loading {
m.spinner, cmd = m.spinner.Update(msg)
return m, tea.Batch(cmd, m.spinner.Tick)
}
return m, textinput.Blink
}
func (m RetrieveModel) View() string {
if m.stderr != "" {
return fmt.Sprintf("%s\n\n(Press ctrl+c to quit)", m.stderr)
}
if m.loading {
return fmt.Sprintf("\n\n %s Loading... press ctl+c to quit\n\n", m.spinner.View())
}
if len(m.records) > 0 {
return fmt.Sprintf("%s\n\n%s\n\n(Press Esc to go back, arrows to navigate)", m.records[m.paginator.Page], m.paginator.View())
}
return fmt.Sprintf(
"Enter domain to retrieve records for:\n\n%s\n\n(esc to quit)",
m.textinput.View(),
)
}
func retrieveRecords(client *porkbun.Client, domain string) tea.Cmd {
return func() tea.Msg {
resp, err := client.RetrieveDNSRecords(domain)
if err != nil {
return messages.ErrorMsg(err)
}
return resp
}
}
func renderRecord(item *porkbun.DNSRecord) string {
var b strings.Builder
b.WriteString("ID: " + item.ID + "\n")
b.WriteString("Name: " + item.Name + "\n")
b.WriteString("Type: " + item.Type + "\n")
b.WriteString(fmt.Sprintln("TTL:", item.TTL))
b.WriteString(fmt.Sprintln("Priority:", item.Priority))
b.WriteString("Content: ")
b.WriteString(utils.WrapText(item.Content, 80))
b.WriteString("\n")
b.WriteString("Notes: " + item.Notes + "\n")
return b.String()
}

View file

@ -51,10 +51,9 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
if !m.loading && msg.String() == "esc" {
m.loading = false
m.domains = nil
return m, func() tea.Msg {
m.loading = false
m.domains = nil
m.client = nil
return messages.SwitchPageMsg{Page: messages.PageMenu}
}
}

View file

@ -17,11 +17,6 @@ type pingResultMsg struct {
IP string
}
type dnsRecordListMsg struct {
Status string
Records []string
}
type menuItem struct {
id uint8
title, desc string
@ -53,7 +48,6 @@ type Model struct {
textInput textinput.Model
loading bool
state uint8
records []string
client *porkbun.Client
err error
output string
@ -109,42 +103,6 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
case tea.KeyMsg:
if m.loading && msg.String() == "ctrl+c" {
return m, tea.Quit
}
if m.state == stateDNSRetrieveGetDomain {
switch msg.String() {
case "enter":
domain := m.textInput.Value()
m.textInput.SetValue("")
m.state = stateNoOp
m.loading = true
cmd := func() tea.Msg {
resp, err := m.client.RetrieveDNSRecords(domain)
if err != nil {
return messages.ErrorMsg(err)
}
var records []string
for _, record := range resp.Records {
records = append(records, renderRecordItem(&record))
}
return dnsRecordListMsg{
Status: resp.Status,
Records: records,
}
}
return m, tea.Batch(cmd, m.spinner.Tick)
case "esc":
m.state = stateNoOp
m.textInput.SetValue("")
return m, nil
}
var cmd tea.Cmd
m.textInput, cmd = m.textInput.Update(msg)
return m, cmd
}
if m.output != "" {
if msg.String() == "esc" {
m.output = ""
@ -153,16 +111,6 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
return m, nil
}
if len(m.records) > 0 {
var cmd tea.Cmd
m.paginator, cmd = m.paginator.Update(msg)
if msg.String() == "esc" {
m.records = nil
return m, nil
}
return m, cmd
}
if msg.String() == "enter" {
i, ok := m.list.SelectedItem().(menuItem)
if ok {
@ -170,13 +118,6 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
}
}
case dnsRecordListMsg:
m.loading = false
m.paginator.PerPage = 1
m.paginator.SetTotalPages(len(msg.Records))
m.paginator.Page = 0
return m, nil
case pingResultMsg:
m.loading = false
m.output = fmt.Sprintf("Ping successful!\nYour IP: %s", msg.IP)
@ -213,19 +154,15 @@ func (m *Model) View() string {
)
}
if len(m.records) > 0 {
return fmt.Sprintf("%s\n\n%s\n\n(Press Esc to go back, arrows to navigate)", m.records[m.paginator.Page], m.paginator.View())
}
return m.list.View()
}
func (m *Model) handleSelection(i menuItem) (tea.Model, tea.Cmd) {
switch i.id {
case dnsRetrieveRecords:
m.state = stateDNSRetrieveGetDomain
m.textInput.Focus()
return m, textinput.Blink
return m, func() tea.Msg {
return messages.DNSRetrieveMsg{}
}
case domainListAll:
return m, func() tea.Msg {
return messages.ListDomainsMsg{}