Commit Graph

42 Commits

Author SHA1 Message Date
Jason Staack
144fb8b32d fix: resolve React Compiler lint errors
- WifiPanel: revert useEffect back to useState initializer (avoids
  synchronous setState in effect)
- Device detail: avoid Date.now() during render for push alert check

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 23:34:58 -05:00
Jason Staack
874542f802 fix: mount RollbackAlert, fix WifiPanel useEffect, remove unused PoolPanel prop
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:17:07 -05:00
Jason Staack
f49f5f739b fix: remove dead code (toast stubs, unused Redis key, tunnel manager fields)
- Remove 7 no-op exported stubs from toast.tsx (ToastProvider, ToastViewport,
  Toast, ToastTitle, ToastDescription, ToastClose, useToasts) — nothing imports them
- Remove fwFailKey variable and its Set() call from worker.go — the
  firmware:check-failed Redis key was never read anywhere
- Remove unused deviceStore and credCache fields from tunnel.Manager struct
  and drop corresponding parameters from NewManager(); update call site in
  main.go and all test usages

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:12:56 -05:00
Jason Staack
461bfcd89b fix: update version string from v8.0 to v9.6
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 23:10:21 -05:00
Jason Staack
409fb000b5 fix(a11y): add focus trap, Escape key, and dialog role to mobile sidebar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:10:32 -05:00
Jason Staack
42216ea326 fix(a11y): add keyboard support and aria-expanded to audit log rows
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:10:17 -05:00
Jason Staack
2e9584d2c7 fix(a11y): add keyboard nav to fleet table rows, fix ARIA, add scope
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:10:08 -05:00
Jason Staack
c0f6f84677 fix(a11y): add aria-live to password strength, role=meter to signal bar
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 21:09:48 -05:00
Jason Staack
608a42a376 fix(a11y): add role=alert to form error messages 2026-03-15 21:08:36 -05:00
Jason Staack
bf2002f8be fix(a11y): restore keyboard access to forgot-password, add role=alert to error 2026-03-15 21:08:19 -05:00
Jason Staack
9ce4c9aa60 fix(a11y): add aria-label to collapsed sidebar links, aria-current to active 2026-03-15 21:08:09 -05:00
Jason Staack
b07659e2c2 fix(a11y): add aria-labels to unlabeled controls and table headers 2026-03-15 21:08:02 -05:00
Jason Staack
9ad5438860 fix(a11y): add focus ring to dialog close button 2026-03-15 21:07:01 -05:00
Jason Staack
5d3463b633 fix(a11y): improve color contrast for WCAG AA compliance
- Darken --text-muted in light mode (6.2:1 on white)
- Brighten --text-muted in dark mode (4.6:1 on surface)
- Split status colors: darker variants for light mode, bright for dark
- All text colors now meet WCAG AA 4.5:1 minimum

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 21:05:51 -05:00
Jason Staack
24cf57d2ca fix: remove unused WirelessIssue type import
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-15 20:09:56 -05:00
Jason Staack
09a3fdcfeb feat: add APs Needing Attention dashboard card
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 20:06:25 -05:00
Jason Staack
5ed98f043e feat: add wireless-issues API client functions
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 20:04:57 -05:00
Jason Staack
3600a8e0ce feat: wire WirelessTab into device detail monitor section
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 20:00:07 -05:00
Jason Staack
fb3669f9ac fix(lint): resolve remaining ESLint errors (unused vars, any types, react-refresh)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:50:50 -05:00
Jason Staack
9fcabb22d3 fix(lint): resolve ESLint errors in frontend components and tests
- Remove unused imports: Mock, VariableDef, within, Badge, deviceGroupsApi, devicesApi
- Fix Unexpected any in AlertRulesPage catch block (use unknown + type assertion)
- Suppress react-refresh/only-export-components for getPasswordScore helper
- Add Link mock to LoginPage test and useAuth.getState() stub for navigation test
- Fix DeviceList tests to use data-testid selectors and correct empty state text
  (component renders dual mobile/desktop views causing multiple-element errors)
