156 lines
3.4 KiB
Go
156 lines
3.4 KiB
Go
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"
|
|
"github.com/charmbracelet/bubbles/spinner"
|
|
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
|
|
spinner spinner.Model
|
|
isCreating 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
|
|
}
|