Add JSON export actions for topology and diff.

Provide UI buttons to export latest topology and diff payloads into downloadable JSON files.

Made-with: Cursor
This commit is contained in:
Andrey Lutsenko
2026-04-09 22:15:44 +10:00
parent 22c07c7fb1
commit 396d139482
3 changed files with 38 additions and 0 deletions
+36
View File
@@ -21,6 +21,7 @@
<select id="scanSelect" style="min-width: 360px;"></select>
<button id="loadBtn">Загрузить</button>
<button id="latestBtn">Последний scan</button>
<button id="exportTopologyBtn">Экспорт topology JSON</button>
<button id="fitBtn">По центру</button>
</div>
<div class="row">
@@ -29,6 +30,7 @@
<select id="fromScanSelect" style="min-width: 240px;"></select>
<select id="toScanSelect" style="min-width: 240px;"></select>
<button id="diffBtn">Сравнить</button>
<button id="exportDiffBtn">Экспорт diff JSON</button>
</div>
<div id="status">Ожидание данных...</div>
<div style="margin: 6px 0 10px 0; font-size: 13px;">
@@ -56,6 +58,8 @@
const loadBtn = document.getElementById("loadBtn");
const latestBtn = document.getElementById("latestBtn");
const diffBtn = document.getElementById("diffBtn");
const exportTopologyBtn = document.getElementById("exportTopologyBtn");
const exportDiffBtn = document.getElementById("exportDiffBtn");
const fitBtn = document.getElementById("fitBtn");
const nodeFilter = document.getElementById("nodeFilter");
const statusEl = document.getElementById("status");
@@ -64,9 +68,21 @@
const NS = "http://www.w3.org/2000/svg";
let lastTopology = null;
let lastHighlight = null;
let lastDiff = null;
function setStatus(text) { statusEl.textContent = text; }
function setDiff(text) { diffBox.textContent = text; }
function downloadJSON(filename, data) {
const blob = new Blob([JSON.stringify(data, null, 2)], { type: "application/json" });
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
a.remove();
URL.revokeObjectURL(url);
}
function clearSVG() {
while (svg.firstChild) svg.removeChild(svg.firstChild);
@@ -240,6 +256,7 @@
return;
}
const d = await res.json();
lastDiff = d;
setDiff(JSON.stringify(d, null, 2));
const topoRes = await fetch(`/api/scans/${encodeURIComponent(toId)}/topology?limit=5000`);
if (topoRes.ok) {
@@ -272,6 +289,25 @@
});
latestBtn.addEventListener("click", loadLatestScan);
diffBtn.addEventListener("click", loadDiff);
exportTopologyBtn.addEventListener("click", () => {
const scanId = scanIdInput.value.trim() || "unknown";
if (!lastTopology) {
setStatus("Сначала загрузите topology");
return;
}
downloadJSON(`topology_${scanId}.json`, lastTopology);
setStatus("Topology JSON экспортирован");
});
exportDiffBtn.addEventListener("click", () => {
const fromId = fromScanIdInput.value.trim() || "from";
const toId = toScanIdInput.value.trim() || "to";
if (!lastDiff) {
setStatus("Сначала выполните diff");
return;
}
downloadJSON(`diff_${fromId}_to_${toId}.json`, lastDiff);
setStatus("Diff JSON экспортирован");
});
fitBtn.addEventListener("click", () => {
svg.setAttribute("viewBox", "0 0 1200 620");
});