- Remove unused container destructuring from TemplatePushWizard test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 22:20:07 -05:00
Jason Staack
2ad0367c91 fix(vpn): backport VPN fixes from production debugging
- Fix _commit_and_sync infinite recursion
- Use admin session for subnet_index allocation (bypass RLS)
- Auto-set VPN endpoint from CORS_ORIGINS hostname
- Remove server address field from VPN setup UI
- Add DELETE endpoint and button for VPN config removal
- Add wg-reload watcher for reliable config hot-reload via wg syncconf
- Add wg_status.json writer for live peer handshake status in UI
- Per-tenant SNAT for poller-to-device routing through VPN
- Restrict VPN→eth0 forwarding to Docker networks only (block exit node abuse)
- Use 10.10.0.0/16 allowed-address in RouterOS commands
- Fix structlog event= conflict (use audit=True)
- Export backup_scheduler proxy for firmware/upgrade imports
2026-03-14 20:59:14 -05:00
Jason Staack
cfa18a4095 refactor: rename remaining mikrotik references to tod across CI, helm, frontend, and observability
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 12:03:51 -05:00
Jason Staack
bb546cf4bc fix: hide first-run credential hint in production builds 2026-03-14 09:56:01 -05:00
Jason Staack
970501e453 feat: implement Remote WinBox worker, API, frontend integration, OpenBao persistence, and supporting docs 2026-03-14 09:05:14 -05:00
Jason Staack
ed3ad8eb17 chore: update about page to v9.6 and Dockerfile to Go 1.25
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 06:54:08 -05:00
Jason Staack
be41add4e9 feat(08-02): add snapshot download button to config history timeline
- Add SnapshotResponse interface and getSnapshot API method
- Add deviceName prop to ConfigHistorySection
- Add download handler that fetches snapshot and triggers .rsc file download
- Add Download icon button on each timeline entry with stopPropagation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:23:55 -05:00
Jason Staack
2cf426fa63 feat(08-01): wire diff viewer into config history timeline
- Add click handlers to timeline entries to open diff viewer
- Render DiffViewer inline above timeline when snapshot selected
- Add hover state and cursor-pointer to timeline entries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:20:52 -05:00
Jason Staack
dda00fbd23 feat(08-01): add diff viewer component and API client
- Add DiffResponse interface and getDiff method to configHistoryApi
- Create DiffViewer component with unified diff rendering
- Green highlighting for added lines, red for removed lines
- Blue styling for hunk headers, loading skeleton, error state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:20:24 -05:00
Jason Staack
36861fffea feat(07-01): wire ConfigHistorySection into device detail page
- Import and render ConfigHistorySection below Interface Utilization
- Configuration history now visible on device overview tab

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:12:16 -05:00
Jason Staack
6bd24517ba feat(07-01): add config history API client and timeline component
- Add ConfigChangeEntry interface and configHistoryApi.list() to api.ts
- Create ConfigHistorySection with timeline, loading skeleton, and empty state
- Poll every 60s via TanStack Query refetchInterval

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 23:11:46 -05:00
Jason Staack
d2471278ab feat(frontend): integrate WinBox and SSH buttons into device page
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 15:45:14 -05:00
Jason Staack
b76fdb3240 feat(frontend): add SSH terminal component with xterm.js 2026-03-12 15:43:31 -05:00
Jason Staack
b3b2f87beb feat(frontend): add WinBox tunnel button component 2026-03-12 15:43:03 -05:00
Jason Staack
79afd2a1ad feat(frontend): add remote access API client methods 2026-03-12 15:42:42 -05:00
Jason Staack
e5a9758f58 chore(frontend): add xterm.js dependencies for SSH terminal 2026-03-12 15:42:29 -05:00
Cog
57e754bb27 fix: implement vault key decryption on login + fix token refresh via cookie
Three bugs fixed:

1. Phase 30 (auth.ts): After SRP login the encrypted_key_set was returned
   from the server but the vault key and RSA private key were never unwrapped
   with the AUK. keyStore.getVaultKey() was always null, causing Tier 1
   config-backup diffs to crash with a TypeError.
   Fix: unwrap vault key and private key using crypto.subtle.unwrapKey after
   successful SRP verification. Non-fatal: warns to console if decryption
   fails so login always succeeds.

2. Token refresh (auth.py): The /refresh endpoint required refresh_token in
   the request body, but the frontend never stored or sent it. After the 15-
   minute access token TTL, all authenticated API calls would fail silently
   because the interceptor sent an empty body and received 422 (not 401),
   so the retry loop never fired.
   Fix: login/srpVerify now set an httpOnly refresh_token cookie scoped to
   /api/auth/refresh. The refresh endpoint now accepts the token from either
   cookie (preferred) or body (legacy). Logout clears both cookies.
   RefreshRequest.refresh_token is now Optional to allow empty-body calls.

3. Silent token rotation: the /refresh endpoint now also rotates the refresh
   token cookie on each use (issues a fresh token), reducing the window for
   stolen refresh token replay.
2026-03-12 14:05:40 -05:00
Jason Staack
d0548bec86 fix(crypto): use 27 base-30 chars for Secret Key to prevent data loss
The Secret Key encoder used 26 base-30 characters which can only
represent 30^26 ≈ 2^127.58 values. Since the key is 128 bits,
~25% of generated keys silently lost their high bits during
formatting, making the Emergency Kit key unable to reconstruct
the original bytes on a new browser.

Changed KEY_CHAR_LENGTH from 26 to 27 (30^27 > 2^128). Parser
accepts both old 26-char and new 27-char keys for backward
compatibility. Format: A3-XXXXXX-XXXXXX-XXXXXX-XXXXXX-XXX

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-10 14:04:24 -05:00
Jason Staack
394be01145 fix(frontend): default ping/traceroute target to 8.8.8.8
The target input showed "8.8.8.8" as placeholder text but the actual
value was empty. Clicking Ping/Traceroute silently returned because
the empty target guard fired. Users saw the placeholder and assumed
the tool was broken.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:57:10 -05:00
Jason Staack
a3cc35c4b7 fix(frontend): generate Emergency Kit PDF client-side with actual Secret Key
The server-generated PDF had a placeholder for the Secret Key that was
never filled in client-side, making the Emergency Kit useless. Users
who relied on it could not recover their Secret Key on new devices.

Now generates the PDF entirely client-side via browser print dialog,
with the real Secret Key embedded. No server round-trip, key never
leaves the browser.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:47:50 -05:00
Jason Staack
6c7dfe02f5 fix(frontend): show Secret Key field when IndexedDB key is stale
When a user logs in from a browser with an outdated Secret Key in
IndexedDB (e.g. after server rebuild/re-enrollment), the SRP handshake
fails with 401 but the Secret Key input field was never shown — leaving
the user stuck with no way to enter their current key.

Now detects stale-key 401s and prompts for manual Secret Key entry.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:30:09 -05:00
Jason Staack
32965857e7 fix: wire up empty-state Add Device button to open dialog
The FleetTable empty state navigated with ?add=true but the devices page
never read that param. Now it opens the AddDeviceForm when add=true is
in the search params.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-09 21:12:01 -05:00
Jason Staack
b840047e19 feat: The Other Dude v9.0.1 — full-featured email system
ci: add GitHub Pages deployment workflow for docs site

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-08 19:30:44 -05:00