5 Commits
Author SHA1 Message Date
agessaman 52822feeda fix(web_viewer): make contact column sorting work from the keyboard
The sort headers were plain <th> cells with a click handler, so keyboard and screen reader users couldn't sort. Each header label is now a <button> inside the <th>, the active column carries aria-sort, and the focus ring follows the header text color. The Hops info tip sits outside the sort button, so opening it no longer re-sorts the table.
2026-10-17 16:56:24 -07:00
Adam GessamanandGerard Hickey dd546a12ed feat(web_viewer): split anonymous read access from an admin session (#300)
* feat(web_viewer): split anonymous read access from an admin session

When web_viewer_password is set, Dashboard, Contacts, and Mesh Graph are readable without logging in, along with the read-only APIs they need. Radio, Config, Logs, every mutating API, /api/channels, and the live Socket.IO streams require an admin session, since those can expose channel keys and decrypted traffic. The Realtime page still renders publicly but shows "Admin login required" instead of connecting. Anything not on the public allowlist fails closed.

Login now compares HMAC digests with hmac.compare_digest and clears the session before setting authenticated_admin. Logout clears the whole session and returns to the dashboard.

Co-authored-by: Gerard Hickey <hickey@kinetic-compute.com>

* fix(web_viewer): stop Contacts redeclaring IS_ADMIN and hide banner admin buttons

contacts.html declared its own top-level const IS_ADMIN, but base.html already declares one in an earlier script on the same page. Classic scripts share one global lexical scope, so the redeclaration threw a SyntaxError and the whole Contacts script never ran, leaving the table on its loading placeholder in every mode. Contacts now uses the global from base.html.

The zombie-recover and clear-offline banner buttons were still rendered for anonymous viewers even though their /api/admin/* endpoints now return 401. They are gated on the same auth_enabled/is_admin check as the other admin controls.

* fix(web_viewer): drop sockets on logout, block off-site login redirects, allow HEAD on public paths

Logging out cleared the HTTP session but left that login's Socket.IO connections open, and a socket keeps the session it connected with, so a /realtime tab kept streaming packets, decrypted messages, and logs after logout. Each login now gets an admin_login_id, sockets record it on connect, and logout disconnects the sockets opened under that login. Other admin logins stay connected.

The login next check passed ///host and /\host because urlparse sees no netloc in either, but browsers resolve both as another origin. Next targets are now rejected if they start with //, contain a backslash, or contain a tab or newline. This check predates the admin split.

Public pages and GET APIs only accepted literal GET, so HEAD (and OPTIONS) on them returned 401 and made uptime checks report the viewer as down. They now accept HEAD and OPTIONS too; admin paths are unchanged.

* docs(changelog): note the login redirect fix

* fix(web_viewer): pass IS_ADMIN through a body data attribute so ESLint can parse base.html

The frontend lint job failed with "Parsing error: Unexpected token {" because base.html's inline script assigned IS_ADMIN from a raw Jinja expression, which isn't valid JS until rendered. The flag now lives in data-is-admin on <body> and the script reads it from document.body.dataset. I also dropped the unused AUTH_ENABLED constant and declared IS_ADMIN as a readonly ESLint global, since contacts.html reads it from base.html.

---------

Co-authored-by: Gerard Hickey <hickey@kinetic-compute.com>
2026-09-23 20:47:58 -07:00
agessaman bdde4efe00 fix(web_viewer): keep hops info tip out of sort icon updates
Scope contacts header sort styling/JS to .sort-icon so the path-encoding
(i) tip is not rewritten into a sort chevron.
2026-08-06 21:14:26 -07:00
Littleaton 9db865131a possible filterting by node type/details/etc
also added sort by bytes.
2026-08-05 14:33:19 -06:00
agessaman 092be2d5bf feat(contacts): implement pagination, search, and sorting for contacts API
- Enhanced the `/api/contacts` endpoint to support pagination, allowing users to specify `page` and `page_size` parameters.
- Added search functionality to filter contacts based on a search term.
- Implemented sorting options for the contacts list, enabling users to sort by various fields.
- Updated the frontend to reflect these changes, including a new pagination UI and improved loading states.
- Introduced caching for multibyte hop chunks to optimize performance when retrieving contact badge evidence.
- Added tests to ensure the new features work as expected and maintain existing functionality.
2026-07-28 12:36:56 -07:00