Skip to content

small web UI improvements#34

Merged
tridge merged 4 commits intoArduPilot:mainfrom
tridge:pr-web-tweaks
May 10, 2026
Merged

small web UI improvements#34
tridge merged 4 commits intoArduPilot:mainfrom
tridge:pr-web-tweaks

Conversation

@tridge
Copy link
Copy Markdown
Contributor

@tridge tridge commented May 10, 2026

No description provided.

tridge and others added 4 commits May 10, 2026 19:09
Drop <meta http-equiv="refresh"> from the four live views and have
new static/auto-refresh.js poll the same URL every N seconds (set
via <body data-auto-refresh="N">) and swap the <main> element in
place via DOMParser. The <header> (logo, nav, log-out) is outside
<main> so it never re-renders — no flash on the auto-refresh.

Firefox's behaviour was the trigger: it doesn't paint-hold across
full-page navigations the way Chrome does, so even with the static
asset Cache-Control header in place the entire page (including the
ArduPilot logo) blanked for a frame on every meta-refresh tick.

Helpers (localtime.js, password-toggle.js) listen for the new
'pageupdate' event auto-refresh.js dispatches after each swap, so
any newly-injected <time> or password input picks up the same
treatment as on initial load. The polling skips ticks when
document.hidden is true so backgrounded tabs don't burn cycles,
guards against overlapping in-flight requests, and uses
redirect: 'manual' so a 302 to /login (e.g. session expired) stops
the swap rather than injecting a login form into the live view.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The browser cache header introduced for the logo flash fix
(SEND_FILE_MAX_AGE_DEFAULT = 86400) also applied to the tlog
download route, so each downloaded telemetry file came back as
Cache-Control: public, max-age=86400. On a shared device or behind
an intermediary cache that's a real exposure window for vehicle
telemetry — flagged by codex review.

Override on the download path only: pass max_age=0 to
send_from_directory and replace the response's Cache-Control with
'private, no-store' (plus a Pragma: no-cache for legacy proxies).
Logo/CSS/JS keep the 24h cache so the auto-refresh still doesn't
flash the header.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The /login route had no rate limit or lockout — flagged by codex
review. Anyone reaching the public listener could grind through
passphrases as fast as the server would answer.

Two layers, in-memory:

  * Constant 0.5 s sleep on every failed login. Caps single-IP
    throughput at ~120/min regardless of the counter.
  * Per-IP sliding-window failure counter (60 s window, 10
    failures). Once exceeded, further attempts from that IP are
    rejected with "Too many failed login attempts" — even if the
    passphrase is correct — until the window expires. A successful
    login clears the IP's history so a few user typos don't lock
    them out for a full minute.

Skipped under app.config['TESTING'] so the existing webadmin suite
(many sequential login_as calls from 127.0.0.1) doesn't trip it.
Counters live in a process-local dict guarded by threading.Lock —
fine for the small admin UI; bigger deploys should swap in
flask-limiter with a redis backend.

5 new tests cover: success resets the counter, threshold blocks
even with the right passphrase, separate IPs don't share state,
the sliding window expires correctly, and TESTING short-circuits
the throttle.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The previous rsync swept the whole working tree, relying on an
allow-list of excludes to keep secrets out. Codex review flagged it:
generic scratch (pass.dat, x.dat, way.txt), local-only scripts,
*.orig from a recent merge etc. weren't covered, so a fresh untracked
file in the local cwd would silently ship.

Switch to deny-by-default. Build a temporary staging tree from
'git ls-files --recurse-submodules' (so modules/mavlink/* lands
along with the top-level files), filter out paths that resolve to a
directory (uninitialised nested submodules — cp without -r would
otherwise fail), then rsync that staging tree to the remote. Local
dry-run confirms pass.dat / x.dat / way.txt / .webadmin_secret /
fullchain.pem / keys.tdb / AGENTS.md / *.orig / *.rej / etc. all
stay behind, while the 477 tracked files (including the mavlink
submodule and .git/) ship correctly. Uncommitted edits to TRACKED
files still go through; only NEW files now have to be git add'd
before they're deployable, which is reasonable hygiene.

RSYNC_EXCLUDES is unchanged — the staging side is already clean,
so the excludes' job is now just protecting server-side state
(keys.tdb, *.pem, logs/, .webadmin_secret) from --delete.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tridge tridge merged commit ceea9de into ArduPilot:main May 10, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant