Selaa lähdekoodia

fix(metadata): eliminate mutex deadlock on save and optimize role rendering

Proxmox Router 1 viikko sitten
vanhempi
commit
5a2c382677
2 muutettua tiedostoa jossa 19 lisäystä ja 9 poistoa
  1. 5 5
      internal/network/metadata.go
  2. 14 4
      internal/web/static/app.js

+ 5 - 5
internal/network/metadata.go

@@ -240,7 +240,7 @@ func (mm *MetadataManager) Set(name string, role, desc string) error {
 	meta.Role = role
 	meta.Description = desc
 	mm.store.Interfaces[name] = meta
-	return mm.save()
+	return mm.saveLocked()
 }
 
 func (mm *MetadataManager) AddIP(name, cidr string) error {
@@ -256,7 +256,7 @@ func (mm *MetadataManager) AddIP(name, cidr string) error {
 	meta.IPAddresses = append(meta.IPAddresses, cidr)
 	meta.AutoUp = true
 	mm.store.Interfaces[name] = meta
-	return mm.save()
+	return mm.saveLocked()
 }
 
 func (mm *MetadataManager) RemoveIP(name, cidr string) error {
@@ -272,7 +272,7 @@ func (mm *MetadataManager) RemoveIP(name, cidr string) error {
 	}
 	meta.IPAddresses = updated
 	mm.store.Interfaces[name] = meta
-	return mm.save()
+	return mm.saveLocked()
 }
 
 func (mm *MetadataManager) GetAllWithIPs() map[string][]string {
@@ -293,7 +293,7 @@ func (mm *MetadataManager) Delete(name string) error {
 	defer mm.mu.Unlock()
 
 	delete(mm.store.Interfaces, name)
-	return mm.save()
+	return mm.saveLocked()
 }
 
 // Rename migrates interface metadata when an interface is renamed.
@@ -304,7 +304,7 @@ func (mm *MetadataManager) Rename(oldName, newName string) error {
 	if meta, exists := mm.store.Interfaces[oldName]; exists {
 		delete(mm.store.Interfaces, oldName)
 		mm.store.Interfaces[newName] = meta
-		return mm.save()
+		return mm.saveLocked()
 	}
 	return nil
 }

+ 14 - 4
internal/web/static/app.js

@@ -880,6 +880,10 @@ function renderRolesTable() {
   }
   empty?.classList.add('hidden');
 
+  const sig = roles.map(r => `${r.id}:${r.name}:${r.color}:${r.description}:${(r.interfaces||[]).join(',')}`).join(';');
+  if (tbody.dataset.sig === sig) return;
+  tbody.dataset.sig = sig;
+
   tbody.innerHTML = roles.map(r => {
     const colorKey = (r.color || 'blue').toLowerCase();
     const theme = ROLE_COLORS[colorKey] || ROLE_COLORS.blue;
@@ -1037,13 +1041,19 @@ function populateRoleSelects(selectedRole = '') {
 
   if (ifaceSelect) {
     const currentVal = selectedRole || ifaceSelect.value || 'LAN';
-    ifaceSelect.innerHTML = optionsHtml;
+    if (ifaceSelect.dataset.optionsSig !== optionsHtml) {
+      ifaceSelect.innerHTML = optionsHtml;
+      ifaceSelect.dataset.optionsSig = optionsHtml;
+    }
     if (currentVal) ifaceSelect.value = currentVal;
   }
 
   if (vlanSelect) {
-    const currentVal = vlanSelect.value || 'LAN';
-    vlanSelect.innerHTML = optionsHtml;
+    const currentVal = selectedRole || vlanSelect.value || 'LAN';
+    if (vlanSelect.dataset.optionsSig !== optionsHtml) {
+      vlanSelect.innerHTML = optionsHtml;
+      vlanSelect.dataset.optionsSig = optionsHtml;
+    }
     if (currentVal) vlanSelect.value = currentVal;
   }
 }
@@ -1101,7 +1111,7 @@ function renderInterfacesList() {
 
   const sig = `${state.ifaceFilter}|${query}|` + filteredParents.map(p => {
     const cVlans = allVlans.filter(v => v.parent_name === p.name || v.name.startsWith(p.name + '.'));
-    return `${p.name}:${p.state}:${(p.ipv4||[]).join(',')}:${p.mtu}:${cVlans.length}:${state.expandedParents.has(p.name)}`;
+    return `${p.name}:${p.state}:${p.role||'LAN'}:${(p.ipv4||[]).join(',')}:${p.mtu}:${cVlans.length}:${state.expandedParents.has(p.name)}`;
   }).join(';');
 
   if (tbody.dataset.sig === sig) {