diff --git a/public/index.html b/public/index.html
index 6354d74c..652c5d30 100644
--- a/public/index.html
+++ b/public/index.html
@@ -81,7 +81,7 @@
-
+
diff --git a/public/packets.js b/public/packets.js
index 72ca31ed..47158137 100644
--- a/public/packets.js
+++ b/public/packets.js
@@ -587,25 +587,21 @@
const groupBtn = document.getElementById('fGroup');
if (groupBtn) groupBtn.classList.toggle('active', groupByHash);
- // Filter to claimed/favorited nodes if toggle is on
+ // Filter to claimed/favorited nodes if toggle is on — use server-side multi-node lookup
let displayPackets = packets;
if (filters.myNodes) {
const myNodes = JSON.parse(localStorage.getItem('meshcore-my-nodes') || '[]');
- const myKeys = new Set(myNodes.map(n => n.pubkey));
+ const myKeys = myNodes.map(n => n.pubkey).filter(Boolean);
const favs = getFavorites();
- const allKeys = new Set([...myKeys, ...favs]);
- displayPackets = packets.filter(p => {
+ const allKeys = [...new Set([...myKeys, ...favs])];
+ if (allKeys.length > 0) {
try {
- const d = JSON.parse(p.decoded_json || '{}');
- const pathHops = JSON.parse(p.path_json || '[]');
- return (d.pubKey && allKeys.has(d.pubKey)) ||
- (d.srcPubKey && allKeys.has(d.srcPubKey)) ||
- (d.destPubKey && allKeys.has(d.destPubKey)) ||
- (d.srcHash && allKeys.has(d.srcHash)) ||
- (d.destHash && allKeys.has(d.destHash)) ||
- pathHops.some(h => allKeys.has(h));
- } catch { return false; }
- });
+ const myData = await api('/packets?nodes=' + allKeys.join(',') + '&limit=500');
+ displayPackets = myData.packets || [];
+ } catch { displayPackets = []; }
+ } else {
+ displayPackets = [];
+ }
}
if (!displayPackets.length) {
diff --git a/server.js b/server.js
index b2fc89f0..af46701d 100644
--- a/server.js
+++ b/server.js
@@ -671,9 +671,28 @@ app.get('/api/stats', (req, res) => {
});
app.get('/api/packets', (req, res) => {
- const { limit = 50, offset = 0, type, route, region, observer, hash, since, until, groupByHash, node } = req.query;
+ const { limit = 50, offset = 0, type, route, region, observer, hash, since, until, groupByHash, node, nodes } = req.query;
const order = req.query.order === 'asc' ? 'ASC' : 'DESC';
+ // Multi-node filter: comma-separated pubkeys
+ if (nodes) {
+ const pubkeys = nodes.split(',').map(s => s.trim()).filter(Boolean);
+ const allPackets = new Map();
+ for (const pk of pubkeys) {
+ const { packets: found } = pktStore.findPacketsForNode(pk);
+ for (const p of found) allPackets.set(p.id, p);
+ }
+ let results = [...allPackets.values()].sort((a, b) => order === 'DESC' ? b.timestamp.localeCompare(a.timestamp) : a.timestamp.localeCompare(b.timestamp));
+ // Apply additional filters
+ if (type !== undefined) results = results.filter(p => String(p.payload_type) === String(type));
+ if (region) results = results.filter(p => (p.observer_id || '').includes(region) || (p.decoded_json || '').includes(region));
+ if (since) results = results.filter(p => p.timestamp >= since);
+ if (until) results = results.filter(p => p.timestamp <= until);
+ const total = results.length;
+ const paged = results.slice(Number(offset), Number(offset) + Number(limit));
+ return res.json({ packets: paged, total, limit: Number(limit), offset: Number(offset) });
+ }
+
if (groupByHash === 'true') {
return res.json(pktStore.queryGrouped({ limit, offset, type, route, region, observer, hash, since, until, node }));
}