Prechádzať zdrojové kódy

fix(routing): define escapeHtml, add defensive guards to routing renderers, and finalize BGP/OSPF split

Proxmox Router 1 týždeň pred
rodič
commit
929b0deae5
2 zmenil súbory, kde vykonal 48 pridanie a 25 odobranie
  1. 47 24
      internal/web/static/app.js
  2. 1 1
      internal/web/static/index.html

+ 47 - 24
internal/web/static/app.js

@@ -44,6 +44,16 @@ let state = {
 
 const API_BASE = '/api';
 
+function escapeHtml(str) {
+  if (str === null || str === undefined) return '';
+  return String(str)
+    .replace(/&/g, '&')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#039;');
+}
+
 // Initialize on page load
 document.addEventListener('DOMContentLoaded', () => {
   const hash = window.location.hash.replace('#', '') || 'dashboard';
@@ -3384,7 +3394,7 @@ function renderGeoCountries() {
       </td>
       <td class="py-2.5 px-3 text-right space-x-2 font-sans whitespace-nowrap">
         <button onclick="updateGeoCountry('${c.code}')" class="text-xs text-cyan-400 hover:text-cyan-300 font-medium" title="Re-download latest country IP blocks">Sync</button>
-        <button onclick="viewGeoSubnets('${c.code}', '${c.name.replace(/'/g, "\\'")}')" class="text-xs text-slate-400 hover:text-white" title="Preview Subnet Ranges">View</button>
+        <button onclick="viewGeoSubnets('${c.code}')" class="text-xs text-slate-400 hover:text-white" title="Preview Subnet Ranges">View</button>
       </td>
     `;
     tbody.appendChild(tr);
@@ -3556,6 +3566,10 @@ async function submitGeoIntervalSettings(e) {
 }
 
 async function viewGeoSubnets(code, name) {
+  if (!name) {
+    const found = (state.geoCountries || []).find(x => x.code === code);
+    name = found ? found.name : code;
+  }
   document.getElementById('geo-subnets-title').innerText = `${name} (${code}) Subnets`;
   document.getElementById('geo-subnets-subtitle').innerText = `Kernel IPSet: mk_geo_${code}`;
   const listEl = document.getElementById('geo-subnets-list');
@@ -5112,7 +5126,7 @@ function switchDynSubTab(subTab) {
   }
 }
 
-async function loadDynamicRouting(showToastMsg = true) {
+async function loadDynamicRouting(showToastMsg = false) {
   try {
     const [sumRes, bgpInstRes, bgpPeersRes, bgpNetsRes, bgpRoutesRes, ospfInstRes, ospfIfacesRes, ospfAreasRes, ospfNbrsRes, ospfRoutesRes] = await Promise.all([
       fetch(`${API_BASE}/routing/dynamic/summary`),
@@ -5138,16 +5152,18 @@ async function loadDynamicRouting(showToastMsg = true) {
     if (ospfNbrsRes.ok) state.ospfNeighbors = await ospfNbrsRes.json();
     if (ospfRoutesRes.ok) state.ospfRoutes = await ospfRoutesRes.json();
 
-    renderBgpStatusPill();
-    renderBgpPeers();
-    renderBgpNetworks();
-    renderBgpRoutes();
-    renderOspfStatusPill();
-    renderOspfInterfaces();
-    renderOspfAreas();
-    renderOspfNeighbors();
-    renderOspfRoutes();
-    updateBadges();
+    try { renderBgpStatusPill(); } catch(e) { console.error('renderBgpStatusPill error:', e); }
+    try { renderBgpPeers(); } catch(e) { console.error('renderBgpPeers error:', e); }
+    try { renderBgpNetworks(); } catch(e) { console.error('renderBgpNetworks error:', e); }
+    try { renderBgpRoutes(); } catch(e) { console.error('renderBgpRoutes error:', e); }
+    try { renderOspfStatusPill(); } catch(e) { console.error('renderOspfStatusPill error:', e); }
+    try { renderOspfInterfaces(); } catch(e) { console.error('renderOspfInterfaces error:', e); }
+    try { renderOspfAreas(); } catch(e) { console.error('renderOspfAreas error:', e); }
+    try { renderOspfNeighbors(); } catch(e) { console.error('renderOspfNeighbors error:', e); }
+    try { renderOspfRoutes(); } catch(e) { console.error('renderOspfRoutes error:', e); }
+    try { updateBadges(); } catch(e) { console.error('updateBadges error:', e); }
+
+    if (showToastMsg) showToast('Refreshed', 'Dynamic routing telemetry updated', 'info');
   } catch (err) {
     console.error('Failed to load dynamic routing:', err);
     if (showToastMsg) showToast('Error', 'Failed to load dynamic routing telemetry', 'error');
@@ -5172,6 +5188,7 @@ function renderBgpStatusPill() {
 
 function renderBgpPeers() {
   const tbody = document.getElementById('bgp-peers-table-body');
+  if (!tbody) return;
   const empty = document.getElementById('bgp-peers-empty');
   const countBadge = document.getElementById('bgp-peers-count-badge');
   const navBadge = document.getElementById('dyn-badge-bgp-peers');
@@ -5182,10 +5199,10 @@ function renderBgpPeers() {
 
   if (peers.length === 0) {
     tbody.innerHTML = '';
-    empty.classList.remove('hidden');
+    if (empty) empty.classList.remove('hidden');
     return;
   }
-  empty.classList.add('hidden');
+  if (empty) empty.classList.add('hidden');
 
   tbody.innerHTML = peers.map(p => {
     let flagA = !p.disabled ? '<span class="text-cyan-400 font-bold" title="Active">A</span>' : '';
@@ -5234,6 +5251,7 @@ function renderBgpPeers() {
 
 function renderBgpNetworks() {
   const tbody = document.getElementById('bgp-nets-table-body');
+  if (!tbody) return;
   const empty = document.getElementById('bgp-nets-empty');
   const countBadge = document.getElementById('bgp-nets-count-badge');
   const navBadge = document.getElementById('dyn-badge-bgp-nets');
@@ -5244,10 +5262,10 @@ function renderBgpNetworks() {
 
   if (nets.length === 0) {
     tbody.innerHTML = '';
-    empty.classList.remove('hidden');
+    if (empty) empty.classList.remove('hidden');
     return;
   }
-  empty.classList.add('hidden');
+  if (empty) empty.classList.add('hidden');
 
   tbody.innerHTML = nets.map(n => `
     <tr class="hover:bg-slate-800/40 transition">
@@ -5269,6 +5287,7 @@ function renderBgpNetworks() {
 
 function renderBgpRoutes() {
   const tbody = document.getElementById('bgp-routes-table-body');
+  if (!tbody) return;
   const empty = document.getElementById('bgp-routes-empty');
   const countBadge = document.getElementById('bgp-routes-count-badge');
   const navBadge = document.getElementById('dyn-badge-bgp-routes');
@@ -5279,10 +5298,10 @@ function renderBgpRoutes() {
 
   if (routes.length === 0) {
     tbody.innerHTML = '';
-    empty.classList.remove('hidden');
+    if (empty) empty.classList.remove('hidden');
     return;
   }
-  empty.classList.add('hidden');
+  if (empty) empty.classList.add('hidden');
 
   tbody.innerHTML = routes.map(r => `
     <tr class="hover:bg-slate-800/40 transition">
@@ -5314,6 +5333,7 @@ function renderOspfStatusPill() {
 
 function renderOspfInterfaces() {
   const tbody = document.getElementById('ospf-ifaces-table-body');
+  if (!tbody) return;
   const empty = document.getElementById('ospf-ifaces-empty');
   const countBadge = document.getElementById('ospf-ifaces-count-badge');
   const navBadge = document.getElementById('dyn-badge-ospf-ifaces');
@@ -5324,10 +5344,10 @@ function renderOspfInterfaces() {
 
   if (ifaces.length === 0) {
     tbody.innerHTML = '';
-    empty.classList.remove('hidden');
+    if (empty) empty.classList.remove('hidden');
     return;
   }
-  empty.classList.add('hidden');
+  if (empty) empty.classList.add('hidden');
 
   tbody.innerHTML = ifaces.map(i => {
     let flagA = !i.disabled ? '<span class="text-cyan-400 font-bold" title="Active">A</span>' : '';
@@ -5363,6 +5383,7 @@ function renderOspfInterfaces() {
 
 function renderOspfAreas() {
   const tbody = document.getElementById('ospf-areas-table-body');
+  if (!tbody) return;
   const countBadge = document.getElementById('ospf-areas-count-badge');
   const navBadge = document.getElementById('dyn-badge-ospf-areas');
 
@@ -5387,6 +5408,7 @@ function renderOspfAreas() {
 
 function renderOspfNeighbors() {
   const tbody = document.getElementById('ospf-nbrs-table-body');
+  if (!tbody) return;
   const empty = document.getElementById('ospf-nbrs-empty');
   const countBadge = document.getElementById('ospf-nbrs-count-badge');
   const navBadge = document.getElementById('dyn-badge-ospf-nbrs');
@@ -5397,10 +5419,10 @@ function renderOspfNeighbors() {
 
   if (nbrs.length === 0) {
     tbody.innerHTML = '';
-    empty.classList.remove('hidden');
+    if (empty) empty.classList.remove('hidden');
     return;
   }
-  empty.classList.add('hidden');
+  if (empty) empty.classList.add('hidden');
 
   tbody.innerHTML = nbrs.map(n => {
     let stateBadge = '';
@@ -5425,6 +5447,7 @@ function renderOspfNeighbors() {
 
 function renderOspfRoutes() {
   const tbody = document.getElementById('ospf-routes-table-body');
+  if (!tbody) return;
   const empty = document.getElementById('ospf-routes-empty');
   const countBadge = document.getElementById('ospf-routes-count-badge');
   const navBadge = document.getElementById('dyn-badge-ospf-routes');
@@ -5435,10 +5458,10 @@ function renderOspfRoutes() {
 
   if (routes.length === 0) {
     tbody.innerHTML = '';
-    empty.classList.remove('hidden');
+    if (empty) empty.classList.remove('hidden');
     return;
   }
-  empty.classList.add('hidden');
+  if (empty) empty.classList.add('hidden');
 
   tbody.innerHTML = routes.map(r => `
     <tr class="hover:bg-slate-800/40 transition">

+ 1 - 1
internal/web/static/index.html

@@ -4368,6 +4368,6 @@
     </div>
   </div>
 
-  <script src="app.js?v=3.2"></script>
+  <script src="app.js?v=3.3"></script>
 </body>
 </html>