2 Commits

Author SHA1 Message Date
hell 9ff488de42 fix: three follow-ups from the second review round
- CachedStaticFiles matched the substring `v=`, so `?rev=1` looked
  fingerprinted and earned the 180-day immutable lifetime. It parses the
  query now and looks for a parameter actually named `v`.
- saveRenameProtocol() restored the button label from a server-rendered
  `'{{ _("save") }}'` string literal; a locale whose translation carries a
  quote would break the whole inline script. Uses the runtime helper and
  textContent instead.
- The add-user modal kept the "password optional" hint up after the role
  changed to one that requires a password. It is hidden with the label now.
2026-09-10 00:17:21 +07:00
hell 95bfd42ff8 fix: four defects in the code that came with #150-#152
- app.py handed every /static response `max-age=15552000, immutable`, but
  only the assets built through static_version() carry ?v=<mtime>. The
  favicon, the icons, qrcode.min.js, searchable-select.js, the vendored
  CodeMirror bundles and ReDoc keep one URL forever, so a redeploy could not
  reach a browser that had already cached them. Immutable is now limited to
  fingerprinted URLs; the rest get an hour plus a revalidation.
- WireGuardManager._apply_bw_limits ran `docker cp ... && docker exec ...`
  through run_sudo_command, which only privileges the head of the chain, so
  the exec ran as the unprivileged SSH user. Wrapped in one `sh -c`, the
  pattern tests/test_sudo_command_chains.py enforces.
- toggleConnection() restored the row only in the error path. When
  loadConnections() fails after a successful toggle, the row it was going to
  rebuild stays disabled with a spinning bulb. Restore now runs in `finally`.
- The add-user modal starts on role "none", where the password is optional,
  but the label said "required" until the select fired its change event.

Tests: the bandwidth chain joins the sudo-chain suite, and a new
tests/test_static_cache.py pins both cache lifetimes.
2026-09-10 00:17:21 +07:00