Time-travel map: filter nodes by replay timestamp

When scrubbing to a past time, only nodes that had advertised before
that time appear on the map. Nodes that hadn't been seen yet are
hidden.

- Added 'before' param to /api/nodes (filters first_seen <= before)
- loadNodes(beforeTs) accepts optional timestamp filter
- clearNodeMarkers() removes all markers and data
- vcrReplayFromTs clears and reloads nodes for replay time
- vcrResumeLive reloads all nodes (no filter)
This commit is contained in:
you
2026-03-19 07:50:17 +00:00
parent a271696766
commit 25fc2924e0
2 changed files with 23 additions and 3 deletions
+21 -2
View File
@@ -92,6 +92,9 @@
VCR.dragPct = null;
VCR.scrubTs = null;
vcrSetMode('LIVE');
// Reload all nodes (no time filter)
clearNodeMarkers();
loadNodes();
const prompt = document.getElementById('vcrPrompt');
if (prompt) prompt.classList.add('hidden');
}
@@ -115,6 +118,11 @@
const fetchTo = new Date(targetTs + 300000).toISOString(); // 5 min window
stopReplay();
vcrSetMode('REPLAY');
// Reload map nodes to match the replay time
clearNodeMarkers();
loadNodes(targetTs);
fetch(`/api/packets?limit=200&grouped=false&since=${encodeURIComponent(fetchFrom)}&until=${encodeURIComponent(fetchTo)}`)
.then(r => r.json())
.then(data => {
@@ -818,9 +826,12 @@
}, 2000);
}
async function loadNodes() {
async function loadNodes(beforeTs) {
try {
const resp = await fetch('/api/nodes?limit=500');
const url = beforeTs
? `/api/nodes?limit=500&before=${encodeURIComponent(new Date(beforeTs).toISOString())}`
: '/api/nodes?limit=500';
const resp = await fetch(url);
const nodes = await resp.json();
const list = Array.isArray(nodes) ? nodes : (nodes.nodes || []);
list.forEach(n => {
@@ -833,6 +844,14 @@
} catch (e) { console.error('Failed to load nodes:', e); }
}
function clearNodeMarkers() {
for (const [key, marker] of Object.entries(nodeMarkers)) {
if (map) map.removeLayer(marker);
}
nodeMarkers = {};
nodeData = {};
}
function addNodeMarker(n) {
if (nodeMarkers[n.public_key]) return nodeMarkers[n.public_key];
const color = ROLE_COLORS[n.role] || ROLE_COLORS.unknown;
+2 -1
View File
@@ -455,13 +455,14 @@ app.post('/api/packets', (req, res) => {
});
app.get('/api/nodes', (req, res) => {
const { limit = 50, offset = 0, role, region, lastHeard, sortBy = 'lastSeen', search } = req.query;
const { limit = 50, offset = 0, role, region, lastHeard, sortBy = 'lastSeen', search, before } = req.query;
let where = [];
let params = {};
if (role) { where.push('role = @role'); params.role = role; }
if (search) { where.push('name LIKE @search'); params.search = `%${search}%`; }
if (before) { where.push('first_seen <= @before'); params.before = before; }
if (lastHeard) {
const durations = { '1h': 3600000, '6h': 21600000, '24h': 86400000, '7d': 604800000, '30d': 2592000000 };
const ms = durations[lastHeard];