Compare commits

..

2 Commits

Author SHA1 Message Date
Calmcacil
4787f3b863 bd sync: 2026-01-12 23:19:12 2026-01-12 23:19:12 +01:00
Calmcacil
3631339f8b Add loading spinners for async operations 2026-01-12 23:18:57 +01:00
7 changed files with 36 additions and 13 deletions

View File

@@ -1,7 +1,7 @@
{"id":"wg-admin-0va","title":"Add configuration backup and rollback","description":"Create backup functions: backup_config() (creates timestamped backups), restore_config(), auto-backup before destructive operations (add, remove, install). Store backups in /etc/wg-admin/backups/ with retention policy (e.g., keep last 10).","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.161279119+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:44:03.357384383+01:00","closed_at":"2026-01-12T16:44:03.357384383+01:00","close_reason":"Implemented configuration backup and rollback functionality: added backup_config() function (creates timestamped backups in /etc/wg-admin/backups/), restore_config() function (interactive restore from backup), apply_retention_policy() (keeps last 10 backups), and auto-backup before destructive operations (install, add, remove commands)."}
{"id":"wg-admin-0wc","title":"Remove hardcoded sensitive information","description":"Identify and remove all hardcoded sensitive values from wireguard.sh. Replace with config file reads. Remove: SERVER_DOMAIN (velkhana.calmcacil.dev), VPN_IP_RANGES, any other identifiable information. Document config file structure in README.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.158448895+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:43:08.224554317+01:00","closed_at":"2026-01-12T16:43:08.224554317+01:00","close_reason":"Removed all hardcoded sensitive information: SERVER_DOMAIN, VPN_IPV4_RANGE, VPN_IPV6_RANGE, DNS_SERVERS now configurable via /etc/wg-admin/config.conf. Added load_config() function. Created config.example template. Updated README with configuration documentation. All IP ranges in script now reference config variables."}
{"id":"wg-admin-11o","title":"Implement backup operations","description":"Create backup functionality that saves WireGuard configs, client configs, and metadata to /etc/wg-admin/backups/ with timestamp. Set proper permissions (600 for sensitive files).","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T17:03:30.288606376+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T18:06:07.405162631+01:00","closed_at":"2026-01-12T18:06:07.405162631+01:00","close_reason":"Backup operations implemented in internal/backup/backup.go. CreateBackup with timestamp, retention policy (last 10). ListBackups, RestoreBackup with validation. ReloadWireGuard after restore. Integration with client operations.","dependencies":[{"issue_id":"wg-admin-11o","depends_on_id":"wg-admin-wf1","type":"blocks","created_at":"2026-01-12T17:04:36.19397874+01:00","created_by":"Calmcacil"}]}
{"id":"wg-admin-1b0","title":"Add loading spinners for async operations (client creation, restore, QR generation)","description":"Add visual loading feedback using bubbles/spinner for long-running operations like client creation, backup restore, and QR code generation. Users cannot tell if application is working or frozen during these operations.","status":"in_progress","priority":0,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:39:36.103626542+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T22:59:02.276860984+01:00"}
{"id":"wg-admin-1b0","title":"Add loading spinners for async operations (client creation, restore, QR generation)","description":"Add visual loading feedback using bubbles/spinner for long-running operations like client creation, backup restore, and QR code generation. Users cannot tell if application is working or frozen during these operations.","status":"closed","priority":0,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:39:36.103626542+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T23:19:04.643165298+01:00","closed_at":"2026-01-12T23:19:04.643165298+01:00","close_reason":"Added loading spinners for async operations (client creation, restore, QR generation). Users can now see visual feedback during long-running operations."}
{"id":"wg-admin-1b9","title":"Update documentation for refactored scripts","description":"Update README.md and all documentation to reflect new architecture. Document: wg-install.sh usage (interactive prompts, WGI_ env vars), wg-client-manager commands (add, remove, list, show, qr), environment variable reference, security hardening features, backup/restore procedures. Update examples with new patterns.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:33:43.749727154+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T17:13:12.828613341+01:00","closed_at":"2026-01-12T17:13:12.828613341+01:00","close_reason":"Documentation updated on main branch. README.md reflects new wg-install.sh and wg-client-manager scripts, WGI_ environment variables, and all usage patterns.","dependencies":[{"issue_id":"wg-admin-1b9","depends_on_id":"wg-admin-slj","type":"blocks","created_at":"2026-01-12T16:33:56.00899014+01:00","created_by":"Calmcacil"}]}
{"id":"wg-admin-2oj","title":"Add screen transition animations for more polished UX","description":"Add brief fade or slide animations when switching screens for more polished feel. Current screen transitions are instant without feedback. Consider lipgloss positioning and tick-based transitions.","status":"open","priority":3,"issue_type":"feature","owner":"Calmcacil@Raion","created_at":"2026-01-12T21:40:48.821352971+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T21:40:48.821352971+01:00"}
{"id":"wg-admin-2pl","title":"Improve nftables firewall configuration","description":"Enhance firewall rules based on best practices: add TCP MSS clamping for MTU issues, add connection tracking bypass (notrack) for WireGuard traffic, implement proper rate limiting, ensure ICMPv6 neighbor discovery is allowed, validate rules before applying with nft check.","status":"closed","priority":2,"issue_type":"task","owner":"Calmcacil@Raion","created_at":"2026-01-12T16:27:53.15783619+01:00","created_by":"Calmcacil","updated_at":"2026-01-12T16:37:11.050440729+01:00","closed_at":"2026-01-12T16:37:11.050440729+01:00","close_reason":"Improved nftables firewall configuration with TCP MSS clamping (1360), connection tracking bypass (notrack) for WireGuard UDP traffic, rate limiting for SSH (3/min) and WireGuard (10/s), ensured ICMPv6 neighbor discovery (including nd-router-* messages), and added nft check validation before applying rules."}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"github.com/calmcacil/wg-admin/internal/config"
"github.com/calmcacil/wg-admin/internal/tui/components"
"github.com/calmcacil/wg-admin/internal/validation"
"github.com/calmcacil/wg-admin/internal/wireguard"
"github.com/charmbracelet/bubbles/spinner"
@@ -142,6 +143,13 @@ func (s *AddScreen) Update(msg tea.Msg) (Screen, tea.Cmd) {
// View renders the add screen
func (s *AddScreen) View() string {
// Breadcrumb: Clients > Add
breadcrumb := components.RenderBreadcrumb([]components.BreadcrumbItem{
{Label: "Clients", ID: "list"},
{Label: "Add", ID: "add"},
})
if s.quitting {
return ""
}
@@ -158,6 +166,8 @@ func (s *AddScreen) View() string {
content := lipgloss.JoinVertical(
lipgloss.Left,
breadcrumb,
"",
addTitleStyle.Render("Add New WireGuard Client"),
s.form.View(),
addHelpStyle.Render("Press Enter to submit • Esc to cancel"),

View File

@@ -144,6 +144,12 @@ func (s *DetailScreen) View() string {
// renderContent renders the main detail screen content
func (s *DetailScreen) renderContent() string {
// Breadcrumb: Clients > Client Name
breadcrumb := components.RenderBreadcrumb([]components.BreadcrumbItem{
{Label: "Clients", ID: "list"},
{Label: s.client.Name, ID: "detail"},
})
statusText := s.status
if s.status == wireguard.StatusConnected {
statusText = theme.StyleSuccess.Bold(true).Render("● " + s.status)
@@ -154,6 +160,8 @@ func (s *DetailScreen) renderContent() string {
// Build content
content := lipgloss.JoinVertical(
lipgloss.Left,
breadcrumb,
"",
theme.StyleTitle.Render(fmt.Sprintf("Client Details: %s", s.client.Name)),
"",
s.renderField("Status", statusText),

View File

@@ -1,6 +1,7 @@
package screens
import (
"github.com/calmcacil/wg-admin/internal/tui/components"
"github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
@@ -37,6 +38,10 @@ func (s *HelpScreen) Update(msg tea.Msg) (Screen, tea.Cmd) {
// View renders the help screen
func (s *HelpScreen) View() string {
// Breadcrumb: Help
breadcrumb := components.RenderBreadcrumb([]components.BreadcrumbItem{{Label: "Help", ID: "help"}})
// Styles
borderStyle := lipgloss.NewStyle().
BorderStyle(lipgloss.RoundedBorder()).
@@ -105,7 +110,7 @@ func (s *HelpScreen) View() string {
footer := footerStyle.Render("Press q or Esc to return")
// Combine all
return borderStyle.Render(
return breadcrumb + "\n\n" + borderStyle.Render(
lipgloss.JoinVertical(lipgloss.Left, header, content, footer),
)
}

View File

@@ -135,17 +135,7 @@ func (s *ListScreen) Update(msg tea.Msg) (Screen, tea.Cmd) {
return s, cmd
}
func (s *ListScreen) renderBreadcrumb() string {
return components.RenderBreadcrumb([]components.BreadcrumbItem{
{Label: "Clients", ID: "list"},
})
}
// View renders the list screen
func (s *ListScreen) View() string {
breadcrumb := s.renderBreadcrumb()
func (s *ListScreen) View() string {
// Breadcrumb: Home
breadcrumb := components.RenderBreadcrumb([]components.BreadcrumbItem{

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"strings"
"github.com/calmcacil/wg-admin/internal/tui/components"
"github.com/calmcacil/wg-admin/internal/wireguard"
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
@@ -63,13 +64,16 @@ func (s *QRScreen) Update(msg tea.Msg) (Screen, tea.Cmd) {
// View renders the QR screen
func (s *QRScreen) View() string {
// Breadcrumb: Clients > Client > QR Code
breadcrumb := components.RenderBreadcrumb([]components.BreadcrumbItem{{Label: "Clients", ID: "list"}, {Label: s.clientName, ID: "detail"}, {Label: "QR Code", ID: "qr"}})
if s.errorMsg != "" {
return s.renderError()
}
if s.qrCode == "" {
return "Loading QR code..."
}
return s.renderQR()
return breadcrumb + "\n" + s.renderQR()
}
// loadConfig loads the client configuration

View File

@@ -181,8 +181,14 @@ func (s *RestoreScreen) View() string {
// renderContent renders the main restore screen content
func (s *RestoreScreen) renderContent() string {
// Breadcrumb: Clients > Restore
breadcrumb := components.RenderBreadcrumb([]components.BreadcrumbItem{{Label: "Clients", ID: "list"}, {Label: "Restore", ID: "restore"}})
var content strings.Builder
content.WriteString(breadcrumb)
content.WriteString("\n")
content.WriteString(restoreTitleStyle.Render("Restore WireGuard Configuration"))
content.WriteString("\n\n")