diff --git a/API_EXAMPLES.json b/API_EXAMPLES.json
index a3c4e98..82cee85 100644
--- a/API_EXAMPLES.json
+++ b/API_EXAMPLES.json
@@ -270,168 +270,8 @@
"body": null
}
}
- },
- "notificaciones": {
- "health_check": {
- "metodo": "GET",
- "url": "http://localhost:8002/",
- "respuesta": {
- "codigo": 200,
- "body": {
- "status": "ok",
- "service": "Notificaciones Microservice"
- }
- }
- },
- "crear_notificacion": {
- "metodo": "POST",
- "url": "http://localhost:8002/notifications/",
- "descripcion": "Crear notificación (uso interno, generalmente disparado por cambios de reportes)",
- "solicitud": {
- "Content-Type": "application/json",
- "headers": {
- "Authorization": "Bearer {jwt_token}"
- },
- "body": {
- "id_usuario": 1,
- "id_reporte": "550e8400-e29b-41d4-a716-446655440000",
- "message": "¡Tu reporte ha sido resuelto!"
- }
- },
- "respuesta_exitosa": {
- "codigo": 200,
- "body": {
- "id_notificacion": "660f8401-f39c-41e5-b727-557666551111",
- "id_usuario": 1,
- "id_reporte": "550e8400-e29b-41d4-a716-446655440000",
- "message": "¡Tu reporte ha sido resuelto!",
- "fecha": "2024-04-29T15:30:00Z",
- "read": false
- }
- }
- },
- "obtener_notificaciones_usuario": {
- "metodo": "GET",
- "url": "http://localhost:8002/notifications/1?limit=50&offset=0",
- "descripcion": "Obtener todas las notificaciones de un usuario con paginación",
- "headers": {
- "Authorization": "Bearer {jwt_token}"
- },
- "query_params": {
- "limit": "50 (máximo 100)",
- "offset": "0 (para paginación)"
- },
- "respuesta": {
- "codigo": 200,
- "body": {
- "total": 150,
- "unread_count": 5,
- "notifications": [
- {
- "id_notificacion": "660f8401-f39c-41e5-b727-557666551111",
- "id_usuario": 1,
- "id_reporte": "550e8400-e29b-41d4-a716-446655440000",
- "message": "¡Tu reporte ha sido resuelto!",
- "fecha": "2024-04-29T15:30:00Z",
- "read": false
- },
- {
- "id_notificacion": "660f8401-f39c-41e5-b727-557666551112",
- "id_usuario": 1,
- "id_reporte": "550e8400-e29b-41d4-a716-446655440001",
- "message": "Tu reporte fue marcado como no resuelto.",
- "fecha": "2024-04-29T14:20:00Z",
- "read": true
- }
- ]
- }
- }
- },
- "obtener_conteo_no_leidas": {
- "metodo": "GET",
- "url": "http://localhost:8002/notifications/1/unread-count",
- "descripcion": "Obtener el número de notificaciones no leídas para un usuario",
- "headers": {
- "Authorization": "Bearer {jwt_token}"
- },
- "respuesta": {
- "codigo": 200,
- "body": {
- "unread_count": 5
- }
- }
- },
- "marcar_como_leida": {
- "metodo": "PUT",
- "url": "http://localhost:8002/notifications/660f8401-f39c-41e5-b727-557666551111/read",
- "descripcion": "Marcar una notificación específica como leída",
- "headers": {
- "Authorization": "Bearer {jwt_token}"
- },
- "respuesta_exitosa": {
- "codigo": 200,
- "body": {
- "id_notificacion": "660f8401-f39c-41e5-b727-557666551111",
- "id_usuario": 1,
- "id_reporte": "550e8400-e29b-41d4-a716-446655440000",
- "message": "¡Tu reporte ha sido resuelto!",
- "fecha": "2024-04-29T15:30:00Z",
- "read": true
- }
- }
- },
- "marcar_todas_como_leidas": {
- "metodo": "PUT",
- "url": "http://localhost:8002/notifications/1/read-all",
- "descripcion": "Marcar todas las notificaciones de un usuario como leídas",
- "headers": {
- "Authorization": "Bearer {jwt_token}"
- },
- "respuesta_exitosa": {
- "codigo": 200,
- "body": {
- "message": "All notifications marked as read",
- "updated_count": 5
- }
- }
- },
- "eliminar_notificacion": {
- "metodo": "DELETE",
- "url": "http://localhost:8002/notifications/660f8401-f39c-41e5-b727-557666551111",
- "descripcion": "Eliminar una notificación específica",
- "headers": {
- "Authorization": "Bearer {jwt_token}"
- },
- "respuesta_exitosa": {
- "codigo": 200,
- "body": {
- "message": "Notification deleted successfully"
- }
- }
- }
}
},
- "cambios_estado_notificaciones": {
- "transiciones": {
- "en_proceso_a_resuelto": {
- "mensaje": "¡Tu reporte #[id_reporte] ha sido resuelto!",
- "tipo": "positivo"
- },
- "en_proceso_a_no_resuelto": {
- "mensaje": "Tu reporte #[id_reporte] fue marcado como no resuelto.",
- "tipo": "info"
- },
- "no_resuelto_a_resuelto": {
- "mensaje": "¡Tu reporte #[id_reporte] ha sido resuelto!",
- "tipo": "positivo"
- },
- "resuelto_a_en_proceso": {
- "mensaje": "Tu reporte #[id_reporte] ha sido reabierto.",
- "tipo": "info"
- }
- },
- "trigger": "Cuando cambias el estado en PUT /reports/{report_id}/status"
- },
"tipos_reporte": {
"1": "Infraestructura/Vía pública",
"2": "Inseguridad",
@@ -439,31 +279,11 @@
"4": "Servicios públicos",
"5": "Otro"
},
- "apis_disponibles": {
- "usuarios": {
- "url": "http://localhost:8000",
- "puerto": 8000,
- "docs": "http://localhost:8000/docs"
- },
- "reportes": {
- "url": "http://localhost:8001",
- "puerto": 8001,
- "docs": "http://localhost:8001/docs"
- },
- "notificaciones": {
- "url": "http://localhost:8002",
- "puerto": 8002,
- "docs": "http://localhost:8002/docs",
- "nuevo": true
- }
- },
"codigos_estado_http": {
"200": "OK - La solicitud fue exitosa",
"201": "Created - Recurso creado exitosamente",
- "202": "Accepted - Solicitud aceptada en cola",
"204": "No Content - Eliminación exitosa",
"400": "Bad Request - Error en la solicitud",
- "401": "Unauthorized - Token inválido/expirado",
"404": "Not Found - Recurso no encontrado",
"500": "Internal Server Error - Error en el servidor"
}
diff --git a/FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md b/FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md
deleted file mode 100644
index fc9e340..0000000
--- a/FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md
+++ /dev/null
@@ -1,617 +0,0 @@
-# Prompt de Implementación: Integración de Notificaciones en el Frontend
-
-## Resumen Ejecutivo
-
-Se ha implementado una nueva **API de Notificaciones** en el backend que se integra automáticamente cuando el estado de los reportes cambia. Tu tarea es implementar la interfaz de usuario para que los usuarios puedan ver, gestionar y ser notificados de los cambios en sus reportes.
-
----
-
-## 1. Arquitectura Backend - Información de Referencia
-
-### Endpoints de la API de Notificaciones (Puerto 8002)
-
-#### 1.1 Crear Notificación (Interno)
-```
-POST /notifications/
-Content-Type: application/json
-
-{
- "id_usuario": 1,
- "id_reporte": "uuid-del-reporte",
- "message": "¡Tu reporte #uuid ha sido resuelto!"
-}
-
-Response: 200 OK
-{
- "id_notificacion": "ObjectId",
- "id_usuario": 1,
- "id_reporte": "uuid-del-reporte",
- "message": "¡Tu reporte #uuid ha sido resuelto!",
- "fecha": "2024-04-29T15:30:00Z",
- "read": false
-}
-```
-
-#### 1.2 Obtener Notificaciones de un Usuario (IMPORTANTE)
-```
-GET /notifications/{user_id}?limit=50&offset=0
-Authorization: Bearer {jwt_token}
-
-Query Parameters:
-- limit: int (1-100, default: 50) - Número máximo de notificaciones
-- offset: int (default: 0) - Paginación
-
-Response: 200 OK
-{
- "total": 150,
- "unread_count": 5,
- "notifications": [
- {
- "id_notificacion": "ObjectId_1",
- "id_usuario": 1,
- "id_reporte": "uuid-reporte-1",
- "message": "¡Tu reporte ha sido resuelto!",
- "fecha": "2024-04-29T15:30:00Z",
- "read": false
- },
- {
- "id_notificacion": "ObjectId_2",
- "id_usuario": 1,
- "id_reporte": "uuid-reporte-2",
- "message": "Tu reporte fue marcado como no resuelto.",
- "fecha": "2024-04-29T14:20:00Z",
- "read": true
- }
- ]
-}
-```
-
-#### 1.3 Obtener Conteo de No Leídas
-```
-GET /notifications/{user_id}/unread-count
-Authorization: Bearer {jwt_token}
-
-Response: 200 OK
-{
- "unread_count": 5
-}
-```
-
-#### 1.4 Marcar como Leída
-```
-PUT /notifications/{notification_id}/read
-Authorization: Bearer {jwt_token}
-
-Response: 200 OK
-{
- "id_notificacion": "ObjectId",
- "id_usuario": 1,
- "id_reporte": "uuid",
- "message": "¡Tu reporte ha sido resuelto!",
- "fecha": "2024-04-29T15:30:00Z",
- "read": true
-}
-```
-
-#### 1.5 Marcar Todas como Leídas
-```
-PUT /notifications/{user_id}/read-all
-Authorization: Bearer {jwt_token}
-
-Response: 200 OK
-{
- "message": "All notifications marked as read",
- "updated_count": 5
-}
-```
-
-#### 1.6 Eliminar Notificación
-```
-DELETE /notifications/{notification_id}
-Authorization: Bearer {jwt_token}
-
-Response: 200 OK
-{
- "message": "Notification deleted successfully"
-}
-```
-
-#### 1.7 Health Check
-```
-GET /
-Response: 200 OK
-{
- "status": "ok",
- "service": "Notificaciones Microservice"
-}
-```
-
----
-
-## 2. Requerimientos Funcionales del Frontend
-
-### 2.1 Panel de Notificaciones
-**Ubicación:** Icono de campana (🔔) en la barra de navegación/header
-
-**Funcionalidades:**
-- Mostrar un badge con el número de notificaciones no leídas
-- Mostrar un dropdown/modal al hacer click en el icono
-- Listar las últimas 10-15 notificaciones con scroll infinito
-- Mostrar fecha relativa (ej: "hace 2 minutos", "hace 1 hora")
-- Indicar visualmente cuáles están leídas y cuáles no
-- Cada notificación debe tener:
- - Mensaje de estado
- - ID del reporte (clickeable para ir al detalle del reporte)
- - Fecha/hora
- - Botón para marcar como leída/no leída
- - Botón para eliminar
-
-### 2.2 Sincronización en Tiempo Real
-**Opciones (elige una o combina):**
-
-**Opción A: Polling (Más Simple)**
-- Hacer request a `/notifications/{user_id}` cada 30 segundos
-- Comparar con estado anterior
-- Mostrar toast/notificación visual si hay nuevas
-
-**Opción B: WebSocket (Recomendado)**
-- Conectar WebSocket a backend (requiere implementar en backend)
-- Recibir eventos en tiempo real
-- Badge se actualiza instantáneamente
-
-**Opción C: Híbrido (Recomendado)**
-- WebSocket para actualizaciones en tiempo real
-- Polling cada 5 minutos como fallback
-- Sincronización al abrir la app
-
-### 2.3 Notificaciones Visuales
-**Toast Notifications:**
-- Mostrar toast en la esquina superior derecha cuando llega una notificación
-- Ejemplo:
- ```
- ✓ ¡Tu reporte ha sido resuelto!
- [Ver Reporte] [Cerrar]
- ```
-- Auto-cerrar después de 5 segundos
-- Permitir cerrar manualmente
-- Solo mostrar en tiempo real (no históricos)
-
-**Sound Notification (Opcional):**
-- Reproducir sonido discreto cuando llega notificación nueva
-- Respetar configuración de silencio del dispositivo
-
-### 2.4 Página Dedicada de Notificaciones
-**Ruta:** `/notifications` o similar
-
-**Características:**
-- Vista completa de todas las notificaciones del usuario
-- Filtros:
- - Todas
- - No leídas
- - Resueltas (estado: resuelto)
- - No resueltas (estado: no resuelto)
- - En proceso
-- Sorting:
- - Por fecha (descendente por defecto)
- - Más antiguas
-- Bulk actions:
- - Marcar todas como leídas
- - Eliminar seleccionadas
- - Marcar seleccionadas como leídas
-- Paginación (implementar scroll infinito o paginación tradicional)
-- Búsqueda por ID de reporte o mensaje
-
----
-
-## 3. Integración con Componentes Existentes
-
-### 3.1 Detalle del Reporte
-Cuando el usuario ve un reporte, mostrar un histórico/timeline de cambios:
-```
-Timeline de Cambios:
-├── 2024-04-29 15:30 - Reporte resuelto
-├── 2024-04-29 14:20 - Reporte marcado como no resuelto
-└── 2024-04-29 10:00 - Reporte creado (estado: en proceso)
-```
-
-**Nota:** Esto requeriría agregar un endpoint en backend para obtener el histórico de cambios por reporte. Alternativa: mostrar las notificaciones del usuario filtradas por `id_reporte`.
-
-### 3.2 Perfil del Usuario
-En la página de perfil/configuración, agregar sección de "Preferencias de Notificaciones":
-- [ ] Recibir notificaciones de reportes
-- [ ] Notificación de sonido
-- [ ] Notificaciones al escribir
-- [ ] Resumen diario/semanal (opcional)
-
----
-
-## 4. Especificaciones Técnicas
-
-### 4.1 Configuración Base
-```javascript
-// config.ts o similar
-const API_BASE_URL = 'http://localhost:8002'; // Notificaciones API
-const NOTIFICATIONS_POLL_INTERVAL = 30000; // 30 segundos
-const NOTIFICATIONS_TOAST_DURATION = 5000; // 5 segundos
-```
-
-### 4.2 Service/Hook para Notificaciones
-```typescript
-// Ejemplo con React
-interface Notification {
- id_notificacion: string;
- id_usuario: number;
- id_reporte: string;
- message: string;
- fecha: Date;
- read: boolean;
-}
-
-interface NotificationsState {
- notifications: Notification[];
- unreadCount: number;
- loading: boolean;
- error: string | null;
-}
-
-// Hook personalizado recomendado
-useNotifications(userId: number)
-
-// Métodos del hook:
-- fetchNotifications(limit?: number, offset?: number)
-- getUnreadCount()
-- markAsRead(notificationId: string)
-- markAllAsRead(userId: number)
-- deleteNotification(notificationId: string)
-- subscribeToUpdates() // Para WebSocket
-```
-
-### 4.3 Estados y Transiciones
-```
-Estados del Reporte → Mensaje de Notificación:
-─────────────────────────────────────────
-en proceso → resuelto → "¡Tu reporte #xxx ha sido resuelto!"
-en proceso → no resuelto → "Tu reporte #xxx fue marcado como no resuelto."
-no resuelto → resuelto → "¡Tu reporte #xxx ha sido resuelto!"
-resuelto → en proceso → "Tu reporte #xxx ha sido reabierto."
-resuelto → no resuelto → "Tu reporte #xxx fue reabierto como no resuelto."
-```
-
----
-
-## 5. UI/UX Recomendaciones
-
-### 5.1 Estilos y Componentes
-- **Badge de no leídas:** Rojo/Naranja, número en blanco, esquina superior derecha del icono
-- **Notificación no leída:** Fondo ligeramente coloreado, indicador visual (punto azul, negrita)
-- **Notificación leída:** Fondo normal, texto gris
-- **Hover state:** Fondo gris claro, mostrar botones de acción
-- **Empty state:** "No tienes notificaciones" con icono de campana vacía
-
-### 5.2 Animaciones (Opcional pero Recomendado)
-- Badge pulsante cuando hay nuevas notificaciones
-- Slide-in del toast desde la esquina
-- Fade cuando se marca como leída
-- Transición suave del dropdown
-
-### 5.3 Diseño Responsivo
-- Mobile: Dropdown debe ser full-width o modal
-- Tablet: Dropdown con ancho fijo
-- Desktop: Dropdown normal
-
----
-
-## 6. Flujo Completo de Usuario
-
-1. **Usuario inicia sesión** → Cargar notificaciones existentes
-2. **Usuario navega por la app** → Polling/WebSocket actualiza notificaciones
-3. **Un reporte cambia de estado en backend** →
- - Evento enviado a RabbitMQ
- - Consumidor de notificaciones lo procesa
- - Notificación almacenada en BD
- - WebSocket notifica al cliente (o lo ve en siguiente polling)
-4. **Cliente recibe actualización** →
- - Badge se actualiza con nuevo conteo
- - Toast se muestra (opcional)
- - Usuario puede ver en el dropdown/página de notificaciones
-5. **Usuario hace click en notificación** →
- - Va al detalle del reporte
- - Marca como leída automáticamente (opcional)
-6. **Usuario gestiona notificaciones** →
- - Puede marcar como leída
- - Puede eliminar
- - Puede marcar todas como leídas
-
----
-
-## 7. Consideraciones de Seguridad
-
-- **Autenticación:** Todas las requests deben incluir JWT token
-- **Autorización:** Un usuario solo puede ver SUS propias notificaciones
- - Backend valida que `user_id` en token === `user_id` en path
-- **Rate Limiting:** Considerar rate limiting en polling (máx 1 request cada 10s)
-- **Validación:** Validar que `notification_id` pertenece al usuario antes de actualizar
-
----
-
-## 8. Testing Recomendado
-
-### 8.1 Unit Tests
-- [ ] Hook `useNotifications` retorna estado correcto
-- [ ] Funciones de formateo de fecha relativa
-- [ ] Lógica de filtrado y sorting
-
-### 8.2 Integration Tests
-- [ ] Polling se ejecuta correctamente cada 30s
-- [ ] Marcar como leída actualiza UI
-- [ ] Eliminar notificación la remueve de la lista
-- [ ] Badge se actualiza cuando llega notificación
-
-### 8.3 E2E Tests
-- [ ] Usuario ve notificación cuando reporte cambia de estado
-- [ ] Usuario puede marcar todas como leídas
-- [ ] Página de notificaciones carga correctamente
-
----
-
-## 9. Dependencias Frontend Recomendadas
-
-```json
-{
- "dependencies": {
- "axios": "^1.x.x", // Para requests HTTP
- "framer-motion": "^10.x.x", // Para animaciones
- "react-query": "^3.x.x", // Para caching y sincronización
- "react-hot-toast": "^2.x.x", // Para toast notifications
- "date-fns": "^2.x.x", // Para formateo de fechas
- "react-infinite-scroll-component": "^6.x.x" // Para scroll infinito (opcional)
- }
-}
-```
-
----
-
-## 10. Roadmap Futuro (Phase 2)
-
-- [ ] Notificaciones push en navegador (Service Workers)
-- [ ] Notificaciones por email
-- [ ] Histórico completo de cambios de estado por reporte
-- [ ] Suscripción a notificaciones de otros reportes
-- [ ] Sistema de preferencias granular de notificaciones
-- [ ] Archivado de notificaciones (en lugar de eliminar)
-- [ ] Notificaciones de actividad comunitaria
-
----
-
-## 11. Endpoints de Referencia Backend Completos
-
-### Base URL
-```
-Desarrollo: http://localhost:8002
-Producción: https://api.voxpopuli.com/notifications
-```
-
-### Headers Requeridos
-```
-Authorization: Bearer {jwt_token}
-Content-Type: application/json
-```
-
-### Códigos de Respuesta HTTP
-- `200 OK` - Operación exitosa
-- `202 Accepted` - Solicitud aceptada pero en proceso
-- `400 Bad Request` - Parámetros inválidos
-- `401 Unauthorized` - Token inválido/expirado
-- `404 Not Found` - Recurso no encontrado
-- `500 Internal Server Error` - Error del servidor
-
----
-
-## 12. Ejemplo de Implementación React
-
-```typescript
-// hooks/useNotifications.ts
-import { useEffect, useState, useCallback } from 'react';
-import { useQuery, useMutation } from 'react-query';
-import axios from 'axios';
-
-interface Notification {
- id_notificacion: string;
- id_usuario: number;
- id_reporte: string;
- message: string;
- fecha: Date;
- read: boolean;
-}
-
-const API_BASE = 'http://localhost:8002';
-
-export function useNotifications(userId: number) {
- const [unreadCount, setUnreadCount] = useState(0);
-
- // Fetch notificaciones
- const { data: response, refetch } = useQuery(
- ['notifications', userId],
- () => axios.get(
- `${API_BASE}/notifications/${userId}?limit=50&offset=0`,
- { headers: { Authorization: `Bearer ${getToken()}` } }
- ),
- { refetchInterval: 30000 } // Poll cada 30s
- );
-
- // Marcar como leída
- const markAsReadMutation = useMutation(
- (notificationId: string) =>
- axios.put(
- `${API_BASE}/notifications/${notificationId}/read`,
- {},
- { headers: { Authorization: `Bearer ${getToken()}` } }
- ),
- { onSuccess: () => refetch() }
- );
-
- // Marcar todas como leídas
- const markAllAsReadMutation = useMutation(
- () =>
- axios.put(
- `${API_BASE}/notifications/${userId}/read-all`,
- {},
- { headers: { Authorization: `Bearer ${getToken()}` } }
- ),
- { onSuccess: () => refetch() }
- );
-
- // Eliminar notificación
- const deleteNotificationMutation = useMutation(
- (notificationId: string) =>
- axios.delete(
- `${API_BASE}/notifications/${notificationId}`,
- { headers: { Authorization: `Bearer ${getToken()}` } }
- ),
- { onSuccess: () => refetch() }
- );
-
- useEffect(() => {
- if (response?.data?.unread_count !== undefined) {
- setUnreadCount(response.data.unread_count);
- }
- }, [response]);
-
- return {
- notifications: response?.data?.notifications || [],
- unreadCount,
- loading: false,
- markAsRead: (id: string) => markAsReadMutation.mutate(id),
- markAllAsRead: () => markAllAsReadMutation.mutate(),
- deleteNotification: (id: string) => deleteNotificationMutation.mutate(id),
- };
-}
-
-// components/NotificationBell.tsx
-import { useNotifications } from '@/hooks/useNotifications';
-import { formatDistanceToNow } from 'date-fns';
-import { es } from 'date-fns/locale';
-
-export function NotificationBell({ userId }: { userId: number }) {
- const { notifications, unreadCount, markAsRead, deleteNotification } =
- useNotifications(userId);
- const [isOpen, setIsOpen] = useState(false);
-
- return (
-
-
-
- {isOpen && (
-
-
- Notificaciones
-
-
- {notifications.length === 0 ? (
-
- No hay notificaciones
-
- ) : (
- notifications.map((n) => (
-
-
- {n.message}
-
-
- Reporte: {n.id_reporte}
-
-
- {formatDistanceToNow(new Date(n.fecha), {
- locale: es,
- addSuffix: true
- })}
-
-
- {!n.read && (
-
- )}
-
-
-
- ))
- )}
-
-
-
- )}
-
- );
-}
-```
-
----
-
-## 13. Preguntas Frecuentes
-
-**P: ¿Debo implementar WebSocket o polling está bien?**
-R: Polling cada 30s está bien para MVP. WebSocket es mejor para experiencia en tiempo real pero requiere implementación en backend.
-
-**P: ¿Cómo manejo notificaciones si el usuario está offline?**
-R: Todas las notificaciones se guardan en BD. Cuando reconecte, vera el histórico completo.
-
-**P: ¿Debo eliminar notificaciones antiguas?**
-R: Por ahora, guardarlas todas. Puedes agregar lógica de archivado después de 30 días.
-
-**P: ¿Puedo mostrar solo las últimas 10 notificaciones?**
-R: Sí, el endpoint soporta `limit=10`. Usa scroll infinito para cargar más.
-
----
-
-## 14. Checklist de Implementación
-
-- [ ] Servicio HTTP creado para llamadas a `/notifications/{user_id}`
-- [ ] Hook `useNotifications` implementado
-- [ ] Componente de icono de campana con badge
-- [ ] Dropdown de últimas notificaciones
-- [ ] Página dedicada `/notifications`
-- [ ] Toast notifications en tiempo real
-- [ ] Funcionalidad de marcar como leída
-- [ ] Funcionalidad de eliminar notificaciones
-- [ ] Polling o WebSocket implementado
-- [ ] Tests unitarios para hooks
-- [ ] Tests de integración end-to-end
-- [ ] Documentación de componentes
-- [ ] Respuesta mobile optimizada
-
----
-
-**Última actualización:** 29 de Abril de 2024
-**Versión API:** 1.0.0
-**Autor:** VoxPopuli Development Team
diff --git a/IMPLEMENTATION_SUMMARY.md b/IMPLEMENTATION_SUMMARY.md
deleted file mode 100644
index b297f58..0000000
--- a/IMPLEMENTATION_SUMMARY.md
+++ /dev/null
@@ -1,515 +0,0 @@
-# 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
diff --git a/MONGODB_SEPARADO.md b/MONGODB_SEPARADO.md
deleted file mode 100644
index 799701f..0000000
--- a/MONGODB_SEPARADO.md
+++ /dev/null
@@ -1,275 +0,0 @@
-# Corrección: MongoDB Separado por Microservicio
-
-**Fecha:** 29 de Abril de 2026
-**Cambio:** Arquitectura actualizada a instancias independientes de MongoDB
-
----
-
-## 📋 Cambio Realizado
-
-Se corrigió la arquitectura para que **cada microservicio tenga su propia instancia de MongoDB** completamente separada.
-
-### Antes ❌
-```
-┌──────────────────────────────┐
-│ MongoDB (Instancia Única) │
-├──────────────────────────────┤
-│ Base de datos: reports │
-│ Base de datos: notifications │
-└──────────────────────────────┘
- ↑
- Compartida entre servicios
-```
-
-### Ahora ✅
-```
-┌──────────────────────────────┐
-│ MongoDB Reports │ ← Puerto 27017
-│ (Instancia 1) │
-│ BD: voxpopuli_reports │
-└──────────────────────────────┘
- API Reportes
-
-┌──────────────────────────────┐
-│ MongoDB Notifications │ ← Puerto 27018
-│ (Instancia 2) │
-│ BD: voxpopuli_notifications │
-└──────────────────────────────┘
- API Notificaciones
-```
-
----
-
-## 🔧 Cambios Realizados
-
-### 1. `docker-compose.yaml`
-
-**Antes:** Una única instancia `mongodb`
-
-**Ahora:** Dos instancias separadas
-```yaml
-mongodb-reports:
- image: mongo:7.0
- container_name: voxpopuli_mongo_reports
- ports:
- - "27017:27017" # Puerto específico para reportes
- volumes:
- - mongo_reports_data:/data/db
-
-mongodb-notifications:
- image: mongo:7.0
- container_name: voxpopuli_mongo_notifications
- ports:
- - "27018:27017" # Puerto específico para notificaciones (mapea al 27017 interno)
- volumes:
- - mongo_notifications_data:/data/db
-```
-
-**Volúmenes:**
-```yaml
-volumes:
- mysql_data:
- mongo_reports_data: # Nuevo
- mongo_notifications_data: # Nuevo
- rabbitmq_data:
-```
-
-### 2. `src/core/config.py`
-
-**Antes:**
-```python
-mongodb_url: str = "mongodb://localhost:27017"
-mongodb_db: str = "voxpopuli_reports"
-mongodb_notifications_db: str = "voxpopuli_notifications"
-```
-
-**Ahora:**
-```python
-# Instancia 1: Reportes
-mongodb_reports_url: str = "mongodb://admin:admin_password@localhost:27017"
-mongodb_reports_db: str = "voxpopuli_reports"
-
-# Instancia 2: Notificaciones
-mongodb_notifications_url: str = "mongodb://admin:admin_password@localhost:27018"
-mongodb_notifications_db: str = "voxpopuli_notifications"
-```
-
-### 3. `src/infrastructure/adapters/persistence/mongodb.py`
-
-**Antes:**
-```python
-mongo_client = MongoClient(ConfSettings.mongodb_url)
-mongodb = mongo_client[ConfSettings.mongodb_db]
-
-def get_reports_collection() -> Collection:
- return mongodb["reportes"]
-```
-
-**Ahora:**
-```python
-# Cliente separado para Reportes (Puerto 27017)
-mongo_client_reports = MongoClient(ConfSettings.mongodb_reports_url)
-mongodb_reports = mongo_client_reports[ConfSettings.mongodb_reports_db]
-
-# Cliente separado para Notificaciones (Puerto 27018)
-mongo_client_notifications = MongoClient(ConfSettings.mongodb_notifications_url)
-mongodb_notifications = mongo_client_notifications[ConfSettings.mongodb_notifications_db]
-
-def get_reports_collection() -> Collection:
- return mongodb_reports["reportes"]
-
-def get_notifications_collection() -> Collection:
- return mongodb_notifications["notificaciones"]
-```
-
-### 4. `src/infrastructure/adapters/persistence/notification_repository_mongo.py`
-
-**Cambio:** Usar la función `get_notifications_collection()` centralizada en lugar de crear el cliente localmente
-
-```python
-from infrastructure.adapters.persistence.mongodb import get_notifications_collection
-
-class NotificationRepositoryMongo(NotificationRepository):
- def __init__(self):
- self.collection = get_notifications_collection() # Usa instancia separada
-```
-
----
-
-## 📊 Arquitectura Final
-
-```
-┌─────────────────────────────────────────────────────┐
-│ VoxPopuli Services │
-├─────────────────────────────────────────────────────┤
-│ │
-│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
-│ │ Usuarios │ │ Reportes │ │Notificacio│ │
-│ │ │ │ │ │nes │ │
-│ │ Puerto 8000 │ │ Puerto 8001 │ │ Puerto... │ │
-│ └──────┬───────┘ └──────┬───────┘ └────┬──────┘ │
-│ │ │ │ │
-│ ↓ ↓ ↓ │
-│ ┌──────────────┐ ┌──────────────┐ ┌────────────┐ │
-│ │ MySQL │ │ MongoDB │ │ MongoDB │ │
-│ │ (Usuarios) │ │ Reports │ │ Notif. │ │
-│ │ │ │ │ │ │ │
-│ │ Puerto 3306 │ │ Puerto 27017 │ │ Puerto... │ │
-│ └──────────────┘ └──────────────┘ └────────────┘ │
-│ │
-│ Instancias │
-│ Independientes │
-│ │
-└─────────────────────────────────────────────────────┘
- ↓
- ┌──────────────────┐
- │ RabbitMQ │
- │ (Compartida) │
- │ Puerto 5672 │
- └──────────────────┘
-```
-
----
-
-## 🚀 Cómo Levantar
-
-```bash
-# Levantar todos los servicios con instancias separadas
-docker-compose up -d
-
-# Verificar que ambas instancias de MongoDB están corriendo
-docker-compose ps
-
-# Resultado esperado:
-# voxpopuli_mysql Up (healthy)
-# voxpopuli_mongo_reports Up (healthy) Puerto 27017
-# voxpopuli_mongo_notifications Up (healthy) Puerto 27018
-# voxpopuli_rabbitmq Up (healthy)
-```
-
----
-
-## 🔌 Puertos Finales
-
-| Servicio | Contenedor | Puerto Externo | Puerto Interno |
-|----------|-----------|-----------------|-----------------|
-| MySQL | voxpopuli_mysql | 3306 | 3306 |
-| MongoDB (Reports) | voxpopuli_mongo_reports | 27017 | 27017 |
-| MongoDB (Notifications) | voxpopuli_mongo_notifications | 27018 | 27017 |
-| RabbitMQ AMQP | voxpopuli_rabbitmq | 5672 | 5672 |
-| RabbitMQ Management | voxpopuli_rabbitmq | 15672 | 15672 |
-
----
-
-## 🧪 Verificar Separación
-
-### Conectar a MongoDB Reports
-```bash
-docker exec -it voxpopuli_mongo_reports mongosh \
- -u admin -p admin_password \
- --authenticationDatabase admin
-
-# En la terminal de MongoDB:
-use voxpopuli_reports
-db.reportes.find() # Solo verá reportes
-```
-
-### Conectar a MongoDB Notifications
-```bash
-docker exec -it voxpopuli_mongo_notifications mongosh \
- -u admin -p admin_password \
- --authenticationDatabase admin
-
-# En la terminal de MongoDB:
-use voxpopuli_notifications
-db.notificaciones.find() # Solo verá notificaciones
-```
-
----
-
-## ✅ Beneficios de esta Arquitectura
-
-1. **Aislamiento Completo** - Cada servicio es totalmente independiente
-2. **Escalabilidad** - Escalar reportes sin afectar notificaciones (y viceversa)
-3. **Backup Independiente** - Backups separados por tipo de dato
-4. **Seguridad** - Credenciales y acceso separados
-5. **Performance** - Sin contención de recursos entre servicios
-6. **Mantenibilidad** - Más fácil de mantener y depurar
-7. **High Availability** - Una BD puede fallar sin afectar la otra
-
----
-
-## 📝 Variables de Entorno (Opcional)
-
-Si necesitas cambiar los puertos en producción:
-
-```bash
-# .env
-MONGODB_REPORTS_URL=mongodb://admin:password@mongo-reports:27017
-MONGODB_NOTIFICATIONS_URL=mongodb://admin:password@mongo-notifications:27017
-```
-
-Luego actualizar en `config.py`:
-```python
-mongodb_reports_url: str = Field(
- default=os.getenv("MONGODB_REPORTS_URL", "...")
-)
-mongodb_notifications_url: str = Field(
- default=os.getenv("MONGODB_NOTIFICATIONS_URL", "...")
-)
-```
-
----
-
-## 🎯 Resumen
-
-✅ Cada microservicio tiene su **propia instancia de MongoDB**
-✅ Datos completamente aislados
-✅ Puertos independientes (27017 y 27018)
-✅ Conexiones separadas en código
-✅ Configuración centralizada
-✅ Totalmente escalable
-
----
-
-**Cambio Completado: Arquitectura microservicios con MongoDB independiente por servicio ✅**
diff --git a/QUICKSTART_NOTIFICATIONS.md b/QUICKSTART_NOTIFICATIONS.md
deleted file mode 100644
index faccac3..0000000
--- a/QUICKSTART_NOTIFICATIONS.md
+++ /dev/null
@@ -1,241 +0,0 @@
-# Quick Start: API de Notificaciones
-
-## 🚀 Inicio Rápido
-
-### Requisitos Previos
-- Docker y Docker Compose instalados
-- Python 3.10+ (si ejecutar sin Docker)
-- Proyecto VoxPopuli clonado
-
----
-
-## 📦 Opción 1: Con Docker Compose (Recomendado)
-
-### 1. Levantar todos los servicios
-```bash
-cd c:\Users\Rodo Machenike\Documents\API\VoxPopuli
-docker-compose up -d
-```
-
-### 2. Verificar que todo está corriendo
-```bash
-docker-compose ps
-```
-
-Deberías ver:
-- voxpopuli_mysql (3306)
-- voxpopuli_mongo (27017)
-- voxpopuli_rabbitmq (5672, 15672)
-
-### 3. Levantar la aplicación Python
-```bash
-# Crear entorno virtual si no existe
-python -m venv venv
-
-# Activar entorno virtual
-# En Windows:
-venv\Scripts\activate
-# En Linux/Mac:
-source venv/bin/activate
-
-# Instalar dependencias
-pip install -r requirements.txt
-
-# Ejecutar la aplicación
-python src/main.py
-```
-
-### 4. Verificar que APIs están funcionando
-```bash
-# Terminal nueva
-curl http://localhost:8000/ # Usuarios
-curl http://localhost:8001/ # Reportes
-curl http://localhost:8002/ # Notificaciones (NUEVA)
-```
-
----
-
-## ✅ Verificar Conexión a RabbitMQ
-
-### Acceder al Dashboard de RabbitMQ
-1. Ir a: http://localhost:15672
-2. Usuario: `voxpopuli`
-3. Contraseña: `voxpopuli_pass`
-
-### Ver colas
-- Queues > `reports_queue` - Para eventos de reportes
-- Queues > `notifications_queue` - Para eventos de notificaciones (NUEVA)
-- Queues > `users_queue` - Para eventos de usuarios
-
----
-
-## 🧪 Prueba Rápida del Flujo Completo
-
-### 1. Crear un usuario
-```bash
-curl -X POST http://localhost:8000/users/ \
- -H "Content-Type: application/json" \
- -d '{
- "nombre": "Juan",
- "apellido": "Pérez",
- "email": "juan@example.com",
- "contraseña": "password123",
- "fecha_nacimiento": "1990-01-15"
- }'
-```
-
-Guardar el `user_id` que retorna (ej: 1)
-
-### 2. Login para obtener token JWT
-```bash
-curl -X POST http://localhost:8000/login \
- -H "Content-Type: application/json" \
- -d '{
- "email": "juan@example.com",
- "contraseña": "password123"
- }'
-```
-
-Guardar el `access_token` que retorna
-
-### 3. Crear un reporte
-```bash
-curl -X POST http://localhost:8001/reports/ \
- -H "Authorization: Bearer {access_token}" \
- -F "id_usuario=1" \
- -F "tipo_reporte=1" \
- -F "descripcion=Calle rota en la esquina" \
- -F "ubicacion=Calle 5 y Carrera 10"
-```
-
-Guardar el `id_reporte` que retorna
-
-### 4. Cambiar estado del reporte (Dispara Notificación)
-```bash
-curl -X PUT http://localhost:8001/reports/{id_reporte}/status \
- -H "Authorization: Bearer {access_token}" \
- -H "Content-Type: application/json" \
- -d '{"estado": "resuelto"}'
-```
-
-### 5. Verificar que notificación se creó
-```bash
-curl http://localhost:8002/notifications/1 \
- -H "Authorization: Bearer {access_token}"
-```
-
-**Resultado esperado:**
-```json
-{
- "total": 1,
- "unread_count": 1,
- "notifications": [
- {
- "id_notificacion": "...",
- "id_usuario": 1,
- "id_reporte": "...",
- "message": "¡Tu reporte ha sido resuelto!",
- "fecha": "2024-04-29T...",
- "read": false
- }
- ]
-}
-```
-
-### 6. Marcar como leída
-```bash
-curl -X PUT http://localhost:8002/notifications/{notification_id}/read \
- -H "Authorization: Bearer {access_token}"
-```
-
----
-
-## 📊 Verificar en Bases de Datos
-
-### MongoDB - Ver notificación creada
-```bash
-# Conectar a MongoDB
-docker exec -it voxpopuli_mongo mongosh
-
-# En la terminal de MongoDB:
-use voxpopuli_notifications
-db.notificaciones.find()
-```
-
-### RabbitMQ - Ver evento enviado
-1. Ir a http://localhost:15672
-2. Queues > `notifications_queue`
-3. Debería mostrar mensajes procesados
-
----
-
-## 📝 Logs en Tiempo Real
-
-### Ver logs del contenedor de la app
-```bash
-# Si ejecutas en Docker
-docker-compose logs -f
-
-# Si ejecutas Python directamente
-# Los logs aparecen en la terminal donde ejecutaste python src/main.py
-```
-
-### Buscar errores de notificaciones
-```bash
-docker-compose logs -f notifications-consumer # Si estuviera en Docker
-# O en los logs de Python cuando ejecutas src/main.py
-```
-
----
-
-## 🔧 Solución de Problemas Rápida
-
-| Problema | Solución |
-|----------|----------|
-| Error de conexión a MongoDB | `docker-compose up -d mongodb && docker-compose logs mongodb` |
-| Error de conexión a RabbitMQ | `docker-compose up -d rabbitmq && docker-compose logs rabbitmq` |
-| Notificaciones no se crean | Verificar que `notification_consumer.py` está ejecutando (ver logs) |
-| Token expirado | Crear nuevo token con login |
-| No aparecen notificaciones | Verificar `unread_count` primero con `/notifications/{user_id}/unread-count` |
-
----
-
-## 📚 Documentos Disponibles
-
-1. **IMPLEMENTATION_SUMMARY.md** - Descripción completa de cambios
-2. **FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md** - Guía para implementar frontend
-3. **API_EXAMPLES.json** - Ejemplos de requests (actualizado con notificaciones)
-4. **docker-compose.yaml** - Servicios disponibles
-
----
-
-## 🎯 Próximos Pasos
-
-1. ✅ Backend: Servicios levantados
-2. ⬜ Frontend: Implementar según `FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md`
-3. ⬜ Testing: Pruebas unitarias e integración
-4. ⬜ Producción: Deploy en servidor
-
----
-
-## 💡 Tips Útiles
-
-- **Debugging RabbitMQ:** Acceder a http://localhost:15672 para ver estado de colas
-- **Debugging MongoDB:** Usar MongoDB Compass (herramienta GUI)
-- **Debugging API:** Usar Swagger UI en http://localhost:8002/docs
-- **Health Check:** GET http://localhost:8002/ para verificar que la API está viva
-
----
-
-## 📞 Contacto
-
-Si hay dudas o errores:
-1. Revisar logs: `docker-compose logs -f`
-2. Revisar documentación en `IMPLEMENTATION_SUMMARY.md`
-3. Verificar endpoints en http://localhost:8002/docs
-
----
-
-**¡Todo listo! 🚀**
-
-La API de Notificaciones está lista para usar.
diff --git a/RESUMEN_FINAL.md b/RESUMEN_FINAL.md
deleted file mode 100644
index a8537f2..0000000
--- a/RESUMEN_FINAL.md
+++ /dev/null
@@ -1,561 +0,0 @@
-# 🎉 API de Notificaciones - Implementación Completada
-
-## 📌 Estado: ✅ 100% COMPLETADO
-
----
-
-## 🎯 Objetivo Alcanzado
-
-Se ha implementado **exitosamente** una **API de Notificaciones** completamente integrada con la arquitectura existente de VoxPopuli. Las notificaciones se disparan automáticamente cuando los reportes cambian de estado.
-
-### Lo que ahora es posible:
-```
-Usuario Crea Reporte → Estado Cambia → Notificación Creada → Usuario Recibe Alerta
-```
-
----
-
-## 📊 Resumen de Implementación
-
-### 🏢 Infraestructura
-```
-┌─────────────────────────────────────────────────────┐
-│ VoxPopuli Services │
-├─────────────────────────────────────────────────────┤
-│ Usuarios API │ Reportes API │ Notificaciones │
-│ Puerto 8000 │ Puerto 8001 │ Puerto 8002 │
-│ (MySQL) │ (MongoDB) │ (MongoDB) │
-└─────────────────────────────────────────────────────┘
- ↓ ↓ ↓
- ┌─────────────────────────────────────────────────┐
- │ RabbitMQ Message Queue │
- │ (reports_queue | notifications_queue) │
- └─────────────────────────────────────────────────┘
-```
-
-### 📁 Archivos Creados: 13
-```
-✨ 6 Archivos en src/infrastructure/api/notifications/
-✨ 1 Archivo en src/domain/ (notifications.py)
-✨ 1 Archivo en src/application/ports/ (notification_repository.py)
-✨ 1 Archivo en src/application/services/ (notification_services.py)
-✨ 1 Archivo en src/infrastructure/adapters/persistence/
-✨ 1 Archivo en src/consumers/ (notification_consumer.py)
-✨ 2 Archivos de documentación técnica
-```
-
-### 📄 Documentación Creada: 4 Documentos
-```
-1. FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md (Guía Completa)
-2. IMPLEMENTATION_SUMMARY.md (Resumen Técnico)
-3. QUICKSTART_NOTIFICATIONS.md (Inicio Rápido)
-4. VERIFICACION.md (Checklist)
-```
-
-### 🔧 Cambios en Archivos Existentes: 5
-```
-✏️ src/core/config.py
-✏️ src/main.py
-✏️ src/infrastructure/adapters/rabbitmq/messages.py
-✏️ src/application/services/report_services.py
-✏️ docker-compose.yaml
-```
-
----
-
-## 🚀 Nuevas Funcionalidades
-
-### 1. API de Notificaciones (Puerto 8002)
-```
-GET / - Health check
-POST /notifications/ - Crear notificación
-GET /notifications/{user_id} - Obtener notificaciones del usuario
-GET /notifications/{user_id}/unread-count - Conteo de no leídas
-PUT /notifications/{id}/read - Marcar como leída
-PUT /notifications/{user_id}/read-all - Marcar todas como leídas
-DELETE /notifications/{id} - Eliminar notificación
-```
-
-### 2. Evento UPDATE_STATUS en RabbitMQ
-```json
-{
- "event_type": "report.update_status",
- "id_reporte": "uuid",
- "id_usuario": 1,
- "old_estado": "en proceso",
- "new_estado": "resuelto",
- "mensaje": "¡Tu reporte ha sido resuelto!"
-}
-```
-
-### 3. Base de Datos MongoDB Dedicada
-```javascript
-// Base de datos: voxpopuli_notifications
-// Colección: notificaciones
-{
- "_id": ObjectId,
- "id_usuario": 1,
- "id_reporte": "uuid",
- "message": "¡Tu reporte ha sido resuelto!",
- "fecha": ISODate,
- "read": false
-}
-```
-
-### 4. Consumidor Automático
-```python
-# Escucha la cola 'notifications_queue'
-# Procesa eventos UPDATE_STATUS
-# Crea notificaciones en MongoDB
-# Se ejecuta en thread separado
-```
-
----
-
-## 📈 Arquitectura Implementada
-
-### Patrón Hexagonal (Limpia)
-```
-┌─────────────────────────────────────────────┐
-│ Capa Presentación (FastAPI) │
-│ - Endpoints REST │
-│ - Validación con Pydantic │
-└──────────────┬──────────────────────────────┘
- ↓
-┌──────────────────────────────────────────────┐
-│ Capa de Aplicación (Services) │
-│ - Lógica de negocio │
-│ - Orquestación de casos de uso │
-└──────────────┬──────────────────────────────┘
- ↓
-┌──────────────────────────────────────────────┐
-│ Capa de Dominio (Domain Models) │
-│ - Entidades puras │
-│ - Lógica de negocio esencial │
-└──────────────┬──────────────────────────────┘
- ↓
-┌──────────────────────────────────────────────┐
-│ Puertos (Interfaces Abstractas) │
-│ - NotificationRepository │
-│ - Define contratos sin implementación │
-└──────────────┬──────────────────────────────┘
- ↓
-┌──────────────────────────────────────────────┐
-│ Adaptadores (Infraestructura) │
-│ - NotificationRepositoryMongo │
-│ - Detalles de implementación │
-└──────────────┬──────────────────────────────┘
- ↓
-┌──────────────────────────────────────────────┐
-│ Base de Datos (MongoDB) │
-│ - Persistencia física │
-└──────────────────────────────────────────────┘
-```
-
----
-
-## 🔄 Flujo de Eventos Completo
-
-### Step-by-Step del Proceso
-
-```
-1️⃣ FRONTEND
- └─ Usuario cambia estado de reporte
- PUT /reports/{id}/status { "estado": "resuelto" }
-
-2️⃣ API DE REPORTES
- ├─ Valida datos
- ├─ Actualiza reporte en MongoDB
- └─ Captura estado anterior/nuevo
-
-3️⃣ CREA EVENTO
- └─ Construye ReportMessage con:
- - event_type: "report.update_status"
- - old_estado: "en proceso"
- - new_estado: "resuelto"
- - id_usuario: 1
-
-4️⃣ ENVÍA A RABBITMQ
- └─ send_to_queue("notifications_queue", message)
-
-5️⃣ RABBITMQ ALMACENA
- └─ Persiste mensaje en cola
-
-6️⃣ NOTIFICATION CONSUMER ESCUCHA
- └─ Thread escuchando notifications_queue
-
-7️⃣ PROCESA EVENTO
- ├─ Recibe mensaje del queue
- ├─ Valida mensaje
- └─ Extrae datos
-
-8️⃣ CREA NOTIFICACIÓN
- └─ NotificationService.send_report_status_notification()
-
-9️⃣ ALMACENA EN MONGODB
- └─ Inserta documento en voxpopuli_notifications.notificaciones
-
-🔟 FRONTEND OBTIENE
- ├─ GET /notifications/{user_id}
- └─ Recibe lista de notificaciones
-
-✅ USUARIO VE NOTIFICACIÓN
-```
-
----
-
-## 🔌 Integración de Servicios
-
-### Colas RabbitMQ Configuradas
-
-```
-┌─────────────────────────────────────────┐
-│ reports_queue │
-│ (Existente) │
-│ ├─ ReportConsumer escucha │
-│ ├─ Eventos: CREATE, DELETE, ... │
-│ └─ Guarda en MongoDB │
-└─────────────────────────────────────────┘
-
-┌─────────────────────────────────────────┐ ✨ NUEVA
-│ notifications_queue │
-│ ├─ NotificationConsumer escucha │
-│ ├─ Eventos: UPDATE_STATUS │
-│ └─ Crea notificaciones en MongoDB │
-└─────────────────────────────────────────┘
-
-┌─────────────────────────────────────────┐
-│ users_queue │
-│ (Existente) │
-│ ├─ UserConsumer escucha │
-│ └─ Eventos: CREATE, UPDATE, DELETE │
-└─────────────────────────────────────────┘
-```
-
-### Bases de Datos
-
-```
-MySQL
-├─ voxpopuli_users (Existente)
-│ └─ Tabla: usuarios
-
-MongoDB
-├─ voxpopuli_reports (Existente)
-│ └─ Colección: reportes
-│
-└─ voxpopuli_notifications (✨ NUEVA)
- └─ Colección: notificaciones
-```
-
----
-
-## 💻 Servicios Docker
-
-### docker-compose.yaml Actualizado
-
-```yaml
-services:
- mysql:
- image: mysql:8.0
- port: 3306
-
- mongodb:
- image: mongo:7.0
- port: 27017
-
- rabbitmq: # ✨ NUEVO
- image: rabbitmq:3.13-management
- port: 5672 (AMQP)
- port: 15672 (Management UI)
-```
-
----
-
-## 🎓 Documentación Entregada
-
-### 1. FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md
-```
-Secciones:
-├─ 1. Arquitectura Backend
-├─ 2. Requerimientos Funcionales
-│ ├─ Panel de Notificaciones
-│ ├─ Sincronización Tiempo Real
-│ ├─ Notificaciones Visuales
-│ └─ Página Dedicada
-├─ 3. Integración con Componentes
-├─ 4. Especificaciones Técnicas
-├─ 5. UI/UX Recomendaciones
-├─ 6. Flujo de Usuario
-├─ 7. Seguridad
-├─ 8. Testing
-├─ 9. Dependencias
-├─ 10. Roadmap Futuro
-├─ 11. Endpoints Referencia
-├─ 12. Ejemplo React Completo
-├─ 13. FAQ
-└─ 14. Checklist Implementación
-```
-
-### 2. IMPLEMENTATION_SUMMARY.md
-```
-Secciones:
-├─ Resumen Ejecutivo
-├─ Estructura de Archivos
-├─ Cambios en Archivos Existentes
-├─ Flujo de Eventos
-├─ BD MongoDB
-├─ Colas RabbitMQ
-├─ API Endpoints
-├─ Seguridad
-├─ Pruebas
-├─ Troubleshooting
-├─ Documentación Relacionada
-└─ Checklist
-```
-
-### 3. QUICKSTART_NOTIFICATIONS.md
-```
-Secciones:
-├─ Inicio Rápido con Docker
-├─ Verificación de Conexiones
-├─ Prueba Completa del Flujo
-├─ Verificar en BD
-├─ Logs en Tiempo Real
-├─ Troubleshooting
-└─ Tips Útiles
-```
-
-### 4. VERIFICACION.md
-```
-Verificación de:
-├─ Archivos Creados (13 archivos)
-├─ Cambios en Existentes (5 archivos)
-├─ Tests de Verificación
-├─ Arquitectura
-├─ BD y Colas
-├─ Endpoints
-├─ Seguridad
-└─ Checklist de Pruebas
-```
-
----
-
-## 🎯 Lo que Ahora Pueden Hacer
-
-### Backend (Ya Implementado)
-✅ Crear y almacenar notificaciones
-✅ Obtener notificaciones de un usuario
-✅ Marcar como leídas
-✅ Eliminar notificaciones
-✅ Contar no leídas
-✅ Sincronizar eventos con cambios de reportes
-
-### Frontend (A Implementar)
-⏳ Mostrar icono de campana en navegación
-⏳ Dropdown con últimas notificaciones
-⏳ Página dedicada de notificaciones
-⏳ Toast notifications en tiempo real
-⏳ Polling o WebSocket
-⏳ Filtros y búsqueda
-⏳ Paginación
-
----
-
-## 🚀 Pasos Siguientes
-
-### Fase 1: Testing Backend ✅
-- [x] Crear archivos de dominio
-- [x] Crear repositorio MongoDB
-- [x] Crear servicios
-- [x] Crear API endpoints
-- [x] Crear consumidor
-- [x] Integrar eventos
-- [x] Documentar
-
-### Fase 2: Implementación Frontend ⏳
-- [ ] Leer `FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md`
-- [ ] Crear hook `useNotifications`
-- [ ] Implementar componente de campana
-- [ ] Implementar dropdown
-- [ ] Agregar polling/WebSocket
-- [ ] Crear página de notificaciones
-- [ ] Implementar toasts
-- [ ] Testing
-
-### Fase 3: Optimización Futura
-- [ ] WebSocket en lugar de polling
-- [ ] Redis para caching
-- [ ] Histórico de cambios
-- [ ] Notificaciones push
-
----
-
-## 📊 Estadísticas
-
-| Métrica | Cantidad |
-|---------|----------|
-| Archivos creados | 13 |
-| Archivos modificados | 5 |
-| Líneas de código (backend) | ~2,500+ |
-| Endpoints API | 7 |
-| Colas RabbitMQ | 3 |
-| BD MongoDB | 2 |
-| Documentación escrita | 4 docs |
-| Ejemplos de código | 200+ líneas |
-| Tiempo estimado de implementación | 8-10 horas |
-
----
-
-## 🎁 Archivos Entregados
-
-### Código Backend
-```
-src/domain/notifications.py
-src/application/ports/notification_repository.py
-src/application/services/notification_services.py
-src/infrastructure/adapters/persistence/notification_repository_mongo.py
-src/infrastructure/api/notifications/__init__.py
-src/infrastructure/api/notifications/app.py
-src/infrastructure/api/notifications/router.py
-src/infrastructure/api/notifications/schemas.py
-src/infrastructure/api/notifications/notifications.py
-src/infrastructure/api/notifications/root.py
-src/consumers/notification_consumer.py
-```
-
-### Actualizaciones
-```
-src/core/config.py (actualizado)
-src/main.py (actualizado)
-src/infrastructure/adapters/rabbitmq/messages.py (actualizado)
-src/application/services/report_services.py (actualizado)
-docker-compose.yaml (actualizado)
-API_EXAMPLES.json (actualizado)
-```
-
-### Documentación
-```
-FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md
-IMPLEMENTATION_SUMMARY.md
-QUICKSTART_NOTIFICATIONS.md
-VERIFICACION.md
-RESUMEN_FINAL.md (este archivo)
-```
-
----
-
-## 🔐 Aspectos de Seguridad Implementados
-
-✅ Autenticación JWT requerida
-✅ Autorización por usuario
-✅ Validación de datos con Pydantic
-✅ Manejo de excepciones
-✅ Logs detallados
-✅ Rate limiting (a considerar en frontend)
-
----
-
-## 🧪 Cómo Probar Rápidamente
-
-```bash
-# 1. Levantar servicios
-docker-compose up -d
-
-# 2. Ejecutar la app
-python src/main.py
-
-# 3. En otra terminal, crear un usuario y un reporte
-curl -X POST http://localhost:8000/users/ ...
-
-# 4. Cambiar estado del reporte (dispara notificación)
-curl -X PUT http://localhost:8001/reports/{id}/status ...
-
-# 5. Obtener notificaciones
-curl http://localhost:8002/notifications/1 ...
-```
-
----
-
-## 📞 Recursos de Ayuda
-
-### Documentación
-- 📖 `FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md` - Guía completa para frontend
-- 📋 `IMPLEMENTATION_SUMMARY.md` - Arquitectura técnica
-- 🚀 `QUICKSTART_NOTIFICATIONS.md` - Inicio rápido
-- ✅ `VERIFICACION.md` - Checklist
-
-### URLs Importantes
-- 🔗 API Docs: http://localhost:8002/docs
-- 🔗 RabbitMQ: http://localhost:15672 (user: voxpopuli, pass: voxpopuli_pass)
-- 🔗 MongoDB: localhost:27017
-
-### Comandos Útiles
-```bash
-# Ver logs en tiempo real
-docker-compose logs -f
-
-# Acceder a MongoDB
-docker exec -it voxpopuli_mongo mongosh
-
-# Ver colas RabbitMQ
-http://localhost:15672 > Queues
-```
-
----
-
-## ✨ Resumen Ejecutivo
-
-### Antes
-```
-Reporte Estado Cambia
- ↓
-¿Cómo sabe el usuario?
- ↓
-No tiene forma de saberlo
-```
-
-### Ahora
-```
-Reporte Estado Cambia
- ↓
-Evento a RabbitMQ
- ↓
-Consumidor procesa
- ↓
-Notificación creada
- ↓
-Usuario recibe alerta
- ↓
-✅ PERFECTO
-```
-
----
-
-## 🎉 Conclusión
-
-La **API de Notificaciones** está completamente implementada y lista para que el equipo de frontend la integre.
-
-### Estado: ✅ LISTO PARA PRODUCCIÓN (Backend)
-
-Todos los archivos han sido creados siguiendo:
-- ✅ Arquitectura hexagonal
-- ✅ Mejores prácticas de código
-- ✅ Documentación exhaustiva
-- ✅ Ejemplos funcionales
-
-### Próximo paso: Implementación en Frontend
-
-Consultar `FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md` para comenzar.
-
----
-
-**🎊 ¡Implementación Completada Exitosamente! 🎊**
-
----
-
-**Información del Proyecto:**
-- Versión: 1.0.0
-- Fecha: 29 de Abril de 2024
-- API Nueva: Notificaciones (Puerto 8002)
-- BD Nueva: voxpopuli_notifications (MongoDB)
-- Estado: ✅ Completado
diff --git a/VERIFICACION.md b/VERIFICACION.md
deleted file mode 100644
index fee1ff5..0000000
--- a/VERIFICACION.md
+++ /dev/null
@@ -1,438 +0,0 @@
-# Verificación de Implementación: API de Notificaciones
-
-**Fecha de Completación:** 29 de Abril de 2024
-**Estado:** ✅ COMPLETADO
-
----
-
-## ✅ Verificación de Archivos Creados
-
-### Archivos de Dominio
-- [x] `src/domain/notifications.py` - Modelo de dominio Notification
-
-### Archivos de Puertos/Interfaces
-- [x] `src/application/ports/notification_repository.py` - Interfaz NotificationRepository
-
-### Archivos de Servicios
-- [x] `src/application/services/notification_services.py` - Lógica de negocio
-
-### Archivos de Infraestructura - Persistencia
-- [x] `src/infrastructure/adapters/persistence/notification_repository_mongo.py` - Implementación MongoDB
-
-### Archivos de API
-- [x] `src/infrastructure/api/notifications/__init__.py` - Package init
-- [x] `src/infrastructure/api/notifications/app.py` - App FastAPI factory
-- [x] `src/infrastructure/api/notifications/router.py` - Enrutador principal
-- [x] `src/infrastructure/api/notifications/schemas.py` - Esquemas Pydantic
-- [x] `src/infrastructure/api/notifications/notifications.py` - Endpoints principales
-- [x] `src/infrastructure/api/notifications/root.py` - Endpoints raíz
-
-### Archivos de Consumidores
-- [x] `src/consumers/notification_consumer.py` - Consumidor RabbitMQ
-
-### Documentación
-- [x] `FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md` - Guía completa para frontend
-- [x] `IMPLEMENTATION_SUMMARY.md` - Resumen de cambios y arquitectura
-- [x] `QUICKSTART_NOTIFICATIONS.md` - Guía de inicio rápido
-- [x] `VERIFICACION.md` - Este documento
-
-### Ejemplos de API
-- [x] `API_EXAMPLES.json` - Actualizado con endpoints de notificaciones
-
----
-
-## ✅ Verificación de Cambios en Archivos Existentes
-
-### `src/core/config.py`
-- [x] Agregado: `mongodb_notifications_db` configuration
-- [x] Actualizado: Comentario de `mongodb_url`
-
-### `src/main.py`
-- [x] Importado: `create_notifications_app`
-- [x] Importado: `NotificationConsumer`
-- [x] Agregado: `run_notifications_api()` function
-- [x] Agregado: `run_notifications_consumer()` function
-- [x] Agregado: Thread para API de notificaciones (puerto 8002)
-- [x] Agregado: Thread para consumidor de notificaciones
-- [x] Actualizado: Mensajes de inicio
-
-### `src/infrastructure/adapters/rabbitmq/messages.py`
-- [x] Agregado: `UPDATE_STATUS` a `ReportEventType` enum
-- [x] Agregado: Campos `old_estado`, `new_estado` a `ReportMessage`
-- [x] Agregado: Campos `old_visibility`, `new_visibility` a `ReportMessage`
-
-### `src/application/services/report_services.py`
-- [x] Refactorizado: Clase `UpdateReportStatus`
-- [x] Agregado: Importación de `ReportMessage` y `send_to_queue`
-- [x] Agregado: Lógica para capturar estado anterior
-- [x] Agregado: Envío de evento UPDATE_STATUS a `notifications_queue`
-
-### `docker-compose.yaml`
-- [x] Actualizado: MongoDB con autenticación
-- [x] Agregado: Servicio RabbitMQ completo
-- [x] Agregado: Volumen para RabbitMQ
-- [x] Actualizado: Comentarios
-
----
-
-## 🧪 Tests de Verificación
-
-### Test 1: Verificar que MongoDB está configurado
-```bash
-# Verificar en config.py
-grep -n "mongodb_notifications_db" src/core/config.py
-# Resultado esperado: Debe haber una línea con la configuración
-```
-
-### Test 2: Verificar que RabbitMQ está en docker-compose
-```bash
-# Verificar en docker-compose.yaml
-grep -n "rabbitmq" docker-compose.yaml
-# Resultado esperado: Debe haber múltiples líneas de configuración
-```
-
-### Test 3: Verificar que UPDATE_STATUS existe
-```bash
-# Verificar en messages.py
-grep -n "UPDATE_STATUS" src/infrastructure/adapters/rabbitmq/messages.py
-# Resultado esperado: Debe haber línea en la enumeración
-```
-
-### Test 4: Verificar que API está configurada para ejecutarse
-```bash
-# Verificar en main.py
-grep -n "run_notifications_api" src/main.py
-# Resultado esperado: Debe haber función y thread
-```
-
-### Test 5: Verificar que los archivos de notificaciones existen
-```bash
-# Listar archivos creados
-ls -la src/infrastructure/api/notifications/
-# Resultado esperado: Debe haber 6 archivos .py
-
-# Listar consumidor
-ls -la src/consumers/notification_consumer.py
-# Resultado esperado: Archivo debe existir
-```
-
----
-
-## 🏗️ Verificación de Arquitectura
-
-### Arquitectura Hexagonal ✅
-
-La implementación sigue el patrón hexagonal existente:
-
-```
-┌─────────────────────────────────────────┐
-│ EXTERNA: FastAPI (Puerto 8002) │
-├─────────────────────────────────────────┤
-│ ADAPTADOR: API Router & Schemas │
-├─────────────────────────────────────────┤
-│ APLICACIÓN: NotificationService │
-├─────────────────────────────────────────┤
-│ PUERTOS: NotificationRepository │
-├─────────────────────────────────────────┤
-│ DOMINIO: Notification (dataclass) │
-├─────────────────────────────────────────┤
-│ ADAPTADOR: MongoDB Persistencia │
-├─────────────────────────────────────────┤
-│ EXTERNA: MongoDB (Base de datos) │
-└─────────────────────────────────────────┘
-```
-
-### Integración de Eventos ✅
-
-Flujo de eventos completamente integrado:
-
-```
-Reporte Estado Cambio
- ↓
-UpdateReportStatus.execute()
- ↓
-Envía evento UPDATE_STATUS a RabbitMQ
- ↓
-NotificationConsumer escucha
- ↓
-Procesa evento y crea notificación
- ↓
-Almacena en MongoDB
- ↓
-Frontend obtiene con polling/WebSocket
-```
-
----
-
-## 📊 Especificación de Base de Datos
-
-### MongoDB - `voxpopuli_notifications`
-
-```javascript
-db.notificaciones.find().pretty()
-{
- "_id": ObjectId("..."),
- "id_usuario": 1,
- "id_reporte": "uuid",
- "message": "string",
- "fecha": ISODate("..."),
- "read": boolean
-}
-```
-
-### Índices Automáticos ✅
-- `id_usuario` - para búsquedas por usuario
-- `fecha` - para ordenamiento
-- `read` - para filtrado
-
----
-
-## 🔌 Colas RabbitMQ Configuradas
-
-| Cola | Propósito | Consumidor | Estado |
-|------|-----------|-----------|--------|
-| `reports_queue` | Eventos de reportes | ReportConsumer | ✅ Existente |
-| `notifications_queue` | Eventos de notificaciones | NotificationConsumer | ✅ Nuevo |
-| `users_queue` | Eventos de usuarios | UserConsumer | ✅ Existente |
-
----
-
-## 🚀 Endpoints Implementados
-
-### Health Check
-- [x] `GET /` - Health check
-
-### Notificaciones (CRUD)
-- [x] `POST /notifications/` - Crear notificación
-- [x] `GET /notifications/{user_id}` - Obtener notificaciones del usuario
-- [x] `GET /notifications/{user_id}/unread-count` - Obtener conteo de no leídas
-- [x] `PUT /notifications/{notification_id}/read` - Marcar como leída
-- [x] `PUT /notifications/{user_id}/read-all` - Marcar todas como leídas
-- [x] `DELETE /notifications/{notification_id}` - Eliminar notificación
-
----
-
-## 📝 Documentación Completa
-
-### Documentos Creados
-1. **FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md** (14 secciones)
- - Arquitectura backend
- - Requerimientos funcionales
- - Integración con componentes
- - Especificaciones técnicas
- - Ejemplos de código React
- - Testing
- - Troubleshooting
-
-2. **IMPLEMENTATION_SUMMARY.md** (14 secciones)
- - Resumen ejecutivo
- - Estructura de archivos
- - Cambios en existentes
- - Flujo de eventos
- - Seguridad
- - Tests
- - Troubleshooting
-
-3. **QUICKSTART_NOTIFICATIONS.md**
- - Setup con Docker
- - Prueba rápida
- - Debugging
- - Tips útiles
-
-4. **API_EXAMPLES.json**
- - Todos los endpoints documentados
- - Ejemplos de requests/responses
- - Mensajes de notificación
-
----
-
-## 🔐 Seguridad Implementada
-
-- [x] Autenticación JWT (requiere token)
-- [x] Autorización por usuario (valida en backend)
-- [x] Base de datos MongoDB con índices
-- [x] Validación de datos en Pydantic
-- [x] Manejo de excepciones
-- [x] Logs de errores
-
----
-
-## 🧪 Checklist de Pruebas
-
-### Pruebas Manuales Recomendadas
-
-- [ ] Docker compose levanta sin errores
-- [ ] MongoDB se conecta correctamente
-- [ ] RabbitMQ se conecta correctamente
-- [ ] API de notificaciones responde a health check
-- [ ] Cambiar estado de reporte dispara notificación
-- [ ] Notificación se guarda en MongoDB
-- [ ] Se obtiene notificación via GET
-- [ ] Marcar como leída funciona
-- [ ] Conteo de no leídas es correcto
-- [ ] Marcar todas como leídas funciona
-- [ ] Eliminar notificación funciona
-- [ ] Badge se actualiza correctamente
-
-### Pruebas de Integración (Frontend)
-
-- [ ] Hook `useNotifications` se conecta correctamente
-- [ ] Polling obtiene notificaciones
-- [ ] Toast se muestra en tiempo real
-- [ ] Dropdown de notificaciones funciona
-- [ ] Página dedicada carga correctamente
-- [ ] Filtros funcionan
-- [ ] Paginación funciona
-- [ ] Búsqueda funciona
-
----
-
-## 🐳 Servicios en Docker
-
-### Verificar que todos están corriendo
-
-```bash
-# Comando
-docker-compose ps
-
-# Resultado esperado:
-# NAME STATUS
-# voxpopuli_mysql Up (healthy)
-# voxpopuli_mongo Up (healthy)
-# voxpopuli_rabbitmq Up (healthy)
-```
-
----
-
-## 📊 Métricas de Implementación
-
-| Métrica | Valor |
-|---------|-------|
-| Archivos creados | 13 |
-| Archivos modificados | 5 |
-| Líneas de código (backend) | ~2,000+ |
-| Endpoints API | 7 |
-| Colas RabbitMQ | 3 |
-| Base de datos MongoDB | 1 nueva |
-| Documentación (caracteres) | ~50,000+ |
-| Ejemplo de código (líneas) | 200+ |
-
----
-
-## 🚀 Próximos Pasos
-
-### Inmediatos
-1. Leer `FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md`
-2. Levantar servicios con `docker-compose up -d`
-3. Ejecutar `python src/main.py`
-4. Probar endpoints con ejemplos en `API_EXAMPLES.json`
-
-### Corto Plazo (Frontend)
-1. Crear hook `useNotifications`
-2. Implementar componente de campana
-3. Implementar dropdown
-4. Agregar polling o WebSocket
-5. Implementar página de notificaciones
-
-### Mediano Plazo (Mejoras)
-1. Implementar WebSocket para tiempo real
-2. Agregar Redis para caching
-3. Implementar histórico completo
-4. Agregar notificaciones push
-
----
-
-## 🎯 Estado Final
-
-```
-Backend: ✅ COMPLETADO
-Documentación: ✅ COMPLETA
-Código: ✅ PROBADO
-Arquitectura: ✅ VÁLIDA
-Tests: ⏳ PENDIENTE (Frontend)
-Producción: ⏳ PENDIENTE
-```
-
----
-
-## 📞 Notas Importantes
-
-### ⚠️ Importante: Autenticación JWT
-Todos los endpoints excepto health check requieren JWT token en header:
-```
-Authorization: Bearer {tu_token_jwt}
-```
-
-### ⚠️ Importante: RabbitMQ
-Debe estar levantado para que los eventos de notificaciones funcionen:
-```bash
-docker-compose up -d rabbitmq
-```
-
-### ⚠️ Importante: Consumidor
-El consumidor de notificaciones debe estar ejecutándose:
-```bash
-# En threads (automático con python src/main.py)
-# O manualmente:
-python src/consumers/notification_consumer.py
-```
-
-### ℹ️ Información: Escalabilidad
-Para producción, considerar:
-- WebSocket en lugar de polling
-- Redis para cache
-- Multiple instancias de consumidor
-- Load balancer
-
----
-
-## 📄 Referencia Rápida
-
-### URLs Importantes
-- API Notificaciones: http://localhost:8002
-- Swagger UI: http://localhost:8002/docs
-- RabbitMQ Management: http://localhost:15672 (user: voxpopuli, pass: voxpopuli_pass)
-
-### Archivos Clave
-- Configuración: `src/core/config.py`
-- Main: `src/main.py`
-- Servicios: `src/application/services/notification_services.py`
-- API: `src/infrastructure/api/notifications/notifications.py`
-- Consumidor: `src/consumers/notification_consumer.py`
-
-### Comandos Útiles
-```bash
-# Levantar servicios
-docker-compose up -d
-
-# Ver logs
-docker-compose logs -f
-
-# Ejecutar app
-python src/main.py
-
-# Acceder a MongoDB
-docker exec -it voxpopuli_mongo mongosh
-
-# Verificar colas RabbitMQ
-# Ir a http://localhost:15672 > Queues
-```
-
----
-
-## ✨ Conclusión
-
-La **API de Notificaciones** ha sido implementada **completamente** siguiendo:
-- ✅ Arquitectura hexagonal
-- ✅ Patrones del proyecto existente
-- ✅ Mejores prácticas de code
-- ✅ Documentación exhaustiva
-- ✅ Ejemplos de implementación
-
-**Estado de Implementación: LISTO PARA FRONTEND**
-
----
-
-**Última actualización:** 29 de Abril de 2024
-**Versión:** 1.0.0
-**Verificador:** Development Team
diff --git a/docker-compose.yaml b/docker-compose.yaml
index e3c5f0a..539acd6 100644
--- a/docker-compose.yaml
+++ b/docker-compose.yaml
@@ -19,61 +19,21 @@ services:
timeout: 5s
retries: 5
- mongodb-reports:
+ mongodb:
image: mongo:7.0
- container_name: voxpopuli_mongo_reports
+ container_name: voxpopuli_mongo
environment:
- MONGO_INITDB_ROOT_USERNAME: admin
- MONGO_INITDB_ROOT_PASSWORD: admin_password
MONGO_INITDB_DATABASE: voxpopuli_reports
ports:
- "27017:27017"
volumes:
- - mongo_reports_data:/data/db
+ - mongo_data:/data/db
healthcheck:
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
interval: 10s
timeout: 5s
retries: 5
- command: mongod --auth
-
- mongodb-notifications:
- image: mongo:7.0
- container_name: voxpopuli_mongo_notifications
- environment:
- MONGO_INITDB_ROOT_USERNAME: admin
- MONGO_INITDB_ROOT_PASSWORD: admin_password
- MONGO_INITDB_DATABASE: voxpopuli_notifications
- ports:
- - "27018:27017"
- volumes:
- - mongo_notifications_data:/data/db
- healthcheck:
- test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
- interval: 10s
- timeout: 5s
- retries: 5
- command: mongod --auth
-
- rabbitmq:
- image: rabbitmq:3.13-management
- container_name: voxpopuli_rabbitmq
- environment:
- RABBITMQ_DEFAULT_USER: voxpopuli
- RABBITMQ_DEFAULT_PASS: voxpopuli_pass
- ports:
- - "5672:5672"
- - "15672:15672" # Management UI
- volumes:
- - rabbitmq_data:/var/lib/rabbitmq
- healthcheck:
- test: ["CMD", "rabbitmq-diagnostics", "-q", "ping"]
- interval: 10s
- timeout: 5s
- retries: 5
volumes:
mysql_data:
- mongo_reports_data:
- mongo_notifications_data:
- rabbitmq_data:
\ No newline at end of file
+ mongo_data:
\ No newline at end of file
diff --git a/src/application/ports/notification_repository.py b/src/application/ports/notification_repository.py
deleted file mode 100644
index 368380a..0000000
--- a/src/application/ports/notification_repository.py
+++ /dev/null
@@ -1,100 +0,0 @@
-from abc import ABC, abstractmethod
-from typing import List, Optional
-from domain.notifications import Notification
-
-
-class NotificationRepository(ABC):
- """Puerto/Interfaz para el repositorio de notificaciones"""
-
- @abstractmethod
- def create(self, notification: Notification) -> str:
- """
- Crea una nueva notificación
-
- Args:
- notification: Objeto Notification a crear
-
- Returns:
- ID de la notificación creada
- """
- pass
-
- @abstractmethod
- def get_by_id(self, notification_id: str) -> Optional[Notification]:
- """
- Obtiene una notificación por ID
-
- Args:
- notification_id: ID de la notificación
-
- Returns:
- Objeto Notification o None si no existe
- """
- pass
-
- @abstractmethod
- def get_by_user(self, user_id: int, limit: int = 50, offset: int = 0) -> List[Notification]:
- """
- Obtiene notificaciones de un usuario
-
- Args:
- user_id: ID del usuario
- limit: Número máximo de notificaciones
- offset: Desplazamiento de registros
-
- Returns:
- Lista de notificaciones del usuario
- """
- pass
-
- @abstractmethod
- def mark_as_read(self, notification_id: str) -> bool:
- """
- Marca una notificación como leída
-
- Args:
- notification_id: ID de la notificación
-
- Returns:
- True si se actualizó exitosamente
- """
- pass
-
- @abstractmethod
- def mark_all_as_read(self, user_id: int) -> int:
- """
- Marca todas las notificaciones de un usuario como leídas
-
- Args:
- user_id: ID del usuario
-
- Returns:
- Número de notificaciones actualizadas
- """
- pass
-
- @abstractmethod
- def delete(self, notification_id: str) -> bool:
- """
- Elimina una notificación
-
- Args:
- notification_id: ID de la notificación
-
- Returns:
- True si se eliminó exitosamente
- """
- pass
-
- @abstractmethod
- def get_unread_count(self, user_id: int) -> int:
- """
- Obtiene el número de notificaciones no leídas para un usuario
-
- Args:
- user_id: ID del usuario
-
- Returns:
- Número de notificaciones no leídas
- """
- pass
diff --git a/src/application/services/notification_services.py b/src/application/services/notification_services.py
deleted file mode 100644
index 575ffa1..0000000
--- a/src/application/services/notification_services.py
+++ /dev/null
@@ -1,99 +0,0 @@
-from typing import List, Optional
-from domain.notifications import Notification
-from infrastructure.adapters.persistence.notification_repository_mongo import NotificationRepositoryMongo
-from datetime import datetime
-
-
-class NotificationService:
- """Servicio de negocio para notificaciones"""
-
- def __init__(self):
- self.repository = NotificationRepositoryMongo()
-
- def create_notification(
- self,
- id_usuario: int,
- id_reporte: str,
- message: str
- ) -> str:
- """
- Crea una nueva notificación
-
- Args:
- id_usuario: ID del usuario
- id_reporte: ID del reporte relacionado
- message: Mensaje de la notificación
-
- Returns:
- ID de la notificación creada
- """
- notification = Notification(
- id_usuario=id_usuario,
- id_reporte=id_reporte,
- message=message,
- fecha=datetime.utcnow(),
- read=False
- )
- return self.repository.create(notification)
-
- def get_notification(self, notification_id: str) -> Optional[Notification]:
- """Obtiene una notificación por ID"""
- return self.repository.get_by_id(notification_id)
-
- def get_user_notifications(
- self,
- user_id: int,
- limit: int = 50,
- offset: int = 0
- ) -> List[Notification]:
- """Obtiene notificaciones de un usuario"""
- return self.repository.get_by_user(user_id, limit, offset)
-
- def mark_as_read(self, notification_id: str) -> bool:
- """Marca una notificación como leída"""
- return self.repository.mark_as_read(notification_id)
-
- def mark_all_as_read(self, user_id: int) -> int:
- """Marca todas las notificaciones de un usuario como leídas"""
- return self.repository.mark_all_as_read(user_id)
-
- def delete_notification(self, notification_id: str) -> bool:
- """Elimina una notificación"""
- return self.repository.delete(notification_id)
-
- def get_unread_count(self, user_id: int) -> int:
- """Obtiene el número de notificaciones no leídas"""
- return self.repository.get_unread_count(user_id)
-
- def send_report_status_notification(
- self,
- id_usuario: int,
- id_reporte: str,
- old_status: str,
- new_status: str
- ) -> str:
- """
- Envía una notificación cuando cambia el estado de un reporte
-
- Args:
- id_usuario: ID del usuario propietario del reporte
- id_reporte: ID del reporte
- old_status: Estado anterior
- new_status: Nuevo estado
-
- Returns:
- ID de la notificación creada
- """
- status_messages = {
- ("en proceso", "resuelto"): f"¡Tu reporte #{id_reporte} ha sido resuelto!",
- ("en proceso", "no resuelto"): f"Tu reporte #{id_reporte} fue marcado como no resuelto.",
- ("no resuelto", "resuelto"): f"¡Tu reporte #{id_reporte} ha sido resuelto!",
- ("resuelto", "en proceso"): f"Tu reporte #{id_reporte} ha sido reabierto.",
- }
-
- message = status_messages.get(
- (old_status, new_status),
- f"El estado de tu reporte #{id_reporte} ha cambiado a {new_status}"
- )
-
- return self.create_notification(id_usuario, id_reporte, message)
diff --git a/src/application/services/report_services.py b/src/application/services/report_services.py
index 0268118..2c6cb47 100644
--- a/src/application/services/report_services.py
+++ b/src/application/services/report_services.py
@@ -256,7 +256,7 @@ class DeleteReport:
}
class UpdateReportStatus:
- """Use case para actualizar el estado de un reporte - envía evento a RabbitMQ"""
+ """Use case para actualizar el estado de un reporte"""
def __init__(self, repo: ReportRepository):
if not isinstance(repo, ReportRepository):
raise TypeError("repo must implement ReportRepository")
@@ -264,7 +264,7 @@ class UpdateReportStatus:
def execute(self, report_id: str, new_estado: str) -> Dict[str, Any]:
"""
- Actualiza el estado de un reporte y envía notificación vía RabbitMQ.
+ Actualiza el estado de un reporte.
Valida previamente:
- Reporte existe
- Estado es válido
@@ -294,32 +294,9 @@ class UpdateReportStatus:
"message": f"Error al buscar reporte: {str(e)}"
}
- # Guardar estado anterior para notificación
- old_estado = report.estado
-
# Actualizar estado
try:
self.repo.update_estado(report_id, new_estado)
-
- # Enviar evento a RabbitMQ solo si el estado cambió
- 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,
- estado=new_estado,
- tipo_reporte=report.tipo_reporte,
- descripcion=report.descripcion,
- ubicacion=report.ubicacion,
- lat=report.lat,
- lng=report.lng,
- visibilidad=report.visibilidad,
- fecha_creacion=report.fecha_creacion.isoformat() if report.fecha_creacion else None
- )
- send_to_queue("notifications_queue", message.to_dict())
-
return {
"status": "success",
"message": f"Estado del reporte actualizado a '{new_estado}'",
diff --git a/src/consumers/notification_consumer.py b/src/consumers/notification_consumer.py
deleted file mode 100644
index 97aa234..0000000
--- a/src/consumers/notification_consumer.py
+++ /dev/null
@@ -1,98 +0,0 @@
-"""Notifications RabbitMQ Consumer - Processes report status change events"""
-import sys
-import os
-import logging
-from datetime import datetime
-
-# Add src to path to import modules
-sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
-
-from infrastructure.adapters.rabbitmq.consumer import RabbitMQConsumer
-from infrastructure.adapters.rabbitmq.messages import ReportMessage, ReportEventType
-from application.services.notification_services import NotificationService
-
-# Set up logging
-logging.basicConfig(
- level=logging.INFO,
- format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
- handlers=[
- logging.FileHandler('logs/notifications_consumer.log'),
- logging.StreamHandler()
- ]
-)
-logger = logging.getLogger(__name__)
-
-
-class NotificationConsumer:
- """Consumer para eventos de cambio de estado de reportes desde RabbitMQ"""
-
- def __init__(self):
- self.notification_service = NotificationService()
- self.consumer = RabbitMQConsumer(queue_name='notifications_queue')
- self.consumer.set_callback(self.process_message)
-
- def start(self):
- """Inicia el consumidor"""
- logger.info("Notifications Consumer started")
- self.consumer.start_consuming()
-
- def process_message(self, message_dict: dict):
- """
- Procesa un evento de cambio de estado de reporte desde RabbitMQ
-
- Args:
- message_dict: Diccionario con los datos del mensaje
- """
- try:
- # Reconstruir el objeto ReportMessage
- message = ReportMessage.from_dict(message_dict)
-
- # Solo procesar eventos de actualización de estado
- if message.event_type == ReportEventType.UPDATE_STATUS:
- self._handle_status_update(message)
- elif message.event_type == ReportEventType.UPDATE_VISIBILITY:
- self._handle_visibility_update(message)
- else:
- logger.debug(f"Ignoring event type: {message.event_type}")
-
- except Exception as e:
- logger.error(f"Error processing notification message: {e}", exc_info=True)
- raise
-
- def _handle_status_update(self, message: ReportMessage):
- """Maneja la actualización de estado de un reporte"""
- try:
- logger.info(
- f"Creating notification for report {message.id_reporte} "
- f"status change from {message.old_estado} to {message.new_estado}"
- )
-
- # Crear notificación para el usuario propietario del reporte
- self.notification_service.send_report_status_notification(
- id_usuario=message.id_usuario,
- id_reporte=message.id_reporte,
- old_status=message.old_estado,
- new_status=message.new_estado
- )
-
- logger.info(f"Notification created for user {message.id_usuario}")
-
- except Exception as e:
- logger.error(f"Error creating status notification: {e}", exc_info=True)
- raise
-
- def _handle_visibility_update(self, message: ReportMessage):
- """Maneja la actualización de visibilidad de un reporte"""
- try:
- # Notificar si la visibilidad cambió significativamente
- if hasattr(message, 'old_visibility') and hasattr(message, 'new_visibility'):
- logger.info(
- f"Report {message.id_reporte} visibility changed "
- f"from {message.old_visibility} to {message.new_visibility}"
- )
-
- # Puedes agregar lógica para notificar sobre cambios de visibilidad
- # Por ahora solo registramos el evento
-
- except Exception as e:
- logger.error(f"Error handling visibility update: {e}", exc_info=True)
diff --git a/src/core/config.py b/src/core/config.py
index 66ce650..2a89b8f 100644
--- a/src/core/config.py
+++ b/src/core/config.py
@@ -11,29 +11,19 @@ class Settings(BaseSettings):
description="URL de conexión a MySQL para API de Usuarios"
)
- # Base de datos MongoDB - Reportes (Instancia Separada)
- mongodb_reports_url: str = Field(
- default=os.getenv("MONGODB_REPORTS_URL", "mongodb://admin:admin_password@localhost:27017"),
- description="URL de conexión a MongoDB para API de Reportes (Instancia 1)"
+ # Base de datos MongoDB
+ mongodb_url: str = Field(
+ default=os.getenv("MONGODB_URL", "mongodb://localhost:27017"),
+ description="URL de conexión a MongoDB para API de Reportes"
)
- mongodb_reports_db: str = Field(
+ mongodb_db: str = Field(
default="voxpopuli_reports",
- description="Base de datos MongoDB para Reportes"
+ description="Base de datos MongoDB"
)
- # Base de datos MongoDB - Notificaciones (Instancia Separada)
- mongodb_notifications_url: str = Field(
- default=os.getenv("MONGODB_NOTIFICATIONS_URL", "mongodb://admin:admin_password@localhost:27018"),
- description="URL de conexión a MongoDB para Notificaciones (Instancia 2)"
- )
- mongodb_notifications_db: str = Field(
- default="voxpopuli_notifications",
- description="Base de datos MongoDB para Notificaciones"
- )
+ rabbitmq: str = Field (
+ default=os.getenv("RABBITMQ_URI", "localhost")
- rabbitmq: str = Field(
- default=os.getenv("RABBITMQ_URI", "localhost"),
- description="URL de conexión a RabbitMQ"
)
# JWT Configuration
diff --git a/src/domain/notifications.py b/src/domain/notifications.py
deleted file mode 100644
index 8639cc8..0000000
--- a/src/domain/notifications.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from dataclasses import dataclass
-from datetime import datetime
-from typing import Optional
-
-
-@dataclass
-class Notification:
- """Modelo de dominio para Notificación"""
- id_notificacion: Optional[str] = None
- id_usuario: int = None
- id_reporte: str = None
- message: str = None
- fecha: Optional[datetime] = None
- read: bool = False
diff --git a/src/infrastructure/adapters/persistence/mongodb.py b/src/infrastructure/adapters/persistence/mongodb.py
index 1b05a4b..974f734 100644
--- a/src/infrastructure/adapters/persistence/mongodb.py
+++ b/src/infrastructure/adapters/persistence/mongodb.py
@@ -2,18 +2,10 @@ from pymongo import MongoClient
from pymongo.collection import Collection
from core.config import ConfSettings
-# Conexión a MongoDB para Reportes (Instancia Separada - Puerto 27017)
-mongo_client_reports = MongoClient(ConfSettings.mongodb_reports_url)
-mongodb_reports = mongo_client_reports[ConfSettings.mongodb_reports_db]
-
-# Conexión a MongoDB para Notificaciones (Instancia Separada - Puerto 27018)
-mongo_client_notifications = MongoClient(ConfSettings.mongodb_notifications_url)
-mongodb_notifications = mongo_client_notifications[ConfSettings.mongodb_notifications_db]
+# Conexión a MongoDB para Reportes
+mongo_client = MongoClient(ConfSettings.mongodb_url)
+mongodb = mongo_client[ConfSettings.mongodb_db]
def get_reports_collection() -> Collection:
- """Obtiene la colección de reportes desde MongoDB (Instancia 1)"""
- return mongodb_reports["reportes"]
-
-def get_notifications_collection() -> Collection:
- """Obtiene la colección de notificaciones desde MongoDB (Instancia 2)"""
- return mongodb_notifications["notificaciones"]
+ """Obtiene la colección de reportes desde MongoDB"""
+ return mongodb["reportes"]
diff --git a/src/infrastructure/adapters/persistence/notification_repository_mongo.py b/src/infrastructure/adapters/persistence/notification_repository_mongo.py
deleted file mode 100644
index 2967c13..0000000
--- a/src/infrastructure/adapters/persistence/notification_repository_mongo.py
+++ /dev/null
@@ -1,87 +0,0 @@
-from application.ports.notification_repository import NotificationRepository
-from domain.notifications import Notification
-from infrastructure.adapters.persistence.mongodb import get_notifications_collection
-from typing import List, Optional
-from bson import ObjectId
-from datetime import datetime
-
-
-class NotificationRepositoryMongo(NotificationRepository):
- """Implementación del repositorio de Notificaciones usando MongoDB (Instancia Separada)"""
-
- def __init__(self):
- self.collection = get_notifications_collection()
-
- def create(self, notification: Notification) -> str:
- """Crea una nueva notificación"""
- notification_dict = {
- "id_usuario": notification.id_usuario,
- "id_reporte": notification.id_reporte,
- "message": notification.message,
- "fecha": notification.fecha or datetime.utcnow(),
- "read": notification.read
- }
- result = self.collection.insert_one(notification_dict)
- return str(result.inserted_id)
-
- def get_by_id(self, notification_id: str) -> Optional[Notification]:
- """Obtiene una notificación por ID"""
- try:
- doc = self.collection.find_one({"_id": ObjectId(notification_id)})
- if doc:
- return self._to_domain(doc)
- except Exception:
- pass
- return None
-
- def get_by_user(self, user_id: int, limit: int = 50, offset: int = 0) -> List[Notification]:
- """Obtiene notificaciones de un usuario ordenadas por fecha descendente"""
- docs = self.collection.find(
- {"id_usuario": user_id}
- ).sort("fecha", -1).skip(offset).limit(limit)
- return [self._to_domain(doc) for doc in docs]
-
- def mark_as_read(self, notification_id: str) -> bool:
- """Marca una notificación como leída"""
- try:
- result = self.collection.update_one(
- {"_id": ObjectId(notification_id)},
- {"$set": {"read": True}}
- )
- return result.modified_count > 0
- except Exception:
- return False
-
- def mark_all_as_read(self, user_id: int) -> int:
- """Marca todas las notificaciones de un usuario como leídas"""
- result = self.collection.update_many(
- {"id_usuario": user_id, "read": False},
- {"$set": {"read": True}}
- )
- return result.modified_count
-
- def delete(self, notification_id: str) -> bool:
- """Elimina una notificación"""
- try:
- result = self.collection.delete_one({"_id": ObjectId(notification_id)})
- return result.deleted_count > 0
- except Exception:
- return False
-
- def get_unread_count(self, user_id: int) -> int:
- """Obtiene el número de notificaciones no leídas para un usuario"""
- return self.collection.count_documents({
- "id_usuario": user_id,
- "read": False
- })
-
- def _to_domain(self, doc: dict) -> Notification:
- """Convierte un documento de MongoDB a un objeto de dominio"""
- return Notification(
- id_notificacion=str(doc.get("_id")),
- id_usuario=doc.get("id_usuario"),
- id_reporte=doc.get("id_reporte"),
- message=doc.get("message"),
- fecha=doc.get("fecha"),
- read=doc.get("read", False)
- )
diff --git a/src/infrastructure/adapters/rabbitmq/messages.py b/src/infrastructure/adapters/rabbitmq/messages.py
index 631589c..18724f4 100644
--- a/src/infrastructure/adapters/rabbitmq/messages.py
+++ b/src/infrastructure/adapters/rabbitmq/messages.py
@@ -17,7 +17,6 @@ class ReportEventType(str, Enum):
"""Types of report events"""
CREATE = "report.create"
UPDATE_VISIBILITY = "report.update_visibility"
- UPDATE_STATUS = "report.update_status"
DELETE = "report.delete"
@@ -70,10 +69,6 @@ class ReportMessage:
estado: Optional[str] = None # Estado del reporte: "en proceso", "no resuelto", "resuelto"
fecha_creacion: Optional[str] = None # ISO format datetime string
penalize_author: Optional[bool] = None # For update_visibility event
- old_estado: Optional[str] = None # Estado anterior (para UPDATE_STATUS)
- new_estado: Optional[str] = None # Nuevo estado (para UPDATE_STATUS)
- old_visibility: Optional[float] = None # Visibilidad anterior (para UPDATE_VISIBILITY)
- new_visibility: Optional[float] = None # Nueva visibilidad (para UPDATE_VISIBILITY)
def to_dict(self):
"""Convert to dictionary"""
diff --git a/src/infrastructure/api/notifications/__init__.py b/src/infrastructure/api/notifications/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/src/infrastructure/api/notifications/app.py b/src/infrastructure/api/notifications/app.py
deleted file mode 100644
index 39dab89..0000000
--- a/src/infrastructure/api/notifications/app.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from fastapi import FastAPI
-from core.config import ConfSettings
-from infrastructure.api.notifications.router import router
-
-
-def create_app() -> FastAPI:
- """Factory para crear la aplicación de Notificaciones"""
- app = FastAPI(
- title="Notificaciones Microservice",
- version="1.0.0",
- description="Microservicio de gestión de notificaciones de reportes"
- )
- app.include_router(router)
- return app
diff --git a/src/infrastructure/api/notifications/notifications.py b/src/infrastructure/api/notifications/notifications.py
deleted file mode 100644
index 9e2faea..0000000
--- a/src/infrastructure/api/notifications/notifications.py
+++ /dev/null
@@ -1,148 +0,0 @@
-from fastapi import APIRouter, HTTPException, Query, Depends, Header
-from infrastructure.api.notifications.schemas import (
- NotificationCreateRequest,
- NotificationResponse,
- NotificationListResponse,
- UnreadCountResponse,
- NotificationMarkAsReadRequest
-)
-from application.services.notification_services import NotificationService
-from typing import Optional
-
-router = APIRouter()
-notification_service = NotificationService()
-
-
-@router.post("/", response_model=NotificationResponse, tags=["notifications"])
-async def create_notification(request: NotificationCreateRequest):
- """Crea una nueva notificación (uso interno)"""
- try:
- notification_id = notification_service.create_notification(
- id_usuario=request.id_usuario,
- id_reporte=request.id_reporte,
- message=request.message
- )
-
- notification = notification_service.get_notification(notification_id)
- if not notification:
- raise HTTPException(status_code=500, detail="Error creating notification")
-
- return NotificationResponse(
- id_notificacion=notification.id_notificacion,
- id_usuario=notification.id_usuario,
- id_reporte=notification.id_reporte,
- message=notification.message,
- fecha=notification.fecha,
- read=notification.read
- )
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
-
-
-@router.get("/{user_id}", response_model=NotificationListResponse, tags=["notifications"])
-async def get_user_notifications(
- user_id: int,
- limit: int = Query(50, ge=1, le=100),
- offset: int = Query(0, ge=0)
-):
- """
- Obtiene notificaciones de un usuario
-
- Args:
- user_id: ID del usuario
- limit: Número máximo de notificaciones (default: 50, max: 100)
- offset: Desplazamiento de registros
- """
- try:
- notifications = notification_service.get_user_notifications(
- user_id=user_id,
- limit=limit,
- offset=offset
- )
-
- unread_count = notification_service.get_unread_count(user_id)
- total = len(notifications) + offset # Aproximado
-
- notification_responses = [
- NotificationResponse(
- id_notificacion=n.id_notificacion,
- id_usuario=n.id_usuario,
- id_reporte=n.id_reporte,
- message=n.message,
- fecha=n.fecha,
- read=n.read
- )
- for n in notifications
- ]
-
- return NotificationListResponse(
- total=total,
- unread_count=unread_count,
- notifications=notification_responses
- )
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
-
-
-@router.get("/{user_id}/unread-count", response_model=UnreadCountResponse, tags=["notifications"])
-async def get_unread_count(user_id: int):
- """Obtiene el número de notificaciones no leídas para un usuario"""
- try:
- unread_count = notification_service.get_unread_count(user_id)
- return UnreadCountResponse(unread_count=unread_count)
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
-
-
-@router.put("/{notification_id}/read", response_model=NotificationResponse, tags=["notifications"])
-async def mark_notification_as_read(notification_id: str):
- """Marca una notificación como leída"""
- try:
- success = notification_service.mark_as_read(notification_id)
- if not success:
- raise HTTPException(status_code=404, detail="Notification not found")
-
- notification = notification_service.get_notification(notification_id)
- if not notification:
- raise HTTPException(status_code=404, detail="Notification not found")
-
- return NotificationResponse(
- id_notificacion=notification.id_notificacion,
- id_usuario=notification.id_usuario,
- id_reporte=notification.id_reporte,
- message=notification.message,
- fecha=notification.fecha,
- read=notification.read
- )
- except HTTPException:
- raise
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
-
-
-@router.put("/{user_id}/read-all", response_model=dict, tags=["notifications"])
-async def mark_all_as_read(user_id: int):
- """Marca todas las notificaciones de un usuario como leídas"""
- try:
- updated_count = notification_service.mark_all_as_read(user_id)
- return {
- "message": "All notifications marked as read",
- "updated_count": updated_count
- }
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
-
-
-@router.delete("/{notification_id}", response_model=dict, tags=["notifications"])
-async def delete_notification(notification_id: str):
- """Elimina una notificación"""
- try:
- success = notification_service.delete_notification(notification_id)
- if not success:
- raise HTTPException(status_code=404, detail="Notification not found")
-
- return {"message": "Notification deleted successfully"}
- except HTTPException:
- raise
- except Exception as e:
- raise HTTPException(status_code=500, detail=str(e))
diff --git a/src/infrastructure/api/notifications/root.py b/src/infrastructure/api/notifications/root.py
deleted file mode 100644
index 3b08833..0000000
--- a/src/infrastructure/api/notifications/root.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from fastapi import APIRouter
-from pydantic import BaseModel
-
-router = APIRouter()
-
-
-class HealthCheck(BaseModel):
- """Health check response"""
- status: str
- service: str
-
-
-@router.get("/", response_model=HealthCheck, tags=["health"])
-async def health_check():
- """Health check endpoint"""
- return {
- "status": "ok",
- "service": "Notificaciones Microservice"
- }
diff --git a/src/infrastructure/api/notifications/router.py b/src/infrastructure/api/notifications/router.py
deleted file mode 100644
index 4fb6834..0000000
--- a/src/infrastructure/api/notifications/router.py
+++ /dev/null
@@ -1,17 +0,0 @@
-from fastapi import APIRouter
-from infrastructure.api.notifications.notifications import router as notifications_router
-from infrastructure.api.notifications.root import router as root_router
-
-router = APIRouter()
-
-router.include_router(
- notifications_router,
- prefix="/notifications",
- tags=["notifications"]
-)
-
-router.include_router(
- root_router,
- prefix='',
- tags=["root"]
-)
diff --git a/src/infrastructure/api/notifications/schemas.py b/src/infrastructure/api/notifications/schemas.py
deleted file mode 100644
index a69351f..0000000
--- a/src/infrastructure/api/notifications/schemas.py
+++ /dev/null
@@ -1,43 +0,0 @@
-from pydantic import BaseModel
-from datetime import datetime
-from typing import Optional
-
-
-class NotificationCreateRequest(BaseModel):
- """Solicitud para crear una notificación"""
- id_usuario: int
- id_reporte: str
- message: str
-
-
-class NotificationResponse(BaseModel):
- """Respuesta con datos de notificación"""
- id_notificacion: str
- id_usuario: int
- id_reporte: str
- message: str
- fecha: datetime
- read: bool
-
- class Config:
- from_attributes = True
-
-
-class NotificationListResponse(BaseModel):
- """Respuesta con lista de notificaciones"""
- total: int
- unread_count: int
- notifications: list[NotificationResponse]
-
- class Config:
- from_attributes = True
-
-
-class NotificationMarkAsReadRequest(BaseModel):
- """Solicitud para marcar como leída"""
- pass
-
-
-class UnreadCountResponse(BaseModel):
- """Respuesta con conteo de no leídas"""
- unread_count: int
diff --git a/src/main.py b/src/main.py
index 02666af..7f608f9 100644
--- a/src/main.py
+++ b/src/main.py
@@ -1,13 +1,11 @@
"""
Punto de entrada principal para VoxPopuli Microservices
-Ejecuta tres APIs en paralelo: Usuarios (puerto 8000), Reportes (puerto 8001) y Notificaciones (puerto 8002)
+Ejecuta dos APIs en paralelo: Usuarios (puerto 8000) y Reportes (puerto 8001)
"""
from infrastructure.api.users.app import create_app as create_users_app
from infrastructure.api.reports.app import create_app as create_reports_app
-from infrastructure.api.notifications.app import create_app as create_notifications_app
from consumers.report_consumer import ReportConsumer
from consumers.user_consumer import UserConsumer
-from consumers.notification_consumer import NotificationConsumer
from core.config import ConfSettings
import threading
import uvicorn
@@ -34,17 +32,6 @@ def run_reports_api():
log_level=ConfSettings.log_level,
)
-def run_notifications_api():
- """Ejecuta la API de Notificaciones en puerto 8002"""
- app_notifications = create_notifications_app()
- uvicorn.run(
- app_notifications,
- host=ConfSettings.host,
- port=8002,
- reload=False,
- log_level=ConfSettings.log_level,
- )
-
def run_user_consumer():
consumer = UserConsumer()
consumer.start()
@@ -53,10 +40,6 @@ def run_reports_consumer():
consumer = ReportConsumer()
consumer.start()
-def run_notifications_consumer():
- consumer = NotificationConsumer()
- consumer.start()
-
def run():
"""Inicia ambas APIs en threads separados"""
@@ -66,32 +49,25 @@ def run():
users_thread = threading.Thread(target=run_users_api, daemon=True, name="Users-API")
reports_thread = threading.Thread(target=run_reports_api, daemon=True, name="Reports-API")
- notifications_thread = threading.Thread(target=run_notifications_api, daemon=True, name="Notifications-API")
user_consumer_thread = threading.Thread(target=run_user_consumer, daemon=True, name="Users-Consumer")
report_consumer_thread = threading.Thread(target=run_reports_consumer, daemon=True, name="Reports-Consumer")
- notifications_consumer_thread = threading.Thread(target=run_notifications_consumer, daemon=True, name="Notifications-Consumer")
users_thread.start()
reports_thread.start()
- notifications_thread.start()
user_consumer_thread.start()
report_consumer_thread.start()
- notifications_consumer_thread.start()
print("\n✓ API de Usuarios ejecutándose en http://0.0.0.0:8000")
print("✓ API de Reportes ejecutándose en http://0.0.0.0:8001")
- print("✓ API de Notificaciones ejecutándose en http://0.0.0.0:8002")
print("\nDocumentación disponible en:")
print(" - Usuarios: http://localhost:8000/docs")
print(" - Reportes: http://localhost:8001/docs")
- print(" - Notificaciones: http://localhost:8002/docs")
print("\n" + "=" * 60 + "\n")
try:
users_thread.join()
reports_thread.join()
- notifications_thread.join()
except KeyboardInterrupt:
print("\n\nRecibiendo señal de salida...")
print("Cerrando APIs...")