mirror of
https://github.com/MeshTender/MeshTender.git
synced 2026-09-16 16:42:37 +00:00
Explain regions and flooding
This commit is contained in:
@@ -361,4 +361,23 @@ func TestConfigRootFloodToggle(t *testing.T) {
|
||||
if !strings.Contains(page, `name="root_allow_flood" value="1"`) {
|
||||
t.Fatal("config page should render the root flood switch")
|
||||
}
|
||||
// While flooding is allowed there's nothing to warn about.
|
||||
if strings.Contains(page, `data-testid="config-root-deny-warning"`) {
|
||||
t.Error("the deny warning should only show while flooding is denied")
|
||||
}
|
||||
|
||||
// Denying is the consequential setting: the wildcard is the only thing that
|
||||
// repeats unscoped packets (meshcore-go node/region.go, FindFloodMatch), so
|
||||
// turning it off silently strands anyone who hasn't configured regions. Say so.
|
||||
resp = post(t, ts, h.app, path, url.Values{}, sess)
|
||||
resp.Body.Close()
|
||||
denied := readBody(t, do(t, ts, h.app, "/orgs/"+org.Slug+"/config", sess))
|
||||
if !strings.Contains(denied, `data-testid="config-root-deny-warning"`) {
|
||||
t.Error("denying root flood should warn what it does to unscoped traffic")
|
||||
}
|
||||
// And the label must not claim the wildcard applies "everywhere" — it governs
|
||||
// only packets that carry no region.
|
||||
if strings.Contains(denied, "everywhere") {
|
||||
t.Error(`the root row should not describe the wildcard as "everywhere"`)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,7 +95,11 @@
|
||||
<label class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="region_allow_flood" value="1" {{if .Region.AllowFlood}}checked{{end}}>
|
||||
<span class="form-check-label">Allow flood</span>
|
||||
<small class="form-hint">Whether flooding is permitted inside this region.</small>
|
||||
{{/* A repeater repeats a scoped flood packet only if it matches a region that
|
||||
allows flooding (RegionMap::findMatch, and allowPacketForward in
|
||||
examples/simple_repeater/MyMesh.cpp), so this is "carry this region's
|
||||
traffic", not a property of the area itself. */}}
|
||||
<small class="form-hint">Repeat flood packets scoped to this region. Turn it off and a repeater here drops them.</small>
|
||||
</label>
|
||||
<label class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" name="region_primary" value="1" {{if .Region.Primary}}checked{{end}}>
|
||||
|
||||
@@ -217,6 +217,51 @@ func TestE2ERegionIconTooltips(t *testing.T) {
|
||||
watch.assertClean(t)
|
||||
}
|
||||
|
||||
// TestE2ERootRegionExplainer: the wildcard's info button opens a popover explaining
|
||||
// what it does and doesn't govern. Bootstrap popovers are opt-in and initialized once
|
||||
// in ui.js against [data-popover]; the content is the thing people most often misread,
|
||||
// so it's worth proving it actually reaches the screen.
|
||||
func TestE2ERootRegionExplainer(t *testing.T) {
|
||||
srv := newE2EServer(t)
|
||||
user, cookie := srv.login(t, "e2erootinfo")
|
||||
org, err := srv.store.CreateOrg(srv.ctx, "Root Info Org", user.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("create org: %v", err)
|
||||
}
|
||||
|
||||
bctx, cancel, watch := startBrowser(t)
|
||||
defer cancel()
|
||||
|
||||
var header, body string
|
||||
cfgURL := srv.appURL + "/orgs/" + org.Slug + "/config"
|
||||
if err := chromedp.Run(bctx,
|
||||
network.Enable(),
|
||||
cdplog.Enable(),
|
||||
setSessionCookie(cookie),
|
||||
chromedp.Navigate(cfgURL),
|
||||
chromedp.WaitVisible(`[data-testid="config-root-info"]`, chromedp.ByQuery),
|
||||
chromedp.Focus(`[data-testid="config-root-info"]`, chromedp.ByQuery),
|
||||
chromedp.WaitVisible(`.popover .popover-body`, chromedp.ByQuery),
|
||||
chromedp.Text(`.popover .popover-header`, &header, chromedp.ByQuery),
|
||||
chromedp.Text(`.popover .popover-body`, &body, chromedp.ByQuery),
|
||||
); err != nil {
|
||||
t.Fatalf("browser run against %s: %v", cfgURL, err)
|
||||
}
|
||||
if !strings.Contains(header, "flooding") {
|
||||
t.Errorf("popover header = %q, want it to mention flooding", header)
|
||||
}
|
||||
// The claims that matter, each verified against the firmware
|
||||
// (filterRecvFloodPacket / allowPacketForward in MyMesh.cpp): the wildcard
|
||||
// governs unscoped packets, scoped ones are matched against the defined regions,
|
||||
// and direct-routed packets are exempt from all of it.
|
||||
for _, want := range []string{"Unscoped", "Scoped", "direct-routed"} {
|
||||
if !strings.Contains(body, want) {
|
||||
t.Errorf("popover body missing %q; got %q", want, body)
|
||||
}
|
||||
}
|
||||
watch.assertClean(t)
|
||||
}
|
||||
|
||||
// TestE2ERootFloodSwitchAutosubmits: the root (*) flood switch on the Configuration
|
||||
// page saves by itself — it's a data-autosubmit checkbox inside its own form nested
|
||||
// in a list row, so this proves the delegated handler reaches it and that the POST
|
||||
|
||||
@@ -23,6 +23,10 @@
|
||||
// on hover/focus, instead of the browser's slow
|
||||
// native one. Use it on icon-only controls,
|
||||
// where the icon alone doesn't say what it does.
|
||||
// [data-popover] — like data-tooltip but for a paragraph rather
|
||||
// than a label: title= is the heading and
|
||||
// data-bs-content= the body. Put it on a
|
||||
// <button> so it is reachable by keyboard.
|
||||
//
|
||||
// It also localizes timestamps: any <time data-fmt="…"> element (emitted by the
|
||||
// `ts` template func) is rewritten from its machine-readable datetime attribute
|
||||
@@ -41,6 +45,18 @@
|
||||
if (window.tabler && window.tabler.Tooltip) {
|
||||
new window.tabler.Tooltip(document.body, { selector: "[data-tooltip]" });
|
||||
}
|
||||
// Popovers carry an explanation too long for a tooltip. Same delegation, and
|
||||
// "hover focus" means a pointer reveals it while keyboard and touch can still
|
||||
// reach it — both dismiss on leave/blur, so there is no sticky panel to close.
|
||||
// Content is server-authored copy, never user data; Bootstrap sanitizes it anyway.
|
||||
if (window.tabler && window.tabler.Popover) {
|
||||
new window.tabler.Popover(document.body, {
|
||||
selector: "[data-popover]",
|
||||
trigger: "hover focus",
|
||||
html: true,
|
||||
placement: "left",
|
||||
});
|
||||
}
|
||||
|
||||
// --- timestamp localization ---------------------------------------------
|
||||
// The server emits <time datetime="<RFC3339 UTC>" data-fmt="date|datetime|
|
||||
|
||||
@@ -108,6 +108,24 @@
|
||||
</div>
|
||||
{{end}}
|
||||
</div>
|
||||
{{if and .CanEdit (not .Config.RootAllowFlood)}}
|
||||
{{/* Denying the wildcard is the one setting here that can quietly take an org off
|
||||
the air, so it gets a standing alert rather than a line of small print. */}}
|
||||
<div class="card-body">
|
||||
<div class="alert alert-danger mb-0" role="alert" data-testid="config-root-deny-warning">
|
||||
<div>{{template "icon-alert" "alert-icon"}}</div>
|
||||
<div>
|
||||
<div class="fw-bold">Unscoped packets are being dropped</div>
|
||||
<p class="mb-0">
|
||||
Flooding is denied on <code>*</code>, so a repeater running this configuration
|
||||
repeats nothing that arrives without a region — including traffic from any
|
||||
companion whose owner hasn't set up regions yet. Only packets scoped to a
|
||||
region you allow below get through.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{{end}}
|
||||
<div class="card-body p-0">
|
||||
<div class="row g-0">
|
||||
<div class="col-lg-8">
|
||||
@@ -142,11 +160,14 @@
|
||||
<div class="list-group list-group-flush" style="max-height:520px; overflow-y:auto">
|
||||
{{if .CanEdit}}
|
||||
{{/* The root (*) isn't a region row — it lives on the org — so it has no
|
||||
swatch and its only setting is flood policy, saved on its own. */}}
|
||||
swatch and its only setting is flood policy, saved on its own.
|
||||
It is NOT "everywhere": MeshCore consults the wildcard only for packets
|
||||
that carry no region, and scoped packets are matched against the defined
|
||||
regions alone (meshcore-go node/region.go, FindFloodMatch). The old
|
||||
wording invited exactly the wrong reading. */}}
|
||||
<div class="list-group-item d-flex align-items-center gap-2" data-testid="config-region-root">
|
||||
<div class="flex-fill" style="min-width:0">
|
||||
<div class="fw-bold">Root <span class="text-secondary fw-normal">· <code>*</code> · everywhere</span></div>
|
||||
<div class="text-secondary small">Deny flood to scope flooding to your defined regions.</div>
|
||||
<div class="fw-bold">Root <span class="text-secondary fw-normal">· <code>*</code></span></div>
|
||||
</div>
|
||||
<form method="post" action="/orgs/{{.Org.Slug}}/config/root-flood" class="m-0 flex-shrink-0">
|
||||
{{/* No hidden shadow input: an unchecked checkbox simply isn't submitted,
|
||||
@@ -158,6 +179,20 @@
|
||||
<span class="form-check-label">Allow flood</span>
|
||||
</label>
|
||||
</form>
|
||||
{{/* Sits next to the switch, not the row label: what needs explaining is
|
||||
allow/deny flooding, not the word "Root". Outside the <label> so a
|
||||
click opens the popover instead of toggling the switch.
|
||||
|
||||
Every claim below is from the firmware — filterRecvFloodPacket and
|
||||
allowPacketForward in examples/simple_repeater/MyMesh.cpp, and
|
||||
RegionMap::findMatch. Notably the direct-route exemption is real:
|
||||
direct packets also get a NULL region, but the region check in
|
||||
allowPacketForward is guarded by isRouteFlood(), so they pass. */}}
|
||||
<button type="button" class="btn btn-icon btn-ghost-secondary btn-sm flex-shrink-0 ms-2"
|
||||
data-popover data-testid="config-root-info"
|
||||
aria-label="How flooding and regions work"
|
||||
title="How flooding and regions work"
|
||||
data-bs-content="A flooded packet either carries a region scope or it doesn't.<br><br><strong>Unscoped</strong> packets are matched only by <code>*</code>. <strong>Scoped</strong> packets are matched only against the regions defined below — define none, and every scoped packet is dropped.<br><br><strong>Allow flood</strong> on a region means a repeater configured with it repeats packets scoped to that region. <strong>Deny flood</strong> means it drops them — the same as never defining that region.<br><br>Most meshes allow flood on <code>*</code>: a companion whose owner hasn't set up regions sends unscoped, and that is how it reaches the mesh.<br><br>None of this touches direct-routed packets. Those follow the path recorded in them rather than flooding.">{{template "icon-help" ""}}</button>
|
||||
</div>
|
||||
{{end}}
|
||||
{{range .Config.Regions}}
|
||||
|
||||
Reference in New Issue
Block a user