Merge branch 'areas-meshguide-sync'

This commit is contained in:
dborup committed 2026-07-26 09:34:23 +02:00
commit 4090d697a1
5 files changed
+37 -10

No files matched your search

+6 -1
View File
@@ -3939,13 +3939,18 @@ func (s *Server) handleScopeStats(w http.ResponseWriter, r *http.Request) {
resp.ConfiguredRegions = len(configured)
if matched, err := s.db.GetMatchedRegionNames(); err == nil {
unused := make([]string, 0, len(configured))
used := make([]string, 0, len(configured))
for _, name := range configured {
if !matched[name] {
if matched[name] {
used = append(used, name)
} else {
unused = append(unused, name)
}
}
sort.Strings(unused)
sort.Strings(used)
resp.UnusedRegions = unused
resp.UsedRegions = used
} else {
log.Printf("WARN GetMatchedRegionNames: %v", err)
}
+11
View File
@@ -4605,6 +4605,17 @@ func TestHandleScopeStats_UnusedRegions(t *testing.T) {
t.Errorf("unusedRegions[%d] = %q, want %q", i, resp.UnusedRegions[i], name)
}
}
// UsedRegions is the complement -- together with UnusedRegions it's a
// complete partition of the (deduped) configured list.
wantUsed := []string{"#belgium"}
if len(resp.UsedRegions) != len(wantUsed) {
t.Fatalf("usedRegions = %v, want %v", resp.UsedRegions, wantUsed)
}
for i, name := range wantUsed {
if resp.UsedRegions[i] != name {
t.Errorf("usedRegions[%d] = %q, want %q", i, resp.UsedRegions[i], name)
}
}
}
// TestHandleScopeStats_RepeatersByRegion verifies the "which repeaters
+9 -5
View File
@@ -157,13 +157,17 @@ type ScopeStatsResponse struct {
Summary ScopeStatsSummary `json:"summary"`
ByRegion []ScopeRegionCount `json:"byRegion"`
TimeSeries []ScopeTimePoint `json:"timeSeries"`
// ConfiguredRegions/UnusedRegions are all-time (not window-scoped):
// how many hashRegions are configured, and which of them have never
// matched a single transmission still in retention. Lets an operator
// see at a glance how much of their region list is dead weight.
// Omitted (both zero-value) when the server config has no hashRegions.
// ConfiguredRegions/UnusedRegions/UsedRegions are all-time (not
// window-scoped): how many hashRegions are configured, and which of
// them have never matched a single transmission still in retention
// (UnusedRegions) vs which have (UsedRegions) -- together a
// complete partition of the configured list. Lets an operator see
// at a glance how much of their region list is dead weight, and
// which specific regions the used/unused counts refer to. Omitted
// (all zero-value) when the server config has no hashRegions.
ConfiguredRegions int `json:"configuredRegions,omitempty"`
UnusedRegions []string `json:"unusedRegions,omitempty"`
UsedRegions []string `json:"usedRegions,omitempty"`
// RepeatersByRegion is all-time (not window-scoped), like
// UnusedRegions: for each region that has ever matched a transmission,
// which distinct repeaters/rooms have relayed traffic carrying that
+10 -4
View File
@@ -5179,7 +5179,7 @@ function destroy() { _stopRolesRefresh(); _stopScopesRefresh(); _stopForeignTraf
}).join('');
setSectionHtml(areaAdoptEl, detailsSection(
'Scope Adoption by Area (' + byArea.length.toLocaleString() + ' areas)',
null,
'All-time, not limited to the window above — of the real nodes sitting in each configured area, how many actually support its linked region (own default_scope, or ever relayed it)?',
areaGroups,
'scope-adoption-by-area'
));
@@ -5197,9 +5197,11 @@ function destroy() { _stopRolesRefresh(); _stopScopesRefresh(); _stopForeignTraf
var configured = d.configuredRegions || 0;
if (configured > 0) {
var unused = d.unusedRegions || [];
var usedCount = configured - unused.length;
var used = d.usedRegions || [];
var usedCount = used.length;
var unusedPct = (unused.length / configured * 100).toFixed(1);
var listHtml = unused.map(function(name) { return regionCodeHtml(name); }).join(', ');
var unusedListHtml = unused.map(function(name) { return regionCodeHtml(name); }).join(', ');
var usedListHtml = used.map(function(name) { return regionCodeHtml(name); }).join(', ');
setSectionHtml(utilEl, detailsSection(
'Region Utilization (' + usedCount.toLocaleString() + ' of ' + configured.toLocaleString() + ' used)',
'All-time, not limited to the window above — has this configured region ever matched a message still in retention?',
@@ -5207,9 +5209,13 @@ function destroy() { _stopRolesRefresh(); _stopScopesRefresh(); _stopForeignTraf
'<strong>' + usedCount.toLocaleString() + '</strong> of <strong>' + configured.toLocaleString() + '</strong> configured regions have matched at least once' +
(unused.length > 0 ? ' — <strong>' + unused.length.toLocaleString() + '</strong> (' + unusedPct + '%) have never matched anything.' : '.') +
'</p>' +
(used.length > 0 ?
'<details data-key="used-regions"><summary style="cursor:pointer">Show ' + used.length.toLocaleString() + ' used region' + (used.length === 1 ? '' : 's') + '</summary>' +
'<div class="mono text-muted" style="font-size:11px;margin-top:8px;max-height:200px;overflow-y:auto;line-height:1.6">' + usedListHtml + '</div></details>'
: '') +
(unused.length > 0 ?
'<details data-key="unused-regions"><summary style="cursor:pointer">Show ' + unused.length.toLocaleString() + ' unused region' + (unused.length === 1 ? '' : 's') + '</summary>' +
'<div class="mono text-muted" style="font-size:11px;margin-top:8px;max-height:200px;overflow-y:auto;line-height:1.6">' + listHtml + '</div></details>'
'<div class="mono text-muted" style="font-size:11px;margin-top:8px;max-height:200px;overflow-y:auto;line-height:1.6">' + unusedListHtml + '</div></details>'
: ''),
'region-utilization'
));
+1
View File
@@ -679,6 +679,7 @@
const btooltip = "Normalized betweenness centrality (0..1). How often this node sits on the shortest path between other pairs of nodes in the affinity graph. 1.0 = the most structurally critical node on the mesh. High Bridge + low Traffic share = a quiet but irreplaceable chokepoint.";
return `<tr id="row-bridge-score" data-bridge-score="${b.toFixed(4)}"><td title="${btooltip}">Bridge score <span style="color:var(--text-muted);cursor:help" aria-label="help">ⓘ</span></td><td><span style="display:inline-block;vertical-align:middle;width:80px;height:8px;background:var(--bg-secondary,#333);border-radius:4px;overflow:hidden;margin-right:6px"><span style="display:block;width:${bbarWidth}%;height:100%;background:${bcolor}"></span></span><span style="color:${bcolor};font-weight:600">${bpct}%</span> <span style="color:var(--text-muted);font-size:11px;margin-left:4px">${blabel}</span></td></tr>`;
})() : ''}
${'default_scope' in n ? `<tr id="row-own-scope"><td title="This node's own configured region scope (hashRegions) — set in its own adverts. Distinct from Transported scopes below, which are regions this node has relayed for OTHER nodes.">Own scope</td><td>${n.default_scope === null ? '<span class="text-muted">—</span>' : n.default_scope === '' ? '<span class="text-muted">unknown scope</span>' : '<span class="badge">' + escapeHtml(n.default_scope) + '</span>'}</td></tr>` : ''}
${(n.role === 'repeater' || n.role === 'room') && Array.isArray(n.transported_scopes) && n.transported_scopes.length ? `<tr id="row-transported-scopes"><td title="Distinct region scopes (transmissions.scope_name) of all non-advert packets in which this repeater appears as a path hop. Shows which regions' traffic this repeater has carried (#1751).">Transported scopes</td><td><span style="display:inline-flex;flex-wrap:wrap;gap:3px;vertical-align:middle">${n.transported_scopes.map(sc => '<span class="badge">' + escapeHtml(String(sc)) + '</span>').join('')}</span></td></tr>` : ''}
<tr><td>First Seen</td><td>${renderNodeTimestampHtml(n.first_seen)}</td></tr>
<tr><td>Total Packets</td><td>${stats.totalTransmissions || stats.totalPackets || n.advert_count || 0}${stats.totalObservations && stats.totalObservations !== (stats.totalTransmissions || stats.totalPackets) ? ' <span class="text-muted" style="font-size:0.85em">(seen ' + stats.totalObservations + '×)</span>' : ''}</td></tr>