diff --git a/public/analytics.js b/public/analytics.js index 7b178bc8..a4ceed7e 100644 --- a/public/analytics.js +++ b/public/analytics.js @@ -876,6 +876,26 @@ `; }).join('')} + ${data.distributionByRepeaters ? (() => { + const dr = data.distributionByRepeaters; + const totalRepeaters = (dr[1] || 0) + (dr[2] || 0) + (dr[3] || 0); + const rpct = (n) => totalRepeaters ? (n / totalRepeaters * 100).toFixed(1) : '0'; + const maxRepeaters = Math.max(dr[1] || 0, dr[2] || 0, dr[3] || 0, 1); + const colors = { 1: '#ef4444', 2: '#22c55e', 3: '#3b82f6' }; + return `

By Repeaters

+

${totalRepeaters.toLocaleString()} unique repeaters

+
+ ${[1, 2, 3].map(size => { + const count = dr[size] || 0; + const width = Math.max((count / maxRepeaters) * 100, count ? 2 : 0); + return `
+
${size}-byte
+
+
${count.toLocaleString()} (${rpct(count)}%)
+
`; + }).join('')} +
`; + })() : ''}

📈 Hash Size Over Time

diff --git a/public/index.html b/public/index.html index a6cb97de..781cf321 100644 --- a/public/index.html +++ b/public/index.html @@ -22,9 +22,9 @@ - - - + + + @@ -81,29 +81,29 @@
- - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/server.js b/server.js index 31cd6d74..b828ae05 100644 --- a/server.js +++ b/server.js @@ -1987,9 +1987,17 @@ app.get('/api/analytics/hash-sizes', (req, res) => { .sort(([, a], [, b]) => b.packets - a.packets) .map(([name, data]) => ({ name, ...data })); + // Distribution by number of repeaters advertising each hash size + const distributionByRepeaters = { 1: 0, 2: 0, 3: 0 }; + for (const [, v] of Object.entries(byNode)) { + const s = v.hashSize; + if (s >= 1 && s <= 3) distributionByRepeaters[s]++; + } + const _hsResult = { total: packets.length, distribution, + distributionByRepeaters, hourly, topHops, multiByteNodes diff --git a/test-server-routes.js b/test-server-routes.js index d1f448d5..78fd785e 100644 --- a/test-server-routes.js +++ b/test-server-routes.js @@ -583,6 +583,11 @@ seedTestData(); await t('GET /api/analytics/hash-sizes', async () => { const r = await request(app).get('/api/analytics/hash-sizes').expect(200); assert(typeof r.body === 'object', 'should return hash sizes'); + assert(r.body.distributionByRepeaters, 'should include distributionByRepeaters'); + assert(typeof r.body.distributionByRepeaters === 'object', 'distributionByRepeaters should be an object'); + for (const s of [1, 2, 3]) { + assert(typeof r.body.distributionByRepeaters[s] === 'number', `distributionByRepeaters[${s}] should be a number`); + } }); await t('GET /api/analytics/hash-sizes with region', async () => {