activity
Signed-off-by: Omar Sánchez Pizarro <omar.sanchez@pistacero.net>
This commit is contained in:
324
KEEPALIVE_SYSTEM.md
Normal file
324
KEEPALIVE_SYSTEM.md
Normal file
@@ -0,0 +1,324 @@
|
||||
# Sistema de Keepalive y Seguimiento de Usuarios Activos
|
||||
|
||||
## 📝 Descripción
|
||||
|
||||
El sistema de keepalive permite rastrear en tiempo real qué usuarios están activos y cuándo fue su última actividad. Utiliza el WebSocket existente para mantener una conexión persistente y enviar heartbeats periódicos.
|
||||
|
||||
## 🎯 Características
|
||||
|
||||
### Backend
|
||||
|
||||
1. **WebSocket con Heartbeat**
|
||||
- Envío automático de ping cada 60 segundos
|
||||
- Detección de conexiones inactivas
|
||||
- Cierre automático de conexiones sin respuesta
|
||||
|
||||
2. **Seguimiento de Actividad**
|
||||
- Registro de última actividad en sesiones
|
||||
- Estado activo/inactivo por usuario
|
||||
- Timeout de inactividad: 5 minutos
|
||||
|
||||
3. **Base de Datos**
|
||||
- Campo `lastActivity` en sesiones
|
||||
- Campo `isActive` para marcar usuarios conectados
|
||||
- Índices optimizados para consultas rápidas
|
||||
|
||||
4. **Endpoints**
|
||||
- `GET /api/users/active` - Obtener usuarios activos en tiempo real
|
||||
- `GET /api/admin/sessions` - Obtener todas las sesiones con estado de actividad
|
||||
- Ambos requieren autenticación (el segundo requiere rol admin)
|
||||
|
||||
### Frontend
|
||||
|
||||
1. **Heartbeat Automático**
|
||||
- Envío de heartbeat cada 30 segundos
|
||||
- Respuesta automática a pings del servidor
|
||||
|
||||
2. **Detección de Actividad**
|
||||
- Seguimiento de clicks, teclas, scroll y movimientos del ratón
|
||||
- Throttling de 10 segundos para no sobrecargar el servidor
|
||||
- Envío automático de eventos de actividad
|
||||
|
||||
3. **Componente de Usuarios Activos**
|
||||
- Lista de usuarios conectados en tiempo real
|
||||
- Indicadores de estado (activo/inactivo)
|
||||
- Actualización automática cada 30 segundos
|
||||
- Actualización en tiempo real vía WebSocket
|
||||
|
||||
## 🚀 Uso
|
||||
|
||||
### Ver Usuarios Activos
|
||||
|
||||
Los administradores pueden ver los usuarios activos en:
|
||||
- **Dashboard principal** - Componente "Usuarios Activos" en la parte superior
|
||||
- **Vista de Sesiones** - `/sessions` - Muestra todas las sesiones con estado de conexión
|
||||
- **Endpoint API**: `GET /api/users/active`
|
||||
|
||||
### Ver Estado de Sesiones
|
||||
|
||||
La vista de **Sesiones** (`/sessions`) ahora muestra información detallada sobre el estado de cada sesión:
|
||||
|
||||
**Estadísticas mejoradas:**
|
||||
- Total de sesiones
|
||||
- Sesiones válidas (no expiradas)
|
||||
- 🟢 Sesiones conectadas (con actividad reciente < 5 min)
|
||||
- 🟡 Sesiones inactivas (válidas pero sin actividad)
|
||||
- 🔴 Sesiones expiradas
|
||||
- Usuarios únicos
|
||||
|
||||
**Por cada sesión:**
|
||||
- Estado de validez: ✅ Válida / 🔴 Expirada
|
||||
- Estado de conexión: 🟢 Conectado / 🟡 Inactivo
|
||||
- Última actividad (relativa y absoluta)
|
||||
- Información del dispositivo
|
||||
- Token y fechas de creación/expiración
|
||||
|
||||
### Estructura de Respuesta
|
||||
|
||||
**GET /api/users/active:**
|
||||
```json
|
||||
{
|
||||
"activeUsers": [
|
||||
{
|
||||
"username": "admin",
|
||||
"role": "admin",
|
||||
"status": "active",
|
||||
"lastActivity": "2026-01-21T10:30:00.000Z",
|
||||
"connectedViaWebSocket": true
|
||||
},
|
||||
{
|
||||
"username": "user1",
|
||||
"role": "user",
|
||||
"status": "inactive",
|
||||
"lastActivity": "2026-01-21T10:25:00.000Z",
|
||||
"connectedViaWebSocket": false,
|
||||
"deviceInfo": {
|
||||
"browser": "Chrome",
|
||||
"os": "Windows"
|
||||
}
|
||||
}
|
||||
],
|
||||
"total": 2,
|
||||
"timestamp": "2026-01-21T10:35:00.000Z"
|
||||
}
|
||||
```
|
||||
|
||||
**GET /api/admin/sessions:**
|
||||
```json
|
||||
{
|
||||
"sessions": [
|
||||
{
|
||||
"token": "abc123...",
|
||||
"username": "admin",
|
||||
"fingerprint": "xyz...",
|
||||
"deviceInfo": {
|
||||
"browser": "Chrome",
|
||||
"browserVersion": "120.0",
|
||||
"os": "Linux",
|
||||
"ip": "192.168.1.100"
|
||||
},
|
||||
"createdAt": "2026-01-21T09:00:00.000Z",
|
||||
"expiresAt": "2026-01-22T09:00:00.000Z",
|
||||
"lastActivity": "2026-01-21T10:30:00.000Z",
|
||||
"isActive": true,
|
||||
"isExpired": false
|
||||
}
|
||||
],
|
||||
"stats": {
|
||||
"total": 10,
|
||||
"active": 8,
|
||||
"expired": 2,
|
||||
"connected": 5,
|
||||
"inactive": 3,
|
||||
"byUser": {
|
||||
"admin": 3,
|
||||
"user1": 2
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 🔧 Configuración
|
||||
|
||||
### Timeouts y Intervalos
|
||||
|
||||
**Backend** (`web/backend/services/websocket.js`):
|
||||
```javascript
|
||||
// Tiempo de inactividad para marcar como inactivo (5 minutos)
|
||||
const INACTIVE_TIMEOUT = 5 * 60 * 1000;
|
||||
|
||||
// Intervalo para limpiar conexiones inactivas (1 minuto)
|
||||
const CLEANUP_INTERVAL = 60 * 1000;
|
||||
```
|
||||
|
||||
**Frontend** (`web/dashboard/src/App.vue`):
|
||||
```javascript
|
||||
// Heartbeat cada 30 segundos
|
||||
heartbeatInterval = setInterval(() => { ... }, 30000);
|
||||
|
||||
// Throttle de actividad: 10 segundos
|
||||
activityThrottleTimeout = setTimeout(() => { ... }, 10000);
|
||||
```
|
||||
|
||||
## 📊 Estados de Usuario
|
||||
|
||||
1. **Active** (Activo)
|
||||
- Usuario conectado vía WebSocket
|
||||
- Actividad reciente (< 5 minutos)
|
||||
- Indicador verde
|
||||
|
||||
2. **Inactive** (Inactivo)
|
||||
- Usuario conectado pero sin actividad reciente (> 5 minutos)
|
||||
- Indicador amarillo
|
||||
|
||||
3. **Offline** (Desconectado)
|
||||
- Sin conexión WebSocket
|
||||
- No aparece en la lista de usuarios activos
|
||||
|
||||
## 🔄 Flujo de Datos
|
||||
|
||||
### Conexión WebSocket
|
||||
|
||||
```
|
||||
Cliente conecta → Servidor valida token → Actualiza lastActivity
|
||||
↓
|
||||
Servidor envía confirmación de conexión
|
||||
↓
|
||||
Cliente inicia heartbeat cada 30s
|
||||
↓
|
||||
Servidor responde con pong
|
||||
↓
|
||||
Se actualiza lastActivity en DB
|
||||
```
|
||||
|
||||
### Eventos de Usuario
|
||||
|
||||
```
|
||||
Usuario interactúa (click, tecla, etc.)
|
||||
↓
|
||||
Cliente envía evento de actividad (throttled)
|
||||
↓
|
||||
Servidor actualiza lastActivity
|
||||
↓
|
||||
Broadcast de cambio de estado a otros usuarios
|
||||
```
|
||||
|
||||
### Desconexión
|
||||
|
||||
```
|
||||
Cliente cierra conexión o timeout
|
||||
↓
|
||||
Servidor marca isActive = false en DB
|
||||
↓
|
||||
Broadcast de estado offline a otros usuarios
|
||||
```
|
||||
|
||||
## 🎨 Componente ActiveUsers
|
||||
|
||||
Ubicación: `web/dashboard/src/components/ActiveUsers.vue`
|
||||
|
||||
### Props
|
||||
|
||||
Ninguna (componente standalone)
|
||||
|
||||
### Eventos Escuchados
|
||||
|
||||
- `user-status-change` - Cambios de estado de usuarios vía WebSocket
|
||||
|
||||
### Características
|
||||
|
||||
- **Actualización automática**: cada 30 segundos
|
||||
- **Actualización en tiempo real**: vía eventos WebSocket
|
||||
- **Indicadores visuales**:
|
||||
- 🟢 Verde: usuario activo
|
||||
- 🟡 Amarillo: usuario inactivo
|
||||
- 📶 Icono de señal: conectado vía WebSocket
|
||||
- **Badges de rol**: admin/user
|
||||
- **Timestamp relativo**: "hace X minutos"
|
||||
|
||||
## 🔐 Seguridad
|
||||
|
||||
- Solo usuarios autenticados pueden ver usuarios activos
|
||||
- El token se valida en cada conexión WebSocket
|
||||
- Las sesiones expiran automáticamente según configuración
|
||||
- Los heartbeats mantienen la sesión activa
|
||||
|
||||
## 📈 Escalabilidad
|
||||
|
||||
El sistema está diseñado para manejar múltiples usuarios concurrentes:
|
||||
|
||||
1. **Throttling de actividad**: evita spam de eventos
|
||||
2. **Índices en MongoDB**: consultas optimizadas
|
||||
3. **Limpieza periódica**: cierre de conexiones inactivas
|
||||
4. **TTL en sesiones**: eliminación automática de sesiones expiradas
|
||||
|
||||
## 🐛 Debugging
|
||||
|
||||
Para ver logs de WebSocket en la consola del navegador:
|
||||
|
||||
```javascript
|
||||
// En la consola del navegador
|
||||
localStorage.setItem('debug', 'websocket');
|
||||
```
|
||||
|
||||
Logs del servidor:
|
||||
```
|
||||
Cliente WebSocket conectado: username (role)
|
||||
Cliente WebSocket desconectado: username
|
||||
Usuario inactivo detectado: username
|
||||
Cerrando conexión inactiva: username
|
||||
```
|
||||
|
||||
## 🖥️ Vista de Sesiones Mejorada
|
||||
|
||||
La vista de Sesiones (`/sessions`) ahora incluye información completa sobre el estado de actividad:
|
||||
|
||||
### Estadísticas Ampliadas
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────┐
|
||||
│ [Total: 10] [Válidas: 8] [🟢 Conectadas: 5] │
|
||||
│ [🟡 Inactivas: 3] [🔴 Expiradas: 2] [Usuarios: 3] │
|
||||
└─────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### Tabla de Sesiones
|
||||
|
||||
Cada fila muestra:
|
||||
- **Usuario**: nombre del usuario
|
||||
- **Dispositivo**: navegador, OS, IP
|
||||
- **Última Actividad**: tiempo relativo (ej: "Hace 2 minutos") + timestamp exacto
|
||||
- **Token**: primeros 16 caracteres
|
||||
- **Creada**: timestamp de creación
|
||||
- **Expira**: timestamp de expiración
|
||||
- **Estado**: doble indicador
|
||||
- ✅ Válida / 🔴 Expirada
|
||||
- 🟢 Conectado / 🟡 Inactivo (solo si válida)
|
||||
- **Acciones**: botón para eliminar sesión
|
||||
|
||||
### Diferencias de Estado
|
||||
|
||||
**✅ Válida + 🟢 Conectado:**
|
||||
- Sesión no expirada
|
||||
- Usuario con actividad reciente (< 5 minutos)
|
||||
- Probablemente conectado vía WebSocket
|
||||
|
||||
**✅ Válida + 🟡 Inactivo:**
|
||||
- Sesión no expirada
|
||||
- Usuario sin actividad reciente (> 5 minutos)
|
||||
- WebSocket desconectado o usuario inactivo
|
||||
|
||||
**🔴 Expirada:**
|
||||
- Sesión pasada la fecha de expiración
|
||||
- Se eliminará automáticamente por TTL de MongoDB
|
||||
- No se puede eliminar manualmente (botón deshabilitado)
|
||||
|
||||
## 📝 Notas Adicionales
|
||||
|
||||
- El sistema es compatible con múltiples pestañas/dispositivos por usuario
|
||||
- Cada conexión WebSocket se rastrea independientemente
|
||||
- Los heartbeats se reinician automáticamente si se reconecta
|
||||
- La actividad del usuario se detecta de forma pasiva (no intrusiva)
|
||||
- La vista de Sesiones se actualiza manualmente (botón "🔄 Actualizar")
|
||||
- Los usuarios activos se actualizan automáticamente cada 30 segundos
|
||||
|
||||
Reference in New Issue
Block a user