The 5s auto-poll (hx-trigger="every Ns" on #ips-table-wrap) and every
button/form that also swaps #ips-table-wrap (bulk recheck/delete/clear,
per-row recheck/cancel/delete, the add-address form) each fired
independent, uncoordinated htmx requests against the same target. With no
hx-sync, whichever response landed last won — including a poll's
in-flight GET landing *after* a slower mutation's own response and
silently reverting the just-applied change with stale data. This matched
every symptom reported: buttons needing several clicks before they
"took", "Перепроверить выбранные" appearing to do nothing with many rows
selected (more DB writes -> wider race window for a poll to land after
and clobber it), the page "blinking" back to a stale queued state a few
seconds after a bulk recheck actually succeeded, and auto-refresh working
"every other time".
Every element that targets #ips-table-wrap now shares
hx-sync="#ips-table-wrap:queue last", so at most one request affecting it
is ever in flight: a trigger that fires while another is pending gets
queued (never aborted mid-write) and only the most recent queued trigger
actually runs once the current one finishes, guaranteeing responses are
always applied in the order they actually resolve.
Rebuilt bin/admin-dashboard (only internal/dashboard changed) and
bin/SHA256SUMS per docs/SETUP.md's documented build recipe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
"Перепроверить выбранные" joins the existing "Удалить выбранные" / "Очистить
всё" bulk actions, using the checked-row selection the same way delete
already does — no control-api changes needed, since forcing a recheck of a
batch of addresses (new, finished, or already-queued, skipping anything
mid-check) is exactly what POST /api/v1/admin/ips (db.SubmitIPs) already
does, and the dashboard's own SubmitIPs client method already backs both
the top add/recheck form and the single-row recheck button.
The page also now auto-refreshes every 5s (handleIPsFragment + GET
/ips/fragment), mirroring the overview page's existing hx-trigger="every
Ns" polling and reusing the same config-driven interval
(Cfg.OverviewPollIntervalS) rather than adding a duplicate knob. Since the
table now polls itself, each row's selection checkbox gets a stable id
plus hx-preserve so a checked box survives the refresh (its own DOM node
is kept) while the rest of the row still updates live.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both binaries registered with control-api exactly once at startup and
exited (os.Exit(1)) on any failure — including control-api simply not
being up yet (no ordering guarantee between the two at boot/redeploy) or
the admin not having added this validator_id/site_id to the config yet.
Run() now retries registration with capped exponential backoff (3s->30s)
until it succeeds or the process is asked to shut down, instead of
crashing; registerWithRetry is identical in agentcore and probercore
since their Run/register shape already was.
Separately, the admin dashboard's Validators page had no hostname column
even though the agent already reports one on register (mirroring the
prober) and control-api already persists it — only the admin-config read
DTO (validatorDTO in httpapi and dashboard) dropped it before it reached
the template. Added hostname + last_heartbeat_at to that DTO end-to-end
and a Хост/Heartbeat column to validators.html, matching sites.html.
Rebuilt bin/{control-api,admin-dashboard,prober,validator-agent} and
bin/SHA256SUMS per docs/SETUP.md's documented build recipe.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>