mirror of
https://github.com/Kpa-clawbot/meshcore-analyzer.git
synced 2026-08-28 00:45:07 +00:00
Fix: per-observer hop resolution for packet list
Ambiguous hops in the list now get resolved per-observer via batch server API calls. Cache uses observer-scoped keys (hop:observerId) so the same 1-byte prefix shows different names depending on which observer saw the packet. Flow: global resolve first (fast, covers unambiguous hops), then batch per-observer resolve for ambiguous ones only.
This commit is contained in:
+1
-1
@@ -86,7 +86,7 @@
|
||||
<script src="hop-display.js?v=1774221932"></script>
|
||||
<script src="app.js?v=1774126708"></script>
|
||||
<script src="home.js?v=1774042199"></script>
|
||||
<script src="packets.js?v=1774224327"></script>
|
||||
<script src="packets.js?v=1774224521"></script>
|
||||
<script src="map.js?v=1774220756" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="channels.js?v=1774331200" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
<script src="nodes.js?v=1774221131" onerror="console.error('Failed to load:', this.src)"></script>
|
||||
|
||||
+39
-9
@@ -129,13 +129,16 @@
|
||||
}
|
||||
}
|
||||
|
||||
function renderHop(h) {
|
||||
return HopDisplay.renderHop(h, hopNameCache[h], { hexMode: showHexHashes });
|
||||
function renderHop(h, observerId) {
|
||||
// Use per-packet cache key if observer context available (ambiguous hops differ by region)
|
||||
const cacheKey = observerId ? h + ':' + observerId : h;
|
||||
const entry = hopNameCache[cacheKey] || hopNameCache[h];
|
||||
return HopDisplay.renderHop(h, entry, { hexMode: showHexHashes });
|
||||
}
|
||||
|
||||
function renderPath(hops) {
|
||||
function renderPath(hops, observerId) {
|
||||
if (!hops || !hops.length) return '—';
|
||||
return hops.map(renderHop).join('<span class="arrow">→</span>');
|
||||
return hops.map(h => renderHop(h, observerId)).join('<span class="arrow">→</span>');
|
||||
}
|
||||
|
||||
let directPacketId = null;
|
||||
@@ -418,6 +421,32 @@
|
||||
}
|
||||
if (allHops.size) await resolveHops([...allHops]);
|
||||
|
||||
// Per-observer batch resolve for ambiguous hops (context-aware disambiguation)
|
||||
const hopsByObserver = {};
|
||||
for (const p of packets) {
|
||||
if (!p.observer_id) continue;
|
||||
try {
|
||||
const path = JSON.parse(p.path_json || '[]');
|
||||
const ambiguous = path.filter(h => hopNameCache[h]?.ambiguous);
|
||||
if (ambiguous.length) {
|
||||
if (!hopsByObserver[p.observer_id]) hopsByObserver[p.observer_id] = new Set();
|
||||
ambiguous.forEach(h => hopsByObserver[p.observer_id].add(h));
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
// Batch resolve — one API call per observer (typically 4-5 observers)
|
||||
await Promise.all(Object.entries(hopsByObserver).map(async ([obsId, hopsSet]) => {
|
||||
try {
|
||||
const params = new URLSearchParams({ hops: [...hopsSet].join(','), observer: obsId });
|
||||
const result = await api(`/resolve-hops?${params}`);
|
||||
if (result?.resolved) {
|
||||
for (const [k, v] of Object.entries(result.resolved)) {
|
||||
hopNameCache[k + ':' + obsId] = v;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
}));
|
||||
|
||||
// Restore expanded group children
|
||||
if (groupByHash && expandedHashes.size > 0) {
|
||||
for (const hash of expandedHashes) {
|
||||
@@ -927,7 +956,7 @@
|
||||
const groupRegion = headerObserverId ? (observers.find(o => o.id === headerObserverId)?.iata || '') : '';
|
||||
let groupPath = [];
|
||||
try { groupPath = JSON.parse(headerPathJson || '[]'); } catch {}
|
||||
const groupPathStr = renderPath(groupPath);
|
||||
const groupPathStr = renderPath(groupPath, headerObserverId);
|
||||
const groupTypeName = payloadTypeName(p.payload_type);
|
||||
const groupTypeClass = payloadTypeColor(p.payload_type);
|
||||
const groupSize = p.raw_hex ? Math.floor(p.raw_hex.length / 2) : 0;
|
||||
@@ -959,7 +988,7 @@
|
||||
const childRegion = c.observer_id ? (observers.find(o => o.id === c.observer_id)?.iata || '') : '';
|
||||
let childPath = [];
|
||||
try { childPath = JSON.parse(c.path_json || '[]'); } catch {}
|
||||
const childPathStr = renderPath(childPath);
|
||||
const childPathStr = renderPath(childPath, child.observer_id);
|
||||
html += `<tr class="group-child" data-id="${c.id}" data-hash="${c.hash || ''}" data-action="select-observation" data-value="${c.id}" data-parent-hash="${p.hash}" tabindex="0" role="row">
|
||||
<td></td><td class="col-region">${childRegion ? `<span class="badge-region">${childRegion}</span>` : '—'}</td>
|
||||
<td class="col-time">${timeAgo(c.timestamp)}</td>
|
||||
@@ -987,8 +1016,7 @@
|
||||
const typeName = payloadTypeName(p.payload_type);
|
||||
const typeClass = payloadTypeColor(p.payload_type);
|
||||
const size = p.raw_hex ? Math.floor(p.raw_hex.length / 2) : 0;
|
||||
const pathStr = renderPath(pathHops);
|
||||
const detail = getDetailPreview(decoded);
|
||||
const pathStr = renderPath(pathHops, p.observer_id); const detail = getDetailPreview(decoded);
|
||||
|
||||
return `<tr data-id="${p.id}" data-hash="${p.hash || ''}" data-action="select-hash" data-value="${p.hash || p.id}" tabindex="0" role="row" class="${selectedId === p.id ? 'selected' : ''}">
|
||||
<td></td><td class="col-region">${region ? `<span class="badge-region">${region}</span>` : '—'}</td>
|
||||
@@ -1131,6 +1159,8 @@
|
||||
if (serverResolved?.resolved) {
|
||||
for (const [k, v] of Object.entries(serverResolved.resolved)) {
|
||||
hopNameCache[k] = v;
|
||||
// Also store observer-scoped key for list view
|
||||
if (pkt.observer_id) hopNameCache[k + ':' + pkt.observer_id] = v;
|
||||
}
|
||||
}
|
||||
} catch {}
|
||||
@@ -1232,7 +1262,7 @@
|
||||
${hashSize ? `<dt>Hash Size</dt><dd>${hashSize} byte${hashSize !== 1 ? 's' : ''}</dd>` : ''}
|
||||
<dt>Timestamp</dt><dd>${pkt.timestamp}</dd>
|
||||
<dt>Propagation</dt><dd>${propagationHtml}</dd>
|
||||
<dt>Path</dt><dd>${pathHops.length ? renderPath(pathHops) : '—'}</dd>
|
||||
<dt>Path</dt><dd>${pathHops.length ? renderPath(pathHops, pkt.observer_id) : '—'}</dd>
|
||||
</dl>
|
||||
<div class="detail-actions">
|
||||
<button class="copy-link-btn" data-packet-hash="${pkt.hash || ''}" data-packet-id="${pkt.id}" title="Copy link to this packet">🔗 Copy Link</button>
|
||||
|
||||
Reference in New Issue
Block a user