From f694022ba3d0b0ac8629124614e9727e64d814d4 Mon Sep 17 00:00:00 2001 From: you Date: Wed, 18 Mar 2026 22:43:00 +0000 Subject: [PATCH] =?UTF-8?q?Fix=20View=20Route=20on=20Map=20=E2=80=94=20use?= =?UTF-8?q?=20sessionStorage=20instead=20of=20URL=20params?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Hop hashes are 1-2 byte truncated values that don't work in URL params. Now passes raw hops via sessionStorage; map page reads them after nodes load and resolves via prefix match against full public keys. --- public/map.js | 41 ++++++++++++++++++++++++++--------------- public/packets.js | 24 ++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 17 deletions(-) diff --git a/public/map.js b/public/map.js index 445c408c..edb11295 100644 --- a/public/map.js +++ b/public/map.js @@ -88,45 +88,56 @@ onWS(wsHandler); loadNodes().then(() => { - // Check for highlight route param (from packet detail) - const hashQuery = location.hash.split('?')[1]; - if (hashQuery) { - const params = new URLSearchParams(hashQuery); - const highlight = params.get('highlight'); - if (highlight) drawPacketRoute(highlight.split(',')); + // Check for route from packet detail (via sessionStorage) + const routeHopsJson = sessionStorage.getItem('map-route-hops'); + if (routeHopsJson) { + sessionStorage.removeItem('map-route-hops'); + try { + const hopKeys = JSON.parse(routeHopsJson); + drawPacketRoute(hopKeys); + } catch {} } }); } function drawPacketRoute(hopKeys) { - // Resolve hop keys to positions + // Resolve hop short hashes to node positions via prefix match const positions = []; for (const hop of hopKeys) { + const hopLower = hop.toLowerCase(); const node = nodes.find(n => - n.public_key.toLowerCase().startsWith(hop.toLowerCase()) + n.public_key.toLowerCase().startsWith(hopLower) ); if (node && node.lat != null && node.lon != null && !(node.lat === 0 && node.lon === 0)) { positions.push({ lat: node.lat, lon: node.lon, name: node.name || hop }); } } - if (positions.length < 2) return; + if (positions.length < 1) return; - // Draw route polyline + // Even a single node is worth showing (zoom to it) const coords = positions.map(p => [p.lat, p.lon]); - const routeLine = L.polyline(coords, { - color: '#f59e0b', weight: 3, opacity: 0.8, dashArray: '8 4' - }).addTo(markerLayer); + + if (positions.length >= 2) { + // Draw route polyline + L.polyline(coords, { + color: '#f59e0b', weight: 3, opacity: 0.8, dashArray: '8 4' + }).addTo(markerLayer); + } // Add numbered markers at each hop positions.forEach((p, i) => { L.circleMarker([p.lat, p.lon], { - radius: 8, fillColor: i === 0 ? '#22c55e' : i === positions.length - 1 ? '#ef4444' : '#f59e0b', + radius: 10, fillColor: i === 0 ? '#22c55e' : i === positions.length - 1 ? '#ef4444' : '#f59e0b', fillOpacity: 0.9, color: '#fff', weight: 2 }).addTo(markerLayer).bindTooltip(`${i + 1}. ${p.name}`, { permanent: true, direction: 'top', className: 'route-tooltip' }); }); // Fit map to route - map.fitBounds(L.latLngBounds(coords).pad(0.2)); + if (coords.length >= 2) { + map.fitBounds(L.latLngBounds(coords).pad(0.3)); + } else { + map.setView(coords[0], 13); + } } async function loadNodes() { diff --git a/public/packets.js b/public/packets.js index 7c209223..e2b82dc4 100644 --- a/public/packets.js +++ b/public/packets.js @@ -445,7 +445,7 @@
Timestamp
${pkt.timestamp}
Path
${pathHops.length ? renderPath(pathHops) : '—'}
- ${pathHops.length ? `🗺️ View route on map` : ''} + ${pathHops.length ? `` : ''} ${hasRawHex ? `
${buildHexLegend(ranges)}
${createColoredHexDump(pkt.raw_hex, ranges)}
` : ''} @@ -459,7 +459,6 @@ const replayBtn = panel.querySelector('.replay-live-btn'); if (replayBtn) { replayBtn.addEventListener('click', () => { - // Store packet in sessionStorage for the live page to pick up const livePkt = { id: pkt.id, hash: pkt.hash, _ts: new Date(pkt.timestamp).getTime(), @@ -470,6 +469,27 @@ window.location.hash = '#/live'; }); } + + // Wire up view route on map button + const routeBtn = document.getElementById('viewRouteBtn'); + if (routeBtn && pathHops.length) { + routeBtn.addEventListener('click', async () => { + try { + const resp = await fetch('/api/resolve-hops?hops=' + encodeURIComponent(pathHops.join(','))); + const data = await resp.json(); + // Build array of {hop, name, pubkey} with resolved full pubkeys + const resolvedHops = pathHops.map(h => { + const name = data.resolved[h]; + // Find full pubkey from name if possible + return name || h; + }); + sessionStorage.setItem('map-route-hops', JSON.stringify(pathHops)); + window.location.hash = '#/map?route=1'; + } catch { + window.location.hash = '#/map'; + } + }); + } } function escapeHtml(s) {