From 8ebce8087b82c0803356dbda6940bab1f83d8a40 Mon Sep 17 00:00:00 2001 From: you Date: Fri, 20 Mar 2026 23:55:38 +0000 Subject: [PATCH] fix: packet detail lookup uses transmission ID index After dedup, table rows have transmission IDs but getById() maps observation IDs. Added byTxId index so /api/packets/:id resolves both observation and transmission IDs correctly. --- packet-store.js | 11 +++++++++++ server.js | 4 +++- 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/packet-store.js b/packet-store.js index f32b231..c909ee9 100644 --- a/packet-store.js +++ b/packet-store.js @@ -23,6 +23,7 @@ class PacketStore { // Indexes this.byId = new Map(); // observation_id → observation object (backward compat for packet detail links) + this.byTxId = new Map(); // transmission_id → transmission object this.byHash = new Map(); // hash → transmission object (1:1) this.byObserver = new Map(); // observer_id → [observation objects] this.byNode = new Map(); // pubkey → [transmission objects] (deduped) @@ -102,6 +103,7 @@ class PacketStore { this.byTransmission.set(row.hash, tx); this.byHash.set(row.hash, tx); this.packets.push(tx); + this.byTxId.set(tx.id, tx); this._indexByNode(tx); } @@ -188,6 +190,7 @@ class PacketStore { this.byTransmission.set(pkt.hash, tx); this.byHash.set(pkt.hash, tx); this.packets.push(tx); + this.byTxId.set(tx.id, tx); this._indexByNode(tx); } @@ -250,6 +253,7 @@ class PacketStore { const old = this.packets.pop(); this.byHash.delete(old.hash); this.byTransmission.delete(old.hash); + this.byTxId.delete(old.id); // Remove observations from byId and byObserver for (const obs of old.observations) { this.byId.delete(obs.id); @@ -292,6 +296,7 @@ class PacketStore { this.byTransmission.set(row.hash, tx); this.byHash.set(row.hash, tx); this.packets.unshift(tx); // newest first + this.byTxId.set(tx.id, tx); this._indexByNode(tx); } else { // Update first_seen if earlier @@ -517,6 +522,12 @@ class PacketStore { return this.byId.get(id) || null; } + /** Get a transmission by its transmission table ID */ + getByTxId(id) { + if (this.sqliteOnly) return this.db.prepare('SELECT * FROM transmissions WHERE id = ?').get(id) || null; + return this.byTxId.get(id) || null; + } + /** Get all siblings of a packet (same hash) — returns observations array */ getSiblings(hash) { if (this.sqliteOnly) return this.db.prepare('SELECT * FROM packets WHERE hash = ? ORDER BY timestamp DESC').all(hash); diff --git a/server.js b/server.js index e52ccff..889f737 100644 --- a/server.js +++ b/server.js @@ -778,7 +778,9 @@ app.get('/api/packets/timestamps', (req, res) => { }); app.get('/api/packets/:id', (req, res) => { - const packet = pktStore.getById(Number(req.params.id)) || db.getPacket(Number(req.params.id)); + const id = Number(req.params.id); + // Try observation ID first, then transmission ID, then legacy packets table + const packet = pktStore.getById(id) || pktStore.getByTxId(id) || db.getPacket(id); if (!packet) return res.status(404).json({ error: 'Not found' }); // Use the sibling with the longest path (most hops) for display