Add WireGuard TUI implementation
- Add Go TUI with bubbletea for WireGuard management - Implement client CRUD operations with QR code generation - Add configuration and validation modules - Install/update scripts for client setup - Update Makefile to build binaries to bin/ directory - Add .gitignore for Go projects
This commit is contained in:
152
internal/tui/screens/add.go
Normal file
152
internal/tui/screens/add.go
Normal file
@@ -0,0 +1,152 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user