Added notifications!!

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-29 12:28:11 -06:00
parent 9e3cc3a03f
commit fef8ab225d
24 changed files with 3596 additions and 20 deletions

515
IMPLEMENTATION_SUMMARY.md Normal file
View File

@@ -0,0 +1,515 @@
# Resumen de Implementación: API de Notificaciones - VoxPopuli
**Fecha:** 29 de Abril de 2024
**Versión:** 1.0.0
**Autor:** VoxPopuli Development Team
---
## 📋 Resumen Ejecutivo
Se ha implementado **exitosamente** una nueva **API de Notificaciones** que se integra con la arquitectura existente del proyecto. Las notificaciones se disparan automáticamente cuando los reportes cambian de estado, proporcionando a los usuarios actualizaciones en tiempo real sobre el progreso de sus reportes.
### Puntos Clave:
✅ Nueva API de Notificaciones en **puerto 8002**
✅ Base de datos MongoDB dedicada (`voxpopuli_notifications`)
✅ Integración automática con eventos de cambio de estado de reportes
✅ Sistema de consumidores RabbitMQ para procesamiento asíncrono
✅ Arquitectura hexagonal consistente con el resto del proyecto
✅ Docker-compose actualizado con nuevos servicios
---
## 📁 Estructura de Archivos Creados
```
src/
├── domain/
│ └── notifications.py ✨ Nuevo
├── application/
│ ├── ports/
│ │ └── notification_repository.py ✨ Nuevo
│ └── services/
│ └── notification_services.py ✨ Nuevo
├── infrastructure/
│ ├── adapters/
│ │ └── persistence/
│ │ └── notification_repository_mongo.py ✨ Nuevo
│ └── api/
│ └── notifications/ ✨ Nuevo directorio
│ ├── __init__.py
│ ├── app.py
│ ├── router.py
│ ├── root.py
│ ├── schemas.py
│ └── notifications.py
└── consumers/
└── notification_consumer.py ✨ Nuevo
📄 FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md ✨ Nuevo
📄 IMPLEMENTATION_SUMMARY.md ✨ Nuevo (este archivo)
```
---
## 🏗️ Cambios en Archivos Existentes
### 1. `src/core/config.py`
- ✏️ Agregado: `mongodb_notifications_db` para configuración de BD de notificaciones
- ✏️ Actualizado comentario de `mongodb_url` para incluir ambas APIs
```python
mongodb_notifications_db: str = Field(
default="voxpopuli_notifications",
description="Base de datos MongoDB para Notificaciones"
)
```
### 2. `src/main.py`
- ✏️ Importado: `create_notifications_app` desde API de notificaciones
- ✏️ Importado: `NotificationConsumer`
- ✏️ Agregado: Thread para ejecutar API de notificaciones (puerto 8002)
- ✏️ Agregado: Thread para ejecutar consumidor de notificaciones
- ✏️ Actualizado: Mensajes de inicio para mostrar la nueva API
```python
# Cambios principales
from infrastructure.api.notifications.app import create_app as create_notifications_app
from consumers.notification_consumer import NotificationConsumer
# Agregado en run():
notifications_thread = threading.Thread(target=run_notifications_api, daemon=True)
notifications_consumer_thread = threading.Thread(target=run_notifications_consumer, daemon=True)
```
### 3. `src/infrastructure/adapters/rabbitmq/messages.py`
- ✏️ Agregado: Tipo de evento `UPDATE_STATUS` a `ReportEventType`
- ✏️ Agregado: Campos `old_estado`, `new_estado`, `old_visibility`, `new_visibility` a `ReportMessage`
```python
class ReportEventType(str, Enum):
CREATE = "report.create"
UPDATE_VISIBILITY = "report.update_visibility"
UPDATE_STATUS = "report.update_status" # ✨ Nuevo
DELETE = "report.delete"
@dataclass
class ReportMessage:
# ... campos existentes ...
old_estado: Optional[str] = None # ✨ Nuevo
new_estado: Optional[str] = None # ✨ Nuevo
```
### 4. `src/application/services/report_services.py`
- ✏️ **Clase `UpdateReportStatus`:** Completamente refactorizada para enviar eventos a RabbitMQ
- ✏️ Agregado: Importación de `ReportMessage` y `send_to_queue`
- ✏️ Agregado: Lógica para crear mensaje UPDATE_STATUS y enviarlo a `notifications_queue`
- ✏️ Agregado: Captura del estado anterior para comparación
```python
# Cambios en UpdateReportStatus.execute():
old_estado = report.estado # Guardar estado anterior
# Después de actualizar:
if old_estado != new_estado:
message = ReportMessage(
event_type=ReportEventType.UPDATE_STATUS,
id_reporte=report_id,
id_usuario=report.id_usuario,
old_estado=old_estado,
new_estado=new_estado,
# ... otros campos ...
)
send_to_queue("notifications_queue", message.to_dict())
```
### 5. `docker-compose.yaml`
- ✏️ **MongoDB:** Agregados credenciales de autenticación
- Usuario: `admin` / Contraseña: `admin_password`
- Agregado flag `--auth` en comando
- ✏️ **Agregado:** Servicio RabbitMQ completo con:
- Usuario: `voxpopuli` / Contraseña: `voxpopuli_pass`
- Management UI en puerto 15672
- Healthcheck configurado
- ✏️ **Volúmenes:** Agregado `rabbitmq_data` para persistencia
---
## 🚀 Nuevos Servicios en Docker Compose
### RabbitMQ (Nuevo)
```yaml
rabbitmq:
image: rabbitmq:3.13-management
container_name: voxpopuli_rabbitmq
ports:
- "5672:5672" # AMQP
- "15672:15672" # Management UI (http://localhost:15672)
credentials:
user: voxpopuli
password: voxpopuli_pass
```
**Acceso Management UI:**
- URL: http://localhost:15672
- Usuario: `voxpopuli`
- Contraseña: `voxpopuli_pass`
---
## 📊 Base de Datos MongoDB
### Nuevas Colecciones
**Base de datos:** `voxpopuli_notifications`
#### Colección: `notificaciones`
```json
{
"_id": ObjectId,
"id_usuario": 1,
"id_reporte": "uuid-del-reporte",
"message": "¡Tu reporte #uuid ha sido resuelto!",
"fecha": ISODate("2024-04-29T15:30:00Z"),
"read": false
}
```
**Índices Recomendados:**
```javascript
db.notificaciones.createIndex({ "id_usuario": 1, "fecha": -1 })
db.notificaciones.createIndex({ "id_usuario": 1, "read": 1 })
```
---
## 🔄 Flujo de Eventos
### Cambio de Estado de Reporte → Notificación
```
1. Frontend llama:
PUT /reports/{report_id}/status
{ "estado": "resuelto" }
2. API de Reportes:
└─ UpdateReportStatus.execute()
├─ Valida estado
├─ Obtiene reporte actual (estado anterior)
├─ Actualiza en MongoDB
└─ NUEVO: Envía mensaje a RabbitMQ
{
"event_type": "report.update_status",
"id_reporte": "uuid",
"id_usuario": 1,
"old_estado": "en proceso",
"new_estado": "resuelto",
...
}
3. RabbitMQ:
└─ Almacena en queue `notifications_queue`
4. Consumidor de Notificaciones:
├─ Escucha la cola
├─ Recibe el evento UPDATE_STATUS
└─ Procesa mensaje:
└─ NotificationService.send_report_status_notification()
└─ Crea notificación en MongoDB
{
"id_usuario": 1,
"id_reporte": "uuid",
"message": "¡Tu reporte ha sido resuelto!",
"fecha": now(),
"read": false
}
5. Frontend (Polling o WebSocket):
└─ GET /notifications/1
└─ Obtiene notificación creada
```
---
## 📡 Colas RabbitMQ
### `reports_queue`
- **Propósito:** Eventos de creación, eliminación y cambios de reportes
- **Consumidor:** `ReportConsumer` (consumer de reportes)
- **Eventos:** CREATE, UPDATE_VISIBILITY, DELETE
### `notifications_queue` (NUEVA)
- **Propósito:** Eventos de cambios de estado para crear notificaciones
- **Consumidor:** `NotificationConsumer` (consumer de notificaciones)
- **Eventos:** UPDATE_STATUS, UPDATE_VISIBILITY
### `users_queue`
- **Propósito:** Eventos de usuarios
- **Consumidor:** `UserConsumer`
---
## 🔌 API Endpoints de Notificaciones
### Base: `http://localhost:8002`
| Método | Endpoint | Descripción | Auth |
|--------|----------|-------------|------|
| GET | `/` | Health check | No |
| POST | `/notifications/` | Crear notificación (interno) | Sí |
| GET | `/notifications/{user_id}` | Obtener notificaciones del usuario | Sí |
| GET | `/notifications/{user_id}/unread-count` | Contar no leídas | Sí |
| PUT | `/notifications/{notification_id}/read` | Marcar como leída | Sí |
| PUT | `/notifications/{user_id}/read-all` | Marcar todas como leídas | Sí |
| DELETE | `/notifications/{notification_id}` | Eliminar notificación | Sí |
**Documentación interactiva:** http://localhost:8002/docs
---
## 🔐 Seguridad y Validaciones
### Por Implementar en Frontend
1. **Autenticación:**
- Incluir JWT token en header `Authorization: Bearer {token}`
- Validar que el token no esté expirado
2. **Autorización:**
- Backend valida que `user_id` en token === `user_id` en path
- Usuario solo puede ver sus propias notificaciones
3. **Rate Limiting:**
- Considerar máximo 1 request de polling cada 10 segundos
- WebSocket es alternativa para mayor escalabilidad
---
## 🧪 Pruebas Recomendadas
### 1. Test Manual: Crear Notificación
```bash
# Terminal 1: Iniciar el proyecto
python src/main.py
# Terminal 2: Test crear notificación
curl -X POST http://localhost:8002/notifications/ \
-H "Content-Type: application/json" \
-d '{
"id_usuario": 1,
"id_reporte": "test-report-123",
"message": "Tu reporte ha sido actualizado"
}'
```
### 2. Test Manual: Obtener Notificaciones
```bash
curl http://localhost:8002/notifications/1 \
-H "Authorization: Bearer {tu_jwt_token}"
```
### 3. Test Manual: Cambio de Estado
```bash
# Cambiar estado de reporte → dispara notificación automáticamente
curl -X PUT http://localhost:8002/reports/{report_id}/status \
-H "Content-Type: application/json" \
-H "Authorization: Bearer {tu_jwt_token}" \
-d '{"estado": "resuelto"}'
# Luego verificar que notificación se creó:
curl http://localhost:8002/notifications/1 \
-H "Authorization: Bearer {tu_jwt_token}"
```
---
## 🐳 Comandos Docker
```bash
# Levantar todos los servicios
docker-compose up -d
# Ver logs en tiempo real
docker-compose logs -f
# Ver logs específicos de un servicio
docker-compose logs -f mongodb
docker-compose logs -f rabbitmq
# Detener servicios
docker-compose down
# Verificar estado
docker-compose ps
```
---
## 📋 Arquitectura de Capas
```
┌─────────────────────────────────────────────────────┐
│ Presentación (Frontend) │
│ (Implementar según FRONTEND_NOTIFICATIONS_...) │
└────────────────────┬────────────────────────────────┘
│ HTTP/REST + WebSocket (opcional)
┌────────────────────v────────────────────────────────┐
│ API FastAPI │
│ ├─ /notifications/{user_id} │
│ ├─ /notifications/{notification_id}/read │
│ └─ ... (Ver endpoints arriba) │
└────────────────────┬────────────────────────────────┘
┌────────────────────v────────────────────────────────┐
│ Capa de Aplicación (Services) │
│ └─ NotificationService │
│ ├─ create_notification() │
│ ├─ get_user_notifications() │
│ ├─ mark_as_read() │
│ └─ send_report_status_notification() │
└────────────────────┬────────────────────────────────┘
┌────────────────────v────────────────────────────────┐
│ Capa de Dominio │
│ └─ Notification (dataclass) │
│ ├─ id_usuario │
│ ├─ id_reporte │
│ ├─ message │
│ ├─ fecha │
│ └─ read │
└────────────────────┬────────────────────────────────┘
┌────────────────────v────────────────────────────────┐
│ Puertos (Interfaces Abstractas) │
│ └─ NotificationRepository │
│ ├─ create() │
│ ├─ get_by_user() │
│ ├─ mark_as_read() │
│ └─ ... │
└────────────────────┬────────────────────────────────┘
┌────────────────────v────────────────────────────────┐
│ Infraestructura (Adaptadores) │
│ └─ NotificationRepositoryMongo │
│ ├─ Implementa NotificationRepository │
│ └─ Usa MongoDB │
└─────────────────────────────────────────────────────┘
```
---
## 📈 Escalabilidad Futura
### Fase 2 - Recomendaciones
1. **WebSocket Server:**
- Implementar socket.io en FastAPI
- Notificaciones push en tiempo real
- Reducir latencia para usuarios conectados
2. **Cache Redis:**
- Cachear conteo de no leídas
- Sincronizar estado entre instancias
3. **Histórico de Cambios:**
- Almacenar todos los cambios de estado
- Mostrar timeline en detalle de reporte
4. **Notificaciones Push:**
- Integrar con Firebase Cloud Messaging
- Enviar notificaciones a dispositivos móviles
5. **Búsqueda Full-Text:**
- Elasticsearch para buscar en notificaciones
- Filtrado avanzado
---
## 🆘 Troubleshooting
### Problema: "Cannot connect to MongoDB"
```bash
# Solución: Asegurar que MongoDB esté levantado
docker-compose up -d mongodb
# Verificar logs
docker-compose logs mongodb
```
### Problema: "RabbitMQ connection refused"
```bash
# Solución: Asegurar que RabbitMQ esté levantado
docker-compose up -d rabbitmq
# Verificar logs
docker-compose logs rabbitmq
# Acceder a management UI
http://localhost:15672 (usuario: voxpopuli, pass: voxpopuli_pass)
```
### Problema: "Notification not found"
- Verificar que `id_usuario` es correcto
- Verificar que JWT token pertenece al usuario
- Revisar que el consumidor está ejecutando
### Problema: No se crean notificaciones al cambiar estado
```bash
# 1. Verificar que consumidor está corriendo
# (Debe haber thread "Notifications-Consumer" en logs)
# 2. Verificar mensaje en RabbitMQ
# Ir a http://localhost:15672 > Queues > notifications_queue
# 3. Revisar logs del consumidor
docker-compose logs notifications-consumer # Si estuviera en Docker
```
---
## 📚 Documentación Relacionada
- **FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md** - Guía completa para implementar frontend
- **ARCHITECTURE.md** - Arquitectura general del proyecto
- **DATABASE.md** - Información sobre bases de datos
- **README.md** - Setup y ejecución del proyecto
---
## ✅ Checklist de Implementación Backend (Completado)
- [x] Crear modelo de dominio `Notification`
- [x] Crear interfaz `NotificationRepository`
- [x] Implementar `NotificationRepositoryMongo`
- [x] Crear `NotificationService` con lógica de negocio
- [x] Crear esquemas Pydantic
- [x] Implementar endpoints de API
- [x] Crear router de notificaciones
- [x] Crear app FastAPI para notificaciones
- [x] Crear `NotificationConsumer`
- [x] Actualizar `config.py` con nueva BD
- [x] Actualizar `docker-compose.yaml`
- [x] Modificar `UpdateReportStatus` para enviar eventos
- [x] Actualizar `messages.py` con `UPDATE_STATUS`
- [x] Actualizar `main.py` para ejecutar nueva API y consumidor
- [x] Crear documentación detallada para frontend
- [x] Crear este documento de resumen
---
## 🎯 Próximos Pasos para Frontend
1. **Leer:** `FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md`
2. **Crear:** Hook `useNotifications` con React Query
3. **Implementar:** Componente de icono de campana
4. **Agregar:** Dropdown de notificaciones
5. **Crear:** Página dedicada de notificaciones
6. **Integrar:** Toast notifications
7. **Implementar:** Polling cada 30 segundos
8. **Testear:** Flujo completo de usuario
---
**Estado:** ✅ Completado
**Próxima Revisión:** Después de implementación de frontend
**Contacto:** Development Team