Commit graph

9 commits

Author SHA1 Message Date
rcourtman
3fa7356ff3 Improve auth timeout handling (related to #705) 2025-11-12 21:50:53 +00:00
Claude
cc46e3c6f0
Refactor update service to eliminate polling and race conditions
This commit implements a comprehensive refactoring of the update system
to address race conditions, redundant polling, and rate limiting issues.

Backend changes:
- Add job queue system to ensure only ONE update runs at a time
- Implement Server-Sent Events (SSE) for real-time update progress
- Add rate limiting to /api/updates/status (5-second minimum per client)
- Create SSE broadcaster for push-based status updates
- Integrate job queue with update manager for atomic operations
- Add comprehensive unit tests for queue and SSE components

Frontend changes:
- Update UpdateProgressModal to use SSE as primary mechanism
- Implement automatic fallback to polling when SSE unavailable
- Maintain backward compatibility with existing update flow
- Clean up SSE connections on component unmount

API changes:
- Add new endpoint: GET /api/updates/stream (SSE)
- Enhance /api/updates/status with client-based rate limiting
- Return cached status with appropriate headers when rate limited

Benefits:
- Eliminates 429 rate limit errors during updates
- Only one update job can run at a time (prevents race conditions)
- Real-time updates via SSE reduce unnecessary polling
- Graceful degradation to polling when SSE unavailable
- Better resource utilization and reduced server load

Testing:
- All existing tests pass
- New unit tests for queue and SSE functionality
- Integration tests verify complete update flow
2025-11-11 09:33:05 +00:00
rcourtman
de10ec949e Fix CRITICAL bug: UpdateProgressModal polling never started (fixes #671)
ROOT CAUSE: The onMount hook checked props.isOpen, but onMount only runs ONCE
when the component first mounts. Since UpdateProgressModal mounts when the app
loads (before the user clicks "Apply Update"), props.isOpen is false at mount
time, so polling never initializes.

When the user later clicks "Apply Update" and props.isOpen becomes true, onMount
doesn't re-run, leaving the modal in a broken state with no polling, no restart
detection, and no auto-reload - exactly what users reported (stuck for 30+ mins).

SOLUTION: Changed from onMount to createEffect watching props.isOpen. Now:
- Polling starts immediately when the modal opens (user clicks "Apply Update")
- Polling stops when the modal closes (cleanup)
- The entire update flow works as designed

This was the ACTUAL bug - the previous commits (global watcher, fallback polling)
were helpful additions but didn't fix the root cause.
2025-11-08 23:26:55 +00:00
rcourtman
c004c4517f Bulletproof the update auto-refresh with fallback mechanisms (related to #671)
After the initial fix, added multiple layers of reliability to ensure updates
ALWAYS auto-refresh, even in edge cases:

1. Fallback polling: GlobalUpdateProgressWatcher now polls /api/updates/status
   every 5 seconds as a safety net in case WebSocket events are dropped, missed,
   or the tab connects mid-update. This ensures tabs that join late or have
   WebSocket issues still detect in-progress updates.

2. Manual reload button: Added "Reload Now" button in UpdateProgressModal that
   appears after 5+ health check attempts during restart. Gives users an escape
   hatch if auto-reload is delayed (slow DNS, reverse proxy issues, etc.).

3. Already protected: Modal close button only shows when update is complete,
   preventing users from accidentally closing it mid-update.

These changes address all failure modes identified:
- Tabs without WebSocket: covered by polling fallback
- Tabs joining mid-update: covered by polling fallback
- Health check delays: covered by manual reload button
- User accidentally closing modal: already prevented

The combination of WebSocket events (primary), polling (fallback), health checks
(restart detection), and manual reload (escape hatch) should make this bulletproof.
2025-11-08 23:19:51 +00:00
rcourtman
5898cb81be Fix update modal hanging indefinitely after completion (related to #628)
When updates complete quickly, the status API may return 'completed' before
the frontend detects the 'restarting' phase. This left users staring at a
frozen modal with no feedback, requiring manual page refresh.

Changes:
- When status is 'completed', immediately check /api/health
- If backend is healthy, reload the page to get new version
- If health check fails, assume restart in progress and start health polling
- Ensures users always get reloaded to the new version automatically

This fixes the UX issue reported in discussion #628 where the update modal
appeared frozen indefinitely despite successful update completion.
2025-11-07 08:11:52 +00:00
rcourtman
6eb1a10d9b Refactor: Code cleanup and localStorage consolidation
This commit includes comprehensive codebase cleanup and refactoring:

## Code Cleanup
- Remove dead TypeScript code (types/monitoring.ts - 194 lines duplicate)
- Remove unused Go functions (GetClusterNodes, MigratePassword, GetClusterHealthInfo)
- Clean up commented-out code blocks across multiple files
- Remove unused TypeScript exports (helpTextClass, private tag color helpers)
- Delete obsolete test files and components

## localStorage Consolidation
- Centralize all storage keys into STORAGE_KEYS constant
- Update 5 files to use centralized keys:
  * utils/apiClient.ts (AUTH, LEGACY_TOKEN)
  * components/Dashboard/Dashboard.tsx (GUEST_METADATA)
  * components/Docker/DockerHosts.tsx (DOCKER_METADATA)
  * App.tsx (PLATFORMS_SEEN)
  * stores/updates.ts (UPDATES)
- Benefits: Single source of truth, prevents typos, better maintainability

## Previous Work Committed
- Docker monitoring improvements and disk metrics
- Security enhancements and setup fixes
- API refactoring and cleanup
- Documentation updates
- Build system improvements

## Testing
- All frontend tests pass (29 tests)
- All Go tests pass (15 packages)
- Production build successful
- Zero breaking changes

Total: 186 files changed, 5825 insertions(+), 11602 deletions(-)
2025-11-04 21:50:46 +00:00
rcourtman
6475ba8374 fix: Update test version fallback and fix lint warnings #64 2025-10-13 15:50:23 +00:00
rcourtman
ee96d11194 fix: Update modal now properly detects when update completes
The update progress modal was stuck showing 'initializing' even after the
backend restarted and websocket reconnected. Users could see the connection
status badge reconnecting behind the modal, but the modal never cleared.

Now the modal:
- Watches websocket connection status during update
- Detects when backend disconnects and reconnects
- Verifies health after reconnection
- Automatically reloads the page when update is complete
- Shows clearer messaging about restart progress
2025-10-12 18:25:38 +00:00
rcourtman
f46ff1792b Fix settings security tab navigation 2025-10-11 23:29:47 +00:00