Commit 19aef7e

Ansari <ping@ansari.wtf>
2026-03-02 15:05:41
Implementing Rules Section and deleting rule feat added
1 parent 2d65333
Changed files (7)
cmd/ufWall/main.go
@@ -12,7 +12,7 @@ import (
 
 func main() {
 
-	if _, err := exec.LookPath("ufsw"); err != nil {
+	if _, err := exec.LookPath("ufw"); err != nil {
 		fmt.Println("Please first install ufw and try again.")
 		os.Exit(1)
 	}
internal/app/update.go
@@ -56,6 +56,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			newPolicy, sectionCmd := m.policySection.Update(msg, m.policy)
 			m.policySection = newPolicy
 			return m, sectionCmd
+
+		case sections.RulesSection:
+			newRules, sectionCmd := m.rulesSection.Update(msg,m.rules)
+			m.rulesSection = newRules
+			return m, sectionCmd
 		}
 
 	case keys.RefreshMsg:
@@ -93,5 +98,5 @@ func (m *model) focusActiveSection() {
 }
 
 func (m *model) isMenuOpen() bool {
-	return m.statsSection.GetMenu() != nil
+	return m.statsSection.GetMenu() != nil || m.rulesSection.GetMenu() != nil
 }
internal/app/view.go
@@ -2,7 +2,6 @@ package app
 
 import (
 	"fmt"
-	"ufWall/internal/sections"
 	"ufWall/internal/ui"
 
 	"github.com/charmbracelet/lipgloss"
@@ -82,9 +81,18 @@ func (m model) View() string {
 		layout,
 	)
 
-	if m.activeSection == sections.StatsSection && m.statsSection.GetMenu() != nil {
+	var menuOpen *ui.Menu
+
+	if m.statsSection.GetMenu() != nil {
+		menuOpen = m.statsSection.GetMenu()
+	} else if m.rulesSection.GetMenu() != nil {
+		menuOpen = m.rulesSection.GetMenu()
+	}
+
+	if menuOpen != nil {
+
 		dimmed := "\x1b[2m" + lipgloss.NewStyle().Faint(true).Render(layout) + "\x1b[0m"
-		menuView := m.statsSection.GetMenu().View(m.styles)
+		menuView := menuOpen.View(m.styles)
 
 		menuW := lipgloss.Width(menuView)
 		menuH := lipgloss.Height(menuView)
internal/sections/rules/model.go
@@ -5,24 +5,40 @@ import (
 )
 
 type Model struct {
-	styles ui.Styles
-	active bool
+	styles     ui.Styles
+	totalOpts  int
+	cursorLine int 
+	showMenu   bool
+	menu       *ui.Menu
+	active     bool
 }
 
 func New(styles ui.Styles) Model {
 	return Model{
-		styles: styles,
-		active: false,
+		styles:     styles,
+		cursorLine: 0,
+		showMenu:   false,
+		menu:       nil,
+		active:     false,
+		totalOpts:  2,
 	}
 }
 
 func (m *Model) Focus() {
-	// m.cursorLine = 0
+	m.cursorLine = 0
 	m.active = true
 }
 
 func (m *Model) Blur() {
 	m.active = false
-	// m.showMenu = false
-	// m.cursorLine = 0
+	m.showMenu = false
+	m.cursorLine = 0
+}
+
+func (m Model) HasOpenMenu() bool {
+	return m.menu != nil
+}
+
+func (m Model) GetMenu() *ui.Menu {
+	return m.menu
 }
internal/sections/rules/update.go
@@ -0,0 +1,64 @@
+package rules
+
+import (
+	"log"
+	"ufWall/internal/keys"
+	"ufWall/internal/ufw"
+	"ufWall/internal/ui"
+
+	"github.com/charmbracelet/bubbles/key"
+	tea "github.com/charmbracelet/bubbletea"
+)
+
+func (m Model) Update(msg tea.Msg, rules []ufw.Rule) (Model, tea.Cmd) {
+	if m.menu != nil {
+		if quit := m.menu.Update(msg); quit {
+			m.menu = nil
+			return m, keys.Refresh()
+		}
+	} else {
+		switch msg := msg.(type) {
+		case tea.KeyMsg:
+			switch {
+			case key.Matches(msg, keys.Bindings.CursorUp):
+				if m.cursorLine > 0 {
+					m.cursorLine--
+				}
+			case key.Matches(msg, keys.Bindings.CursorDown):
+				if m.cursorLine < len(rules)-1 {
+					m.cursorLine++
+				}
+			case key.Matches(msg, keys.Bindings.Execute):
+				if len(rules) > 0 {
+					return m.openMenu(rules)
+				}
+			}
+		}
+	}
+	return m, nil
+}
+
+func (m Model) openMenu(rules []ufw.Rule) (Model, tea.Cmd) {
+	if len(rules) == 0 || m.cursorLine >= len(rules) {
+		return m, nil
+	}
+
+	selectedRule := rules[m.cursorLine]
+	log.Println(selectedRule)
+
+	options := ui.MakeMenuItems(
+		[]string{"Delete Rule"},
+		func(label string) tea.Cmd {
+			switch label {
+			case "Delete Rule":
+				log.Printf("Deleting rule #%d", selectedRule.Num)
+				ufw.DeleteRule(selectedRule.Num)
+			}
+			return nil
+		},
+	)
+
+	menu := ui.NewMenu(options, m.styles)
+	m.menu = &menu
+	return m, nil
+}
internal/sections/rules/view.go
@@ -18,9 +18,11 @@ func (m Model) View(rules []ufw.Rule) string {
 
 	headerContent := m.styles.Label.UnsetWidth().Render(header)
 	line := strings.Repeat("─", lipgloss.Width(headerContent))
-	rows = append(rows, headerContent, m.styles.Label.UnsetWidth().Render(line))
+	rows = append(rows, "  "+headerContent, "  "+m.styles.Label.UnsetWidth().Render(line))
 
-	for _, r := range rules {
+	sectionActiveNoMenu := m.menu == nil && m.active
+
+	for i, r := range rules {
 		row := fmt.Sprintf(
 			"%-3d │ %-6s │ %-5s │ %-16s │ %-5s │ %-16s │ %-5s",
 			r.Num,
@@ -31,7 +33,7 @@ func (m Model) View(rules []ufw.Rule) string {
 			r.ToDest,
 			r.ToPort,
 		)
-		rows = append(rows, m.styles.Value.Render(row))
+		rows = append(rows, ui.InsertCursor(row, m.cursorLine == i && sectionActiveNoMenu, m.styles))
 	}
 	table := strings.Join(rows, "\n")
 	content := lipgloss.JoinVertical(
internal/ufw/main.go
@@ -120,7 +120,7 @@ func Disable() (stdout, stderr string, err error) {
 }
 
 func DeleteRule(num int) (stdout, stderr string, err error) {
-	return RunSudo("delete", fmt.Sprintf("%d", num))
+	return RunSudo("--force", "delete", fmt.Sprintf("%d", num))
 }
 
 func DefaultIncoming(allow bool) (stdout, stderr string, err error) {