132 lines
4.6 KiB
Markdown
132 lines
4.6 KiB
Markdown
# WebSocket Live Log Viewer Fix
|
|
|
|
## Problem
|
|
|
|
The live log viewer in the Cerberus Dashboard was always showing "Disconnected" status even when it should connect to the WebSocket endpoint.
|
|
|
|
## Root Cause
|
|
|
|
The `LiveLogViewer` component was setting `isConnected=true` immediately when the component mounted, before the WebSocket actually established a connection. This premature status update masked the real connection state and made it impossible to see whether the WebSocket was actually connecting.
|
|
|
|
## Solution
|
|
|
|
Modified the WebSocket connection flow to properly track connection lifecycle:
|
|
|
|
### Frontend Changes
|
|
|
|
#### 1. API Layer (`frontend/src/api/logs.ts`)
|
|
|
|
- Added `onOpen?: () => void` callback parameter to `connectLiveLogs()`
|
|
- Added `ws.onopen` event handler that calls the callback when connection opens
|
|
- Enhanced logging for debugging:
|
|
- Log WebSocket URL on connection attempt
|
|
- Log when connection establishes
|
|
- Log close event details (code, reason, wasClean)
|
|
|
|
#### 2. Component (`frontend/src/components/LiveLogViewer.tsx`)
|
|
|
|
- Updated to use the new `onOpen` callback
|
|
- Initial state is now "Disconnected"
|
|
- Only set `isConnected=true` when `onOpen` callback fires
|
|
- Added console logging for connection state changes
|
|
- Properly cleanup and set disconnected state on unmount
|
|
|
|
#### 3. Tests (`frontend/src/components/__tests__/LiveLogViewer.test.tsx`)
|
|
|
|
- Updated mock implementation to include `onOpen` callback
|
|
- Fixed test expectations to match new behavior (initially Disconnected)
|
|
- Added proper simulation of WebSocket opening
|
|
|
|
### Backend Changes (for debugging)
|
|
|
|
#### 1. Auth Middleware (`backend/internal/api/middleware/auth.go`)
|
|
|
|
- Added `fmt` import for logging
|
|
- Detect WebSocket upgrade requests (`Upgrade: websocket` header)
|
|
- Log auth method used for WebSocket (cookie vs query param)
|
|
- Log auth failures with context
|
|
|
|
#### 2. WebSocket Handler (`backend/internal/api/handlers/logs_ws.go`)
|
|
|
|
- Added log on connection attempt received
|
|
- Added log when connection successfully established with subscriber ID
|
|
|
|
## How Authentication Works
|
|
|
|
The WebSocket endpoint (`/api/v1/logs/live`) is protected by the auth middleware, which supports three authentication methods (in order):
|
|
|
|
1. **Authorization header**: `Authorization: Bearer <token>`
|
|
2. **HttpOnly cookie**: `auth_token=<token>` (automatically sent by browser)
|
|
3. **Query parameter**: `?token=<token>`
|
|
|
|
For same-origin WebSocket connections from a browser, **cookies are sent automatically**, so the existing cookie-based auth should work. The middleware has been enhanced with logging to debug any auth issues.
|
|
|
|
## Testing
|
|
|
|
To test the fix:
|
|
|
|
1. **Build and Deploy**:
|
|
|
|
```bash
|
|
# Build Docker image
|
|
docker build -t charon:local .
|
|
|
|
# Restart containers
|
|
docker-compose -f docker-compose.local.yml down
|
|
docker-compose -f docker-compose.local.yml up -d
|
|
```
|
|
|
|
2. **Access the Application**:
|
|
- Navigate to the Security page
|
|
- Enable Cerberus if not already enabled
|
|
- The LiveLogViewer should appear at the bottom
|
|
|
|
3. **Check Connection Status**:
|
|
- Should initially show "Disconnected" (red badge)
|
|
- Should change to "Connected" (green badge) within 1-2 seconds
|
|
- Look for console logs:
|
|
- "Connecting to WebSocket: ws://..."
|
|
- "WebSocket connection established"
|
|
- "Live log viewer connected"
|
|
|
|
4. **Verify WebSocket in DevTools**:
|
|
- Open Browser DevTools → Network tab
|
|
- Filter by "WS" (WebSocket)
|
|
- Should see connection to `/api/v1/logs/live`
|
|
- Status should be "101 Switching Protocols"
|
|
- Messages tab should show incoming log entries
|
|
|
|
5. **Check Backend Logs**:
|
|
|
|
```bash
|
|
docker logs <charon-container> 2>&1 | grep -i websocket
|
|
```
|
|
|
|
Should see:
|
|
- "WebSocket connection attempt received"
|
|
- "WebSocket connection established successfully"
|
|
|
|
## Expected Behavior
|
|
|
|
- **Initial State**: "Disconnected" (red badge)
|
|
- **After Connection**: "Connected" (green badge)
|
|
- **Log Streaming**: Real-time security logs appear as they happen
|
|
- **On Error**: Badge turns red, shows "Disconnected"
|
|
- **Reconnection**: Not currently implemented (would require retry logic)
|
|
|
|
## Files Modified
|
|
|
|
- `frontend/src/api/logs.ts`
|
|
- `frontend/src/components/LiveLogViewer.tsx`
|
|
- `frontend/src/components/__tests__/LiveLogViewer.test.tsx`
|
|
- `backend/internal/api/middleware/auth.go`
|
|
- `backend/internal/api/handlers/logs_ws.go`
|
|
|
|
## Notes
|
|
|
|
- The fix properly implements the WebSocket lifecycle tracking
|
|
- All frontend tests pass
|
|
- Pre-commit checks pass (except coverage which is expected)
|
|
- The backend logging is temporary for debugging and can be removed once verified working
|
|
- SameSite=Strict cookie policy should work for same-origin WebSocket connections
|