package screens import ( "fmt" "github.com/calmcacil/wg-admin/internal/config" "github.com/calmcacil/wg-admin/internal/validation" "github.com/calmcacil/wg-admin/internal/wireguard" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" ) // AddScreen is a form for adding new WireGuard clients type AddScreen struct { form *huh.Form quitting bool } // Styles var ( addTitleStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("62")). Bold(true). MarginBottom(1) addHelpStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color("241")). MarginTop(1) ) // NewAddScreen creates a new add screen func NewAddScreen() *AddScreen { // Get default DNS from config cfg, err := config.LoadConfig() defaultDNS := "8.8.8.8, 8.8.4.4" if err == nil && cfg.DNSServers != "" { defaultDNS = cfg.DNSServers } // Create the form form := huh.NewForm( huh.NewGroup( huh.NewInput(). Key("name"). Title("Client Name"). Description("Name for the new client (alphanumeric, -, _)"). Placeholder("e.g., laptop-john"). Validate(func(s string) error { return validation.ValidateClientName(s) }), huh.NewInput(). Key("dns"). Title("DNS Servers"). Description("Comma-separated IPv4 addresses"). Placeholder("e.g., 8.8.8.8, 8.8.4.4"). Value(&defaultDNS). Validate(func(s string) error { return validation.ValidateDNSServers(s) }), huh.NewConfirm(). Key("use_psk"). Title("Use Preshared Key"). Description("Enable additional security layer with a preshared key"). Affirmative("Yes"). Negative("No"), ), ) return &AddScreen{ form: form, quitting: false, } } // Init initializes the add screen func (s *AddScreen) Init() tea.Cmd { return s.form.Init() } // Update handles messages for the add screen func (s *AddScreen) Update(msg tea.Msg) (Screen, tea.Cmd) { var cmds []tea.Cmd switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "q", "ctrl+c", "esc": // Cancel and return to list return nil, nil } } // Update the form form, cmd := s.form.Update(msg) if f, ok := form.(*huh.Form); ok { s.form = f } cmds = append(cmds, cmd) // Check if form is completed if s.form.State == huh.StateCompleted { name := s.form.GetString("name") dns := s.form.GetString("dns") usePSK := s.form.GetBool("use_psk") // Create the client return s, s.createClient(name, dns, usePSK) } return s, tea.Batch(cmds...) } // View renders the add screen func (s *AddScreen) View() string { if s.quitting { return "" } content := lipgloss.JoinVertical( lipgloss.Left, addTitleStyle.Render("Add New WireGuard Client"), s.form.View(), addHelpStyle.Render("Press Enter to submit • Esc to cancel"), ) return content } // createClient creates a new WireGuard client func (s *AddScreen) createClient(name, dns string, usePSK bool) tea.Cmd { return func() tea.Msg { // Create the client via wireguard package err := wireguard.CreateClient(name, dns, usePSK) if err != nil { return errMsg{err: fmt.Errorf("failed to create client: %w", err)} } // Return success message return ClientCreatedMsg{ Name: name, } } } // Messages // ClientCreatedMsg is sent when a client is successfully created type ClientCreatedMsg struct { Name string }