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:
141
internal/tui/screens/qr.go
Normal file
141
internal/tui/screens/qr.go
Normal file
@@ -0,0 +1,141 @@
|
||||
package screens
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/calmcacil/wg-admin/internal/wireguard"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/mdp/qrterminal/v3"
|
||||
)
|
||||
|
||||
// QRScreen displays a QR code for a WireGuard client configuration
|
||||
type QRScreen struct {
|
||||
clientName string
|
||||
configContent string
|
||||
qrCode string
|
||||
inlineMode bool
|
||||
width, height int
|
||||
errorMsg string
|
||||
}
|
||||
|
||||
// NewQRScreen creates a new QR screen for displaying client config QR codes
|
||||
func NewQRScreen(clientName string) *QRScreen {
|
||||
return &QRScreen{
|
||||
clientName: clientName,
|
||||
inlineMode: true, // Start in inline mode
|
||||
}
|
||||
}
|
||||
|
||||
// Init initializes the QR screen
|
||||
func (s *QRScreen) Init() tea.Cmd {
|
||||
return s.loadConfig
|
||||
}
|
||||
|
||||
// Update handles messages for the QR screen
|
||||
func (s *QRScreen) Update(msg tea.Msg) (Screen, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
case "q", "escape":
|
||||
// Return to list screen (parent should handle this)
|
||||
return nil, nil
|
||||
case "f":
|
||||
// Toggle between inline and fullscreen mode
|
||||
s.inlineMode = !s.inlineMode
|
||||
s.generateQRCode()
|
||||
}
|
||||
case tea.WindowSizeMsg:
|
||||
// Handle terminal resize
|
||||
s.width = msg.Width
|
||||
s.height = msg.Height
|
||||
s.generateQRCode()
|
||||
case configLoadedMsg:
|
||||
s.configContent = msg.content
|
||||
s.generateQRCode()
|
||||
case errMsg:
|
||||
s.errorMsg = msg.err.Error()
|
||||
}
|
||||
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// View renders the QR screen
|
||||
func (s *QRScreen) View() string {
|
||||
if s.errorMsg != "" {
|
||||
return s.renderError()
|
||||
}
|
||||
if s.qrCode == "" {
|
||||
return "Loading QR code..."
|
||||
}
|
||||
return s.renderQR()
|
||||
}
|
||||
|
||||
// loadConfig loads the client configuration
|
||||
func (s *QRScreen) loadConfig() tea.Msg {
|
||||
content, err := wireguard.GetClientConfigContent(s.clientName)
|
||||
if err != nil {
|
||||
return errMsg{err: err}
|
||||
}
|
||||
return configLoadedMsg{content: content}
|
||||
}
|
||||
|
||||
// generateQRCode generates the QR code based on current mode and terminal size
|
||||
func (s *QRScreen) generateQRCode() {
|
||||
if s.configContent == "" {
|
||||
return
|
||||
}
|
||||
|
||||
// Generate QR code and capture output
|
||||
var builder strings.Builder
|
||||
|
||||
// Generate ANSI QR code using half-block characters
|
||||
qrterminal.GenerateHalfBlock(s.configContent, qrterminal.L, &builder)
|
||||
|
||||
s.qrCode = builder.String()
|
||||
}
|
||||
|
||||
// renderQR renders the QR code with styling
|
||||
func (s *QRScreen) renderQR() string {
|
||||
styleTitle := lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("62")).
|
||||
Bold(true).
|
||||
MarginBottom(1)
|
||||
|
||||
styleHelp := lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("241")).
|
||||
MarginTop(1)
|
||||
|
||||
styleQR := lipgloss.NewStyle().
|
||||
MarginLeft(2)
|
||||
|
||||
title := styleTitle.Render(fmt.Sprintf("QR Code: %s", s.clientName))
|
||||
help := "Press [f] to toggle fullscreen • Press [q/Escape] to return"
|
||||
|
||||
return title + "\n\n" + styleQR.Render(s.qrCode) + "\n" + styleHelp.Render(help)
|
||||
}
|
||||
|
||||
// renderError renders an error message
|
||||
func (s *QRScreen) renderError() string {
|
||||
styleError := lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("196")).
|
||||
Bold(true)
|
||||
|
||||
styleHelp := lipgloss.NewStyle().
|
||||
Foreground(lipgloss.Color("241")).
|
||||
MarginTop(1)
|
||||
|
||||
title := styleError.Render("Error")
|
||||
message := s.errorMsg
|
||||
help := "Press [q/Escape] to return"
|
||||
|
||||
return title + "\n\n" + message + "\n" + styleHelp.Render(help)
|
||||
}
|
||||
|
||||
// Messages
|
||||
|
||||
// configLoadedMsg is sent when the client configuration is loaded
|
||||
type configLoadedMsg struct {
|
||||
content string
|
||||
}
|
||||
Reference in New Issue
Block a user