فهرست منبع

feat(routing): hide default/system rules by default in policy routing rules with toggle

Proxmox Router 1 هفته پیش
والد
کامیت
ebeee4a79d
4فایلهای تغییر یافته به همراه68 افزوده شده و 14 حذف شده
  1. 37 3
      internal/routing/manager.go
  2. 2 0
      internal/routing/types.go
  3. 17 8
      internal/web/static/app.js
  4. 12 3
      internal/web/static/index.html

+ 37 - 3
internal/routing/manager.go

@@ -464,6 +464,11 @@ func (m *RoutingManager) ListPolicyRules() []PolicyRuleItem {
 	tableNames := m.getTableMap()
 	var res []PolicyRuleItem
 
+	userRulesByPrio := make(map[int]PolicyRuleItem)
+	for _, pr := range m.policyRules {
+		userRulesByPrio[pr.Priority] = pr
+	}
+
 	for _, r := range rules {
 		tableName := "main"
 		if name, ok := tableNames[r.Table]; ok {
@@ -487,6 +492,24 @@ func (m *RoutingManager) ListPolicyRules() []PolicyRuleItem {
 		}
 
 		id := fmt.Sprintf("rule-%d-%d", r.Priority, r.Table)
+		comment := ""
+		isDefault := false
+		isSystem := false
+
+		if r.Priority == 0 || r.Priority == 32766 || r.Priority == 32767 {
+			isDefault = true
+			isSystem = true
+		} else if userRule, ok := userRulesByPrio[r.Priority]; ok {
+			if userRule.ID != "" {
+				id = userRule.ID
+			}
+			comment = userRule.Comment
+			isDefault = false
+			isSystem = false
+		} else {
+			// System/daemon rule not added by user in proxmox-router (e.g. WireGuard, IPsec)
+			isSystem = true
+		}
 
 		res = append(res, PolicyRuleItem{
 			ID:          id,
@@ -497,6 +520,9 @@ func (m *RoutingManager) ListPolicyRules() []PolicyRuleItem {
 			Table:       tableName,
 			TableID:     r.Table,
 			Action:      "lookup",
+			Comment:     comment,
+			IsDefault:   isDefault,
+			IsSystem:    isSystem,
 		})
 	}
 
@@ -532,7 +558,7 @@ func (m *RoutingManager) DeletePolicyRule(id string) error {
 	var updated []PolicyRuleItem
 	var target *PolicyRuleItem
 	for _, pr := range m.policyRules {
-		if pr.ID == id {
+		if pr.ID == id || fmt.Sprintf("rule-%d-%d", pr.Priority, pr.TableID) == id {
 			target = &pr
 		} else {
 			updated = append(updated, pr)
@@ -541,6 +567,13 @@ func (m *RoutingManager) DeletePolicyRule(id string) error {
 
 	if target != nil {
 		_ = m.removeRuleFromKernel(*target)
+	} else if strings.HasPrefix(id, "rule-") {
+		parts := strings.Split(id, "-")
+		if len(parts) >= 2 {
+			if prio, err := strconv.Atoi(parts[1]); err == nil && prio > 0 {
+				_ = exec.Command("ip", "rule", "del", "pref", strconv.Itoa(prio)).Run()
+			}
+		}
 	}
 	m.policyRules = updated
 	return m.save()
@@ -578,10 +611,11 @@ func (m *RoutingManager) applyRuleToKernel(pr PolicyRuleItem) error {
 }
 
 func (m *RoutingManager) removeRuleFromKernel(pr PolicyRuleItem) error {
-	args := []string{"rule", "del"}
 	if pr.Priority > 0 {
-		args = append(args, "pref", strconv.Itoa(pr.Priority))
+		_ = exec.Command("ip", "rule", "del", "pref", strconv.Itoa(pr.Priority)).Run()
+		return nil
 	}
+	args := []string{"rule", "del"}
 	if pr.RoutingMark != "" {
 		markClean := pr.RoutingMark
 		if strings.Contains(markClean, "(") {

+ 2 - 0
internal/routing/types.go

@@ -35,6 +35,8 @@ type PolicyRuleItem struct {
 	TableID     int    `json:"table_id"`
 	Action      string `json:"action"`       // lookup, unreachable, blackhole, prohibit
 	Comment     string `json:"comment,omitempty"`
+	IsDefault   bool   `json:"is_default,omitempty"`
+	IsSystem    bool   `json:"is_system,omitempty"`
 }
 
 // RoutingTableItem represents a named routing table in Linux (/etc/iproute2/rt_tables)

+ 17 - 8
internal/web/static/app.js

@@ -5499,8 +5499,10 @@ async function loadRoutes() {
 
     const bRoutes = document.getElementById('routes-badge-routes');
     if (bRoutes) bRoutes.innerText = (state.routes || []).length;
+    const showSystem = document.getElementById('prule-show-system')?.checked;
+    const userRules = (state.policyRules || []).filter(pr => showSystem || (!pr.is_default && !pr.is_system));
     const bRules = document.getElementById('routes-badge-rules');
-    if (bRules) bRules.innerText = (state.policyRules || []).length;
+    if (bRules) bRules.innerText = userRules.length;
     const bTables = document.getElementById('routes-badge-tables');
     if (bTables) bTables.innerText = (state.routingTables || []).length;
     const bRoutesCount = document.getElementById('badge-routes-count');
@@ -5794,18 +5796,24 @@ function renderPolicyRules() {
   if (!tbody) return;
   tbody.innerHTML = '';
 
-  const rules = state.policyRules || [];
-  document.getElementById('rules-count-badge').innerText = `${rules.length} Rules`;
+  const showSystem = document.getElementById('prule-show-system')?.checked;
+  const filtered = (state.policyRules || []).filter(pr => showSystem || (!pr.is_default && !pr.is_system));
 
-  if (rules.length === 0) {
+  const countBadge = document.getElementById('rules-count-badge');
+  if (countBadge) countBadge.innerText = `${filtered.length} Rules`;
+  const bRules = document.getElementById('routes-badge-rules');
+  if (bRules) bRules.innerText = filtered.length;
+
+  if (filtered.length === 0) {
     empty?.classList.remove('hidden');
     return;
   }
   empty?.classList.add('hidden');
 
-  rules.forEach(pr => {
+  filtered.forEach(pr => {
+    const isSys = pr.is_default || pr.is_system;
     const tr = document.createElement('tr');
-    tr.className = 'hover:bg-slate-800/40 transition text-xs';
+    tr.className = `hover:bg-slate-800/40 transition text-xs ${isSys ? 'opacity-70 bg-slate-950/20' : ''}`;
     tr.innerHTML = `
       <td class="py-2.5 px-3 font-mono text-cyan-400 font-bold">${pr.priority}</td>
       <td class="py-2.5 px-3 font-mono text-white">${pr.src_address || '<span class="text-slate-600">all</span>'}</td>
@@ -5814,7 +5822,7 @@ function renderPolicyRules() {
       <td class="py-2.5 px-3 font-mono font-bold text-amber-300">${pr.table}</td>
       <td class="py-2.5 px-3 font-mono text-slate-300">${pr.action}</td>
       <td class="py-2.5 px-3 text-right font-sans">
-        ${pr.priority < 32766 && pr.priority > 0 ? `
+        ${!pr.is_default ? `
           <button onclick="deletePolicyRule('${pr.id}')" class="text-xs text-slate-500 hover:text-rose-400">Delete</button>
         ` : `<span class="text-slate-600 text-[10px]">Default</span>`}
       </td>
@@ -5824,7 +5832,8 @@ function renderPolicyRules() {
 }
 
 function openAddPolicyRuleModal() {
-  document.getElementById('prule-modal-pref').value = (state.policyRules || []).length * 10 + 1000;
+  const userRules = (state.policyRules || []).filter(pr => !pr.is_default && !pr.is_system);
+  document.getElementById('prule-modal-pref').value = userRules.length * 10 + 1000;
   document.getElementById('prule-modal-mark').value = '';
   document.getElementById('prule-modal-src').value = '';
   document.getElementById('prule-modal-dst').value = '';

+ 12 - 3
internal/web/static/index.html

@@ -1899,9 +1899,18 @@
 
           <!-- Sub-Tab 2: Policy Rules (/ip route rule) -->
           <div id="routes-subtab-rules" class="hidden bg-slate-900/90 border border-slate-800 rounded-xl overflow-hidden">
-            <div class="p-4 border-b border-slate-800 flex items-center justify-between">
-              <h3 class="text-sm font-bold text-white">Policy Routing Rules (ip rule)</h3>
-              <span id="rules-count-badge" class="text-xs font-mono text-slate-400">0 Rules</span>
+            <div class="p-4 border-b border-slate-800 flex items-center justify-between flex-wrap gap-3">
+              <div class="flex items-center gap-3">
+                <h3 class="text-sm font-bold text-white">Policy Routing Rules (ip rule)</h3>
+                <span id="rules-count-badge" class="text-xs font-mono text-slate-400">0 Rules</span>
+              </div>
+              <div class="flex items-center gap-2">
+                <label class="flex items-center gap-1.5 text-xs text-slate-400 hover:text-white cursor-pointer select-none bg-slate-800/80 hover:bg-slate-800 border border-slate-700/80 px-2.5 py-1 rounded-lg transition">
+                  <input type="checkbox" id="prule-show-system" onchange="renderPolicyRules()" class="rounded bg-slate-900 border-slate-700 text-cyan-500 focus:ring-0 w-3.5 h-3.5">
+                  <span>Show System / Default Rules</span>
+                </label>
+                <button onclick="loadRoutes()" class="text-xs text-cyan-400 hover:text-cyan-300 font-semibold px-2 py-1 bg-slate-800 rounded border border-slate-700">Refresh</button>
+              </div>
             </div>
 
             <div class="overflow-x-auto">