Files
claude-code-monitor/backend/app/websocket.py
felix.zoesch f6ef7ff5d3 Initial commit: Claude Code Monitor v1.0.0
Complete real-time monitoring dashboard for Claude Code

Features:
- FastAPI backend with REST API and WebSocket
- React + TypeScript frontend with dark theme
- SQLite database for event storage
- 6 hook scripts for Claude Code events
- Docker deployment setup
- Real-time event tracking and statistics
- Event filtering (ALL, TOOLS, AGENTS, PROMPTS, SESSIONS)
- Connection status indicator
- Reset stats functionality

Tech Stack:
- Backend: Python 3.11, FastAPI, SQLAlchemy, WebSockets
- Frontend: React 18, TypeScript, Vite
- Database: SQLite
- Deployment: Docker, Docker Compose

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-15 09:49:06 +01:00

90 lines
3.0 KiB
Python

"""WebSocket connection manager for real-time updates."""
from fastapi import WebSocket
from typing import List, Dict, Any
import json
import asyncio
import logging
logger = logging.getLogger(__name__)
class ConnectionManager:
"""Manages WebSocket connections for broadcasting events."""
def __init__(self):
self.active_connections: List[WebSocket] = []
async def connect(self, websocket: WebSocket):
"""Accept a new WebSocket connection."""
await websocket.accept()
self.active_connections.append(websocket)
logger.info(f"New WebSocket connection. Total connections: {len(self.active_connections)}")
def disconnect(self, websocket: WebSocket):
"""Remove a WebSocket connection."""
if websocket in self.active_connections:
self.active_connections.remove(websocket)
logger.info(f"WebSocket disconnected. Total connections: {len(self.active_connections)}")
async def send_personal_message(self, message: Dict[str, Any], websocket: WebSocket):
"""Send a message to a specific connection."""
try:
await websocket.send_text(json.dumps(message))
except Exception as e:
logger.error(f"Error sending personal message: {e}")
async def broadcast(self, message: Dict[str, Any]):
"""Broadcast a message to all connected clients."""
if not self.active_connections:
return
# Convert message to JSON string
message_str = json.dumps(message)
# Send to all connections
disconnected = []
for connection in self.active_connections:
try:
await connection.send_text(message_str)
except Exception as e:
logger.error(f"Error broadcasting to connection: {e}")
disconnected.append(connection)
# Clean up disconnected connections
for connection in disconnected:
self.disconnect(connection)
async def broadcast_event(self, event_data: Dict[str, Any], statistics_data: Dict[str, Any]):
"""Broadcast a new event with updated statistics."""
message = {
"type": "event_created",
"event": event_data,
"statistics": statistics_data,
}
await self.broadcast(message)
async def broadcast_stats_reset(self):
"""Broadcast a stats reset notification."""
message = {
"type": "stats_reset",
"message": "Statistics have been reset",
}
await self.broadcast(message)
async def broadcast_stats_update(self, statistics_data: Dict[str, Any]):
"""Broadcast updated statistics."""
message = {
"type": "stats_updated",
"statistics": statistics_data,
}
await self.broadcast(message)
def get_connection_count(self) -> int:
"""Get the number of active connections."""
return len(self.active_connections)
# Global connection manager instance
manager = ConnectionManager()