From ec130594375c2f3aae411905dd120b2e2f742aec Mon Sep 17 00:00:00 2001 From: kodi Date: Tue, 24 Feb 2026 12:37:17 +0100 Subject: [PATCH] feat (ui): netwerk map functionaliteit verder uitgebreid en polish --- webui/conf/httpd.conf | 10 +++- webui/html/assets/js/tabs/images.js | 10 +++- webui/html/assets/js/tabs/networks.js | 80 ++++++++++++++++++++++++--- webui/html/index.html | 13 +++-- 4 files changed, 100 insertions(+), 13 deletions(-) diff --git a/webui/conf/httpd.conf b/webui/conf/httpd.conf index 920ec14..7765d20 100644 --- a/webui/conf/httpd.conf +++ b/webui/conf/httpd.conf @@ -22,6 +22,14 @@ DirectoryIndex index.html ErrorLog /proc/self/fd/2 CustomLog /proc/self/fd/1 combined +#ProxyPreserveHost On +#ProxyPass "/api/" "http://127.0.0.1:8000/api/" +#ProxyPassReverse "/api/" "http://127.0.0.1:8000/api/" + +# allow long-running upstream requests (image builds) +Timeout 600 +ProxyTimeout 600 + ProxyPreserveHost On -ProxyPass "/api/" "http://127.0.0.1:8000/api/" +ProxyPass "/api/" "http://127.0.0.1:8000/api/" connectiontimeout=5 timeout=600 retry=0 ProxyPassReverse "/api/" "http://127.0.0.1:8000/api/" diff --git a/webui/html/assets/js/tabs/images.js b/webui/html/assets/js/tabs/images.js index 7449057..0285e88 100644 --- a/webui/html/assets/js/tabs/images.js +++ b/webui/html/assets/js/tabs/images.js @@ -165,7 +165,15 @@ async function buildImage() { }) }); - const data = await res.json(); + const ct = (res.headers.get("content-type") || "").toLowerCase(); + let data; + + if (ct.includes("application/json")) { + data = await res.json(); + } else { + const text = await res.text(); + data = { ok: res.ok, status: res.status, non_json: true, body: text.slice(0, 4000) }; + } if (!res.ok) { outputBox.value += "\nERROR:\n" + JSON.stringify(data, null, 2); diff --git a/webui/html/assets/js/tabs/networks.js b/webui/html/assets/js/tabs/networks.js index 5221070..d750e28 100644 --- a/webui/html/assets/js/tabs/networks.js +++ b/webui/html/assets/js/tabs/networks.js @@ -48,6 +48,7 @@ connectedOnly: false, hideDefaults: true, sharedOnly: false, + showModes: true, sort: 'name_asc', }, }; @@ -126,6 +127,18 @@ return { totalNetworks, usedNetworks, unusedNetworks, connectedContainers, sharedNetns }; } + function buildMapStatus(label, nodesCount, linksCount) { + const f = state.filters || {}; + const flags = []; + if (f.connectedOnly) flags.push('connected'); + if (f.hideDefaults) flags.push('hide-defaults'); + if (f.sharedOnly) flags.push('shared'); + if (f.showModes === false) flags.push('modes-off'); + + const flagTxt = flags.length ? ` | ${flags.join(', ')}` : ''; + return `${label} | ${nodesCount} nodes, ${linksCount} links${flagTxt}`; + } + function renderNetworksSummary() { const host = document.getElementById('networksSummary'); if (!host) return; @@ -345,6 +358,7 @@ const connectedOnly = !!state.filters.connectedOnly; const hideDefaults = !!state.filters.hideDefaults; const sharedOnly = !!state.filters.sharedOnly; + const showModes = state.filters.showModes !== false; const byMeta = state.usage?.byContainerMeta || {}; @@ -353,6 +367,9 @@ if (hideDefaults) { out = out.filter(n => !isDefaultNetworkName(n.name)); } + if (!showModes) { + out = out.filter(n => String(n.meta?.driver || '') !== 'mode'); + } if (connectedOnly) { out = out.filter(n => n.containerCount > 0); @@ -696,8 +713,8 @@ // simulation const sim = d3.forceSimulation(model.nodes) - .force('link', d3.forceLink(model.links).id(d => d.id).distance(45)) - .force('charge', d3.forceManyBody().strength(-40)) + .force('link', d3.forceLink(model.links).id(d => d.id).distance(80)) + .force('charge', d3.forceManyBody().strength(-30)) .force('center', d3.forceCenter(w / 2, h / 2).strength(0.15)) .force('collide', d3.forceCollide().radius(d => d.type === 'network' ? 18 : 16)) .on('tick', () => { @@ -731,7 +748,7 @@ showDetailPanel(networkName); const s = document.getElementById('networksMapStatus'); - if (s) s.textContent = `Detail: ${networkName}`; + if (s) s.textContent = buildMapStatus(`Detail: ${networkName}`, model.nodes.length, model.links.length); } function showDetailPanel(networkName) { @@ -792,7 +809,7 @@ hideDetailPanel(); const s = document.getElementById('networksMapStatus'); - if (s) s.textContent = `Kaart: ${model.meta.nodes} nodes, ${model.meta.links} links`; + if (s) s.textContent = buildMapStatus('Global', model.nodes.length, model.links.length); } function renderNetworks() { @@ -927,6 +944,10 @@ resetBtn.dataset.bound = '1'; resetBtn.addEventListener('click', () => { if (!graphCtx) return; + + // wis highlight/dim + graphCtx.g.selectAll('.graphNode').classed('graphDim', false); + graphCtx.g.selectAll('.graphLink').classed('graphDim', false).classed('graphActive', false); // reset zoom graphCtx.svg @@ -990,7 +1011,13 @@ }); const s = document.getElementById('networksMapStatus'); - if (s) s.textContent = `Container: ${id.slice(0, 12)}…`; + if (s) { + const label = (state.mapMode === 'detail' && state.selectedNetwork) + ? `Detail: ${state.selectedNetwork}` + : 'Global'; + + s.textContent = buildMapStatus(label, graphCtx.model.nodes.length, graphCtx.model.links.length) + ` | container: ${id.slice(0, 12)}…`; + } }); } @@ -999,13 +1026,24 @@ const fHideDefaults = document.getElementById('networksFilterHideDefaults'); const fShared = document.getElementById('networksFilterShared'); const sort = document.getElementById('networksSort'); + const mapShowModes = document.getElementById('networksMapShowModes'); + const mapConnectedOnly = document.getElementById('networksMapConnectedOnly'); function rerender() { renderNetworks(); + if (state.view === 'map') { + + // als we in detail zitten → detail opnieuw renderen + if (state.mapMode === 'detail' && state.selectedNetwork) { + openNetworkDetail(state.selectedNetwork); + return; + } + + // anders global map const model = buildGlobalGraphModel(); const s = document.getElementById('networksMapStatus'); - if (s) s.textContent = `Kaart: ${model.meta.nodes} nodes, ${model.meta.links} links`; + if (s) s.textContent = buildMapStatus('Global', model.nodes.length, model.links.length); renderGraph(model); } } @@ -1051,8 +1089,36 @@ }); } + if (mapConnectedOnly && !mapConnectedOnly.dataset.bound) { + mapConnectedOnly.dataset.bound = '1'; + mapConnectedOnly.checked = !!state.filters.connectedOnly; + + mapConnectedOnly.addEventListener('change', () => { + state.filters.connectedOnly = !!mapConnectedOnly.checked; + + // sync ook de tabel checkbox (zodat alles hetzelfde blijft) + const tableChk = document.getElementById('networksFilterConnected'); + if (tableChk) tableChk.checked = !!state.filters.connectedOnly; + + rerender(); + }); + } + + if (mapShowModes && !mapShowModes.dataset.bound) { + mapShowModes.dataset.bound = '1'; + mapShowModes.checked = state.filters.showModes !== false; + mapShowModes.addEventListener('change', () => { + state.filters.showModes = !!mapShowModes.checked; + rerender(); + }); + } + renderNetworksSummary(); setNetworksView(state.view); + // sync kaart-checkbox bij init + if (mapConnectedOnly) { + mapConnectedOnly.checked = !!state.filters.connectedOnly; + } } // Expose minimal API @@ -1066,7 +1132,7 @@ console.table(model.nodes.slice(0, 12)); console.table(model.links.slice(0, 12)); return model; - }, + }, }; // Bind when script loads (DOM is already mostly there because script is at end of body) diff --git a/webui/html/index.html b/webui/html/index.html index 3db2063..c47c7b3 100644 --- a/webui/html/index.html +++ b/webui/html/index.html @@ -162,14 +162,19 @@ - Kaartweergave (placeholder) + + + Kaartweergave (placeholder)
-
- Kaartweergave is actief. (STAP 3A-1: alleen layout/controls. D3 rendering komt in 3C.) -