Fixed metrics api to recieve all metrics

This commit is contained in:
2026-05-06 01:30:20 -06:00
parent 543ba8870c
commit 7331dcb086
5 changed files with 213 additions and 459 deletions

View File

@@ -4,7 +4,6 @@ import os
import logging import logging
from datetime import datetime from datetime import datetime
# Add src to path to import modules
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..')) sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..'))
from infrastructure.adapters.rabbitmq.consumer import RabbitMQConsumer from infrastructure.adapters.rabbitmq.consumer import RabbitMQConsumer
@@ -14,7 +13,6 @@ from infrastructure.adapters.rabbitmq.messages import (
from application.services.metrics_services import MetricsService from application.services.metrics_services import MetricsService
from infrastructure.adapters.persistence.metrics_repository_postgres import MetricsRepositoryPostgres from infrastructure.adapters.persistence.metrics_repository_postgres import MetricsRepositoryPostgres
# Set up logging
logging.basicConfig( logging.basicConfig(
level=logging.INFO, level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
@@ -25,27 +23,32 @@ logging.basicConfig(
) )
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Colas dedicadas para métricas — no compiten con los consumers de dominio
METRICS_QUEUES = {
'user': 'metrics_users_queue',
'report': 'metrics_reports_queue',
'notification': 'metrics_notifications_queue',
'moderation': 'metrics_moderations_queue',
}
class MetricsConsumer: class MetricsConsumer:
"""Consumer for all system events from RabbitMQ""" """Consumer for all system events from RabbitMQ"""
def __init__(self): def __init__(self):
self.metrics_service = MetricsService(MetricsRepositoryPostgres()) self.metrics_service = MetricsService(MetricsRepositoryPostgres())
# Create separate consumers for each event type self.user_consumer = RabbitMQConsumer(queue_name=METRICS_QUEUES['user'])
self.user_consumer = RabbitMQConsumer(queue_name='users_queue') self.report_consumer = RabbitMQConsumer(queue_name=METRICS_QUEUES['report'])
self.report_consumer = RabbitMQConsumer(queue_name='reports_queue') self.notification_consumer = RabbitMQConsumer(queue_name=METRICS_QUEUES['notification'])
self.notification_consumer = RabbitMQConsumer(queue_name='notifications_queue') self.moderation_consumer = RabbitMQConsumer(queue_name=METRICS_QUEUES['moderation'])
self.moderation_consumer = RabbitMQConsumer(queue_name='moderations_queue')
# Set callbacks
self.user_consumer.set_callback(self.process_user_event) self.user_consumer.set_callback(self.process_user_event)
self.report_consumer.set_callback(self.process_report_event) self.report_consumer.set_callback(self.process_report_event)
self.notification_consumer.set_callback(self.process_notification_event) self.notification_consumer.set_callback(self.process_notification_event)
self.moderation_consumer.set_callback(self.process_moderation_event) self.moderation_consumer.set_callback(self.process_moderation_event)
def process_user_event(self, message_dict: dict): def process_user_event(self, message_dict: dict):
"""Procesa eventos de usuario"""
try: try:
message = UserMessage.from_dict(message_dict) message = UserMessage.from_dict(message_dict)
self.metrics_service.record_event( self.metrics_service.record_event(
@@ -58,107 +61,83 @@ class MetricsConsumer:
logger.info(f"Métrica registrada: user_{message.event_type.value}") logger.info(f"Métrica registrada: user_{message.event_type.value}")
except Exception as e: except Exception as e:
logger.error(f"Error procesando evento de usuario: {e}") logger.error(f"Error procesando evento de usuario: {e}")
def process_report_event(self, message_dict: dict): def process_report_event(self, message_dict: dict):
"""Procesa eventos de reportes"""
try: try:
message = ReportMessage.from_dict(message_dict) message = ReportMessage.from_dict(message_dict)
self.metrics_service.record_event( self.metrics_service.record_event(
event_type=f"report_{message.event_type.value}", event_type=f"report_{message.event_type.value}",
entity_id=str(message.report_id), entity_id=str(message.id_reporte),
entity_type="report", entity_type="report",
user_id=message.user_id, user_id=message.id_usuario,
metadata={"status": message.status} metadata={"estado": message.estado}
) )
logger.info(f"Métrica registrada: report_{message.event_type.value}") logger.info(f"Métrica registrada: report_{message.event_type.value}")
except Exception as e: except Exception as e:
logger.error(f"Error procesando evento de reporte: {e}") logger.error(f"Error procesando evento de reporte: {e}")
def process_notification_event(self, message_dict: dict): def process_notification_event(self, message_dict: dict):
"""Procesa eventos de notificaciones"""
try: try:
message = NotificationMessage.from_dict(message_dict) message = NotificationMessage.from_dict(message_dict)
self.metrics_service.record_event( self.metrics_service.record_event(
event_type=f"notification_{message.event_type.value}", event_type=f"notification_{message.event_type.value}",
entity_id=str(message.notification_id), entity_id=str(message.id_notificacion),
entity_type="notification", entity_type="notification",
user_id=message.user_id, user_id=message.id_usuario,
metadata={"type": message.notification_type} metadata={"type": message.tipo_notificacion}
) )
logger.info(f"Métrica registrada: notification_{message.event_type.value}") logger.info(f"Métrica registrada: notification_{message.event_type.value}")
except Exception as e: except Exception as e:
logger.error(f"Error procesando evento de notificación: {e}") logger.error(f"Error procesando evento de notificación: {e}")
def process_moderation_event(self, message_dict: dict): def process_moderation_event(self, message_dict: dict):
"""Procesa eventos de moderación"""
try: try:
message = ModerationMessage.from_dict(message_dict) message = ModerationMessage.from_dict(message_dict)
self.metrics_service.record_event( self.metrics_service.record_event(
event_type=f"moderation_{message.event_type.value}", event_type=f"moderation_{message.event_type.value}",
entity_id=str(message.moderation_id), entity_id=str(message.action_id),
entity_type="moderation", entity_type="moderation",
user_id=message.moderator_id, user_id=message.moderator_id,
metadata={"action": message.action} metadata={"action": message.review_action, "reason": message.reason}
) )
logger.info(f"Métrica registrada: moderation_{message.event_type.value}") logger.info(f"Métrica registrada: moderation_{message.event_type.value}")
except Exception as e: except Exception as e:
logger.error(f"Error procesando evento de moderación: {e}") logger.error(f"Error procesando evento de moderación: {e}")
def start(self): def start(self):
"""Inicia todos los consumers"""
logger.info("Iniciando Metrics Consumer...") logger.info("Iniciando Metrics Consumer...")
try: try:
# Start consumers in separate threads
import threading import threading
def start_user_consumer():
try:
self.user_consumer.start_consuming()
except Exception as e:
logger.error(f"Error en user consumer: {e}")
def start_report_consumer():
try:
self.report_consumer.start_consuming()
except Exception as e:
logger.error(f"Error en report consumer: {e}")
def start_notification_consumer():
try:
self.notification_consumer.start_consuming()
except Exception as e:
logger.error(f"Error en notification consumer: {e}")
def start_moderation_consumer():
try:
self.moderation_consumer.start_consuming()
except Exception as e:
logger.error(f"Error en moderation consumer: {e}")
threads = [ threads = [
threading.Thread(target=start_user_consumer, daemon=True), threading.Thread(target=self._safe_consume, args=(self.user_consumer,), daemon=True),
threading.Thread(target=start_report_consumer, daemon=True), threading.Thread(target=self._safe_consume, args=(self.report_consumer,), daemon=True),
threading.Thread(target=start_notification_consumer, daemon=True), threading.Thread(target=self._safe_consume, args=(self.notification_consumer,), daemon=True),
threading.Thread(target=start_moderation_consumer, daemon=True), threading.Thread(target=self._safe_consume, args=(self.moderation_consumer,), daemon=True),
] ]
for t in threads: for t in threads:
t.start() t.start()
# Keep main thread alive
for t in threads: for t in threads:
t.join() t.join()
except KeyboardInterrupt: except KeyboardInterrupt:
logger.info("Metrics Consumer detenido") logger.info("Metrics Consumer detenido")
self.stop() self.stop()
def _safe_consume(self, consumer: RabbitMQConsumer):
try:
consumer.start_consuming()
except Exception as e:
logger.error(f"Error en consumer de {consumer.queue_name}: {e}")
def stop(self): def stop(self):
"""Detiene los consumers""" for c in [self.user_consumer, self.report_consumer,
self.user_consumer.stop() self.notification_consumer, self.moderation_consumer]:
self.report_consumer.stop() try:
self.notification_consumer.stop() c.stop()
self.moderation_consumer.stop() except Exception:
pass
if __name__ == "__main__": if __name__ == "__main__":
consumer = MetricsConsumer() consumer = MetricsConsumer()
consumer.start() consumer.start()

View File

@@ -3,41 +3,34 @@ import pika
import json import json
from typing import Any, Dict from typing import Any, Dict
import logging import logging
import os
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _get_rabbitmq_host() -> str:
try:
from core.config import ConfSettings
return ConfSettings.rabbitmq
except Exception:
return os.getenv("RABBITMQ_URI", "localhost")
class RabbitMQSender: class RabbitMQSender:
"""Generic RabbitMQ sender for publishing messages to queues""" """Generic RabbitMQ sender for publishing messages to queues"""
def __init__(self, host: str = 'localhost', port: int = 5672): def __init__(self, host: str = None, port: int = 5672):
self.host = host self.host = host or _get_rabbitmq_host()
self.port = port self.port = port
def send_message(self, queue_name: str, message: Dict[str, Any]) -> bool: def send_message(self, queue_name: str, message: Dict[str, Any]) -> bool:
"""
Sends a message to a RabbitMQ queue
Args:
queue_name: Name of the queue to send to
message: Dictionary containing the message data
Returns:
True if successful, False otherwise
"""
try: try:
connection = pika.BlockingConnection( connection = pika.BlockingConnection(
pika.ConnectionParameters(host=self.host, port=self.port) pika.ConnectionParameters(host=self.host, port=self.port)
) )
channel = connection.channel() channel = connection.channel()
# Declare queue to ensure it exists
channel.queue_declare(queue=queue_name, durable=True) channel.queue_declare(queue=queue_name, durable=True)
# Convert message to JSON
message_json = json.dumps(message) message_json = json.dumps(message)
# Publish the message
channel.basic_publish( channel.basic_publish(
exchange='', exchange='',
routing_key=queue_name, routing_key=queue_name,
@@ -46,29 +39,15 @@ class RabbitMQSender:
delivery_mode=pika.spec.PERSISTENT_DELIVERY_MODE delivery_mode=pika.spec.PERSISTENT_DELIVERY_MODE
) )
) )
connection.close() connection.close()
logger.info(f"Message sent to queue '{queue_name}': {message_json}") logger.info(f"Message sent to queue '{queue_name}': {message_json}")
return True return True
except Exception as e: except Exception as e:
logger.error(f"Error sending message to RabbitMQ: {e}") logger.error(f"Error sending message to RabbitMQ: {e}")
return False return False
def send_to_queue(queue_name: str, message: Dict[str, Any], def send_to_queue(queue_name: str, message: Dict[str, Any],
host: str = 'localhost', port: int = 5672) -> bool: host: str = None, port: int = 5672) -> bool:
"""
Convenience function to send a message to RabbitMQ
Args:
queue_name: Name of the queue
message: Message dictionary
host: RabbitMQ host
port: RabbitMQ port
Returns:
True if successful, False otherwise
"""
sender = RabbitMQSender(host=host, port=port) sender = RabbitMQSender(host=host, port=port)
return sender.send_message(queue_name, message) return sender.send_message(queue_name, message)

View File

@@ -2,76 +2,67 @@
from fastapi import APIRouter, HTTPException, Depends from fastapi import APIRouter, HTTPException, Depends
from fastapi.responses import JSONResponse from fastapi.responses import JSONResponse
from infrastructure.api.moderations.schemas import ( from infrastructure.api.moderations.schemas import (
DeleteReportRequest, DeleteReportRequest, CloseAccountRequest, BanUserRequest,
CloseAccountRequest, WarnUserRequest, ReviewContentRequest, ModerationActionResponse
BanUserRequest,
WarnUserRequest,
ReviewContentRequest,
ModerationActionResponse
) )
from infrastructure.api.auth import get_current_admin_user from infrastructure.api.auth import get_current_admin_user
from application.services.moderation_services import ( from application.services.moderation_services import (
DeleteReportUseCase, DeleteReportUseCase, CloseAccountUseCase, BanUserUseCase,
CloseAccountUseCase, WarnUserUseCase, ReviewContentUseCase
BanUserUseCase,
WarnUserUseCase,
ReviewContentUseCase
) )
from infrastructure.adapters.persistence.mongodb import mongodb from infrastructure.adapters.persistence.mongodb import mongodb
from infrastructure.adapters.persistence.db import get_db from infrastructure.adapters.persistence.db import get_db
from infrastructure.adapters.moderation_repository_mongo import ModerationRepositoryMongo from infrastructure.adapters.moderation_repository_mongo import ModerationRepositoryMongo
from infrastructure.adapters.rabbitmq.sender import send_to_queue
from infrastructure.adapters.rabbitmq.messages import ModerationMessage, ModerationEventType
from domain.users import User from domain.users import User
from sqlalchemy.orm import Session from sqlalchemy.orm import Session
import logging import logging
from datetime import datetime
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
router = APIRouter() router = APIRouter()
# Instanciar repositorio de moderación
moderation_repo = ModerationRepositoryMongo() moderation_repo = ModerationRepositoryMongo()
def _publish_moderation_event(event_type: ModerationEventType, **kwargs):
try:
msg = ModerationMessage(event_type=event_type, fecha_creacion=datetime.utcnow().isoformat(), **kwargs)
payload = msg.to_dict()
send_to_queue('moderations_queue', payload)
send_to_queue('metrics_moderations_queue', payload)
except Exception as e:
logger.warning(f"Error publicando evento de moderación a RabbitMQ: {e}")
@router.post("/reports/delete", response_model=ModerationActionResponse) @router.post("/reports/delete", response_model=ModerationActionResponse)
async def delete_report( async def delete_report(request: DeleteReportRequest, current_admin: User = Depends(get_current_admin_user)):
request: DeleteReportRequest,
current_admin: User = Depends(get_current_admin_user)
):
""" """
Eliminar un reporte como moderador (requiere permisos de admin) Eliminar un reporte como moderador (requiere permisos de admin)
- **report_id**: ID del reporte a eliminar - **report_id**: ID del reporte a eliminar
- **reason**: Razón de la eliminación (mínimo 5 caracteres) - **reason**: Razón de la eliminación (mínimo 5 caracteres)
- **description**: Descripción adicional (opcional) - **description**: Descripción adicional (opcional)
""" """
try: try:
use_case = DeleteReportUseCase(moderation_repo) use_case = DeleteReportUseCase(moderation_repo)
result = use_case.execute( result = use_case.execute(moderator_id=current_admin.user_id, report_id=request.report_id, reason=request.reason, description=request.description)
moderator_id=current_admin.user_id,
report_id=request.report_id,
reason=request.reason,
description=request.description
)
if result["status"] == "error": if result["status"] == "error":
raise HTTPException(status_code=400, detail=result["message"]) raise HTTPException(status_code=400, detail=result["message"])
_publish_moderation_event(ModerationEventType.DELETE_REPORT, moderator_id=current_admin.user_id, report_id=request.report_id, reason=request.reason, description=request.description, action_id=result.get("action_id"))
return ModerationActionResponse(**result) return ModerationActionResponse(**result)
except HTTPException:
raise
except Exception as e: except Exception as e:
logger.error(f"Error in delete_report: {e}") logger.error(f"Error in delete_report: {e}")
raise HTTPException(status_code=500, detail="Error interno del servidor") raise HTTPException(status_code=500, detail="Error interno del servidor")
@router.post("/accounts/close", response_model=ModerationActionResponse) @router.post("/accounts/close", response_model=ModerationActionResponse)
async def close_account( async def close_account(request: CloseAccountRequest, current_admin: User = Depends(get_current_admin_user)):
request: CloseAccountRequest,
current_admin: User = Depends(get_current_admin_user)
):
""" """
Cerrar una cuenta de usuario (requiere permisos de admin) Cerrar una cuenta de usuario (requiere permisos de admin)
- **user_id**: ID del usuario cuya cuenta cerrar - **user_id**: ID del usuario cuya cuenta cerrar
- **reason**: Razón del cierre (mínimo 5 caracteres) - **reason**: Razón del cierre (mínimo 5 caracteres)
- **description**: Descripción adicional (opcional) - **description**: Descripción adicional (opcional)
@@ -79,32 +70,23 @@ async def close_account(
""" """
try: try:
use_case = CloseAccountUseCase(moderation_repo) use_case = CloseAccountUseCase(moderation_repo)
result = use_case.execute( result = use_case.execute(moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, description=request.description, is_permanent=request.is_permanent)
moderator_id=current_admin.user_id,
user_id=request.user_id,
reason=request.reason,
description=request.description,
is_permanent=request.is_permanent
)
if result["status"] == "error": if result["status"] == "error":
raise HTTPException(status_code=400, detail=result["message"]) raise HTTPException(status_code=400, detail=result["message"])
_publish_moderation_event(ModerationEventType.CLOSE_ACCOUNT, moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, description=request.description, is_permanent=request.is_permanent, action_id=result.get("action_id"))
return ModerationActionResponse(**result) return ModerationActionResponse(**result)
except HTTPException:
raise
except Exception as e: except Exception as e:
logger.error(f"Error in close_account: {e}") logger.error(f"Error in close_account: {e}")
raise HTTPException(status_code=500, detail="Error interno del servidor") raise HTTPException(status_code=500, detail="Error interno del servidor")
@router.post("/users/ban", response_model=ModerationActionResponse) @router.post("/users/ban", response_model=ModerationActionResponse)
async def ban_user( async def ban_user(request: BanUserRequest, current_admin: User = Depends(get_current_admin_user)):
request: BanUserRequest,
current_admin: User = Depends(get_current_admin_user)
):
""" """
Banear a un usuario (requiere permisos de admin) Banear a un usuario (requiere permisos de admin)
- **user_id**: ID del usuario a banear - **user_id**: ID del usuario a banear
- **reason**: Razón del ban (mínimo 5 caracteres) - **reason**: Razón del ban (mínimo 5 caracteres)
- **duration_days**: Duración del ban en días (None para permanente) - **duration_days**: Duración del ban en días (None para permanente)
@@ -112,63 +94,46 @@ async def ban_user(
""" """
try: try:
use_case = BanUserUseCase(moderation_repo) use_case = BanUserUseCase(moderation_repo)
result = use_case.execute( result = use_case.execute(moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, duration_days=request.duration_days, description=request.description)
moderator_id=current_admin.user_id,
user_id=request.user_id,
reason=request.reason,
duration_days=request.duration_days,
description=request.description
)
if result["status"] == "error": if result["status"] == "error":
raise HTTPException(status_code=400, detail=result["message"]) raise HTTPException(status_code=400, detail=result["message"])
_publish_moderation_event(ModerationEventType.BAN_USER, moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, duration_days=request.duration_days, description=request.description, is_permanent=request.duration_days is None, action_id=result.get("action_id"))
return ModerationActionResponse(**result) return ModerationActionResponse(**result)
except HTTPException:
raise
except Exception as e: except Exception as e:
logger.error(f"Error in ban_user: {e}") logger.error(f"Error in ban_user: {e}")
raise HTTPException(status_code=500, detail="Error interno del servidor") raise HTTPException(status_code=500, detail="Error interno del servidor")
@router.post("/users/warn", response_model=ModerationActionResponse) @router.post("/users/warn", response_model=ModerationActionResponse)
async def warn_user( async def warn_user(request: WarnUserRequest, current_admin: User = Depends(get_current_admin_user)):
request: WarnUserRequest,
current_admin: User = Depends(get_current_admin_user)
):
""" """
Advertir a un usuario (requiere permisos de admin) Advertir a un usuario (requiere permisos de admin)
- **user_id**: ID del usuario a advertir - **user_id**: ID del usuario a advertir
- **reason**: Razón de la advertencia (mínimo 5 caracteres) - **reason**: Razón de la advertencia (mínimo 5 caracteres)
- **description**: Descripción adicional (opcional) - **description**: Descripción adicional (opcional)
""" """
try: try:
use_case = WarnUserUseCase(moderation_repo) use_case = WarnUserUseCase(moderation_repo)
result = use_case.execute( result = use_case.execute(moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, description=request.description)
moderator_id=current_admin.user_id,
user_id=request.user_id,
reason=request.reason,
description=request.description
)
if result["status"] == "error": if result["status"] == "error":
raise HTTPException(status_code=400, detail=result["message"]) raise HTTPException(status_code=400, detail=result["message"])
_publish_moderation_event(ModerationEventType.WARN_USER, moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, description=request.description, action_id=result.get("action_id"))
return ModerationActionResponse(**result) return ModerationActionResponse(**result)
except HTTPException:
raise
except Exception as e: except Exception as e:
logger.error(f"Error in warn_user: {e}") logger.error(f"Error in warn_user: {e}")
raise HTTPException(status_code=500, detail="Error interno del servidor") raise HTTPException(status_code=500, detail="Error interno del servidor")
@router.post("/content/review", response_model=ModerationActionResponse) @router.post("/content/review", response_model=ModerationActionResponse)
async def review_content( async def review_content(request: ReviewContentRequest, current_admin: User = Depends(get_current_admin_user)):
request: ReviewContentRequest,
current_admin: User = Depends(get_current_admin_user)
):
""" """
Revisar y actuar sobre contenido reportado (requiere permisos de admin) Revisar y actuar sobre contenido reportado (requiere permisos de admin)
- **report_id**: ID del reporte a revisar - **report_id**: ID del reporte a revisar
- **action**: Acción a tomar (approve, reject, needs_more_info) - **action**: Acción a tomar (approve, reject, needs_more_info)
- **reason**: Razón de la decisión (opcional) - **reason**: Razón de la decisión (opcional)
@@ -176,19 +141,13 @@ async def review_content(
""" """
try: try:
use_case = ReviewContentUseCase(moderation_repo) use_case = ReviewContentUseCase(moderation_repo)
result = use_case.execute( result = use_case.execute(moderator_id=current_admin.user_id, report_id=request.report_id, action=request.action, reason=request.reason, notes=request.notes)
moderator_id=current_admin.user_id,
report_id=request.report_id,
action=request.action,
reason=request.reason,
notes=request.notes
)
if result["status"] == "error": if result["status"] == "error":
raise HTTPException(status_code=400, detail=result["message"]) raise HTTPException(status_code=400, detail=result["message"])
_publish_moderation_event(ModerationEventType.REVIEW_CONTENT, moderator_id=current_admin.user_id, report_id=request.report_id, review_action=request.action, reason=request.reason, notes=request.notes, action_id=result.get("action_id"))
return ModerationActionResponse(**result) return ModerationActionResponse(**result)
except HTTPException:
raise
except Exception as e: except Exception as e:
logger.error(f"Error in review_content: {e}") logger.error(f"Error in review_content: {e}")
raise HTTPException(status_code=500, detail="Error interno del servidor") raise HTTPException(status_code=500, detail="Error interno del servidor")

View File

@@ -8,7 +8,10 @@ from infrastructure.adapters.persistence.report_repository_mongo import ReportRe
from infrastructure.adapters.persistence.user_repository_sql import UserRepositorySQL from infrastructure.adapters.persistence.user_repository_sql import UserRepositorySQL
from infrastructure.adapters.file_storage import image_storage from infrastructure.adapters.file_storage import image_storage
from infrastructure.adapters.rabbitmq.sender import send_to_queue from infrastructure.adapters.rabbitmq.sender import send_to_queue
from infrastructure.adapters.rabbitmq.messages import NotificationMessage, NotificationEventType from infrastructure.adapters.rabbitmq.messages import (
NotificationMessage, NotificationEventType,
ReportMessage, ReportEventType
)
import logging import logging
from typing import Optional from typing import Optional
from datetime import datetime from datetime import datetime
@@ -21,7 +24,6 @@ logger = logging.getLogger(__name__)
def _report_to_response(report) -> dict: def _report_to_response(report) -> dict:
"""Convierte un objeto Report a dict con image_url"""
return { return {
"id_reporte": report.id_reporte, "id_reporte": report.id_reporte,
"id_usuario": report.id_usuario, "id_usuario": report.id_usuario,
@@ -36,6 +38,17 @@ def _report_to_response(report) -> dict:
"fecha_creacion": report.fecha_creacion "fecha_creacion": report.fecha_creacion
} }
def _publish_report_event(event_type: ReportEventType, **kwargs):
try:
msg = ReportMessage(event_type=event_type, **kwargs)
payload = msg.to_dict()
send_to_queue('reports_queue', payload)
send_to_queue('metrics_reports_queue', payload)
except Exception as e:
logger.warning(f"Error publicando evento de reporte a RabbitMQ: {e}")
@router.post("/", status_code=status.HTTP_202_ACCEPTED) @router.post("/", status_code=status.HTTP_202_ACCEPTED)
async def create_report( async def create_report(
id_usuario: int = Form(...), id_usuario: int = Form(...),
@@ -49,54 +62,38 @@ async def create_report(
): ):
"""Crea un nuevo reporte - envía a cola de procesamiento con validaciones previas""" """Crea un nuevo reporte - envía a cola de procesamiento con validaciones previas"""
try: try:
# Procesar imagen si fue proporcionada
image_filename = None image_filename = None
if file: if file:
logger.info(f"Processing image file: {file.filename} ({file.content_type})") logger.info(f"Processing image file: {file.filename} ({file.content_type})")
image_filename = image_storage.validate_and_save_image(file, f"temp_{id_usuario}_{tipo_reporte}") image_filename = image_storage.validate_and_save_image(file, f"temp_{id_usuario}_{tipo_reporte}")
create_use_case = CreateReport(report_repo, user_repo) create_use_case = CreateReport(report_repo, user_repo)
result = create_use_case.execute( result = create_use_case.execute(
id_usuario=id_usuario, id_usuario=id_usuario, tipo_reporte=tipo_reporte, descripcion=descripcion,
tipo_reporte=tipo_reporte, ubicacion=ubicacion, lat=lat, lng=lng, image_filename=image_filename, estado=estado
descripcion=descripcion,
ubicacion=ubicacion,
lat=lat,
lng=lng,
image_filename=image_filename,
estado=estado
) )
if result["status"] == "error": if result["status"] == "error":
# Si hay error, eliminar imagen si fue guardada
if image_filename: if image_filename:
image_storage.delete_image(image_filename) image_storage.delete_image(image_filename)
message = result["message"] message = result["message"]
if "no existe" in message: if "no existe" in message:
# 404 Not Found: usuario no existe raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=message)
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
status_code=status.HTTP_404_NOT_FOUND,
detail=message _publish_report_event(
) ReportEventType.CREATE,
else: id_reporte=result.get("id_reporte"), id_usuario=id_usuario, tipo_reporte=tipo_reporte,
# 400 Bad Request: error de validación descripcion=descripcion, ubicacion=ubicacion, lat=lat, lng=lng, estado=estado,
raise HTTPException( fecha_creacion=datetime.utcnow().isoformat()
status_code=status.HTTP_400_BAD_REQUEST, )
detail=message
)
# 202 Accepted: enviado a la cola correctamente
return result return result
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error inesperado en create_report: {e}", exc_info=True) logger.error(f"Error inesperado en create_report: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.get("/{report_id}", response_model=ReportResponse) @router.get("/{report_id}", response_model=ReportResponse)
async def get_report(report_id: str): async def get_report(report_id: str):
@@ -105,19 +102,13 @@ async def get_report(report_id: str):
get_use_case = GetReportById(report_repo) get_use_case = GetReportById(report_repo)
report = get_use_case.execute(report_id) report = get_use_case.execute(report_id)
if not report: if not report:
raise HTTPException( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Reporte con ID {report_id} no encontrado")
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Reporte con ID {report_id} no encontrado"
)
return _report_to_response(report) return _report_to_response(report)
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error al obtener reporte {report_id}: {e}", exc_info=True) logger.error(f"Error al obtener reporte {report_id}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.get("/user/{user_id}") @router.get("/user/{user_id}")
async def get_user_reports(user_id: int): async def get_user_reports(user_id: int):
@@ -128,10 +119,7 @@ async def get_user_reports(user_id: int):
return [_report_to_response(report) for report in reports] return [_report_to_response(report) for report in reports]
except Exception as e: except Exception as e:
logger.error(f"Error al obtener reportes del usuario {user_id}: {e}", exc_info=True) logger.error(f"Error al obtener reportes del usuario {user_id}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.get("/") @router.get("/")
async def list_reports(): async def list_reports():
@@ -142,10 +130,7 @@ async def list_reports():
return [_report_to_response(report) for report in reports] return [_report_to_response(report) for report in reports]
except Exception as e: except Exception as e:
logger.error(f"Error al listar reportes: {e}", exc_info=True) logger.error(f"Error al listar reportes: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.get("/shadowbanned/list") @router.get("/shadowbanned/list")
async def get_shadowbanned_reports(threshold: float = 20): async def get_shadowbanned_reports(threshold: float = 20):
@@ -156,48 +141,26 @@ async def get_shadowbanned_reports(threshold: float = 20):
return [_report_to_response(report) for report in reports] return [_report_to_response(report) for report in reports]
except Exception as e: except Exception as e:
logger.error(f"Error al obtener reportes shadowbaneados: {e}", exc_info=True) logger.error(f"Error al obtener reportes shadowbaneados: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.put("/{report_id}/visibility", status_code=status.HTTP_202_ACCEPTED) @router.put("/{report_id}/visibility", status_code=status.HTTP_202_ACCEPTED)
async def update_report_visibility(report_id: str, visibility_data: ReportUpdateVisibilityRequest): async def update_report_visibility(report_id: str, visibility_data: ReportUpdateVisibilityRequest):
"""Actualiza la visibilidad de un reporte - envía a cola de procesamiento con validaciones previas""" """Actualiza la visibilidad de un reporte - envía a cola de procesamiento con validaciones previas"""
try: try:
update_use_case = UpdateReportVisibility(report_repo, user_repo) update_use_case = UpdateReportVisibility(report_repo, user_repo)
result = update_use_case.execute( result = update_use_case.execute(report_id=report_id, new_visibility=visibility_data.new_visibility, penalize_author=visibility_data.penalize_author)
report_id=report_id,
new_visibility=visibility_data.new_visibility,
penalize_author=visibility_data.penalize_author
)
if result["status"] == "error": if result["status"] == "error":
message = result["message"] message = result["message"]
if "no existe" in message: if "no existe" in message:
# 404 Not Found: reporte no existe raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=message)
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
status_code=status.HTTP_404_NOT_FOUND, _publish_report_event(ReportEventType.UPDATE_VISIBILITY, id_reporte=report_id, visibilidad=visibility_data.new_visibility, penalize_author=visibility_data.penalize_author)
detail=message
)
else:
# 400 Bad Request: error de validación
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=message
)
# 202 Accepted: enviado a la cola correctamente
return result return result
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error al actualizar visibilidad del reporte {report_id}: {e}", exc_info=True) logger.error(f"Error al actualizar visibilidad del reporte {report_id}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.delete("/{report_id}", status_code=status.HTTP_202_ACCEPTED) @router.delete("/{report_id}", status_code=status.HTTP_202_ACCEPTED)
async def delete_report(report_id: str): async def delete_report(report_id: str):
@@ -205,69 +168,38 @@ async def delete_report(report_id: str):
try: try:
delete_use_case = DeleteReport(report_repo) delete_use_case = DeleteReport(report_repo)
result = delete_use_case.execute(report_id) result = delete_use_case.execute(report_id)
if result["status"] == "error": if result["status"] == "error":
message = result["message"] message = result["message"]
if "no existe" in message: if "no existe" in message:
# 404 Not Found: reporte no existe raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=message)
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
status_code=status.HTTP_404_NOT_FOUND, _publish_report_event(ReportEventType.DELETE, id_reporte=report_id)
detail=message
)
else:
# 400 Bad Request: error de validación
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=message
)
# 202 Accepted: enviado a la cola correctamente
return result return result
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error al eliminar reporte {report_id}: {e}", exc_info=True) logger.error(f"Error al eliminar reporte {report_id}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.put("/{report_id}/status", status_code=status.HTTP_200_OK) @router.put("/{report_id}/status", status_code=status.HTTP_200_OK)
async def update_report_status(report_id: str, status_data: ReportUpdateStatusRequest): async def update_report_status(report_id: str, status_data: ReportUpdateStatusRequest):
"""Actualiza el estado de un reporte y envía notificación al usuario""" """Actualiza el estado de un reporte y envía notificación al usuario"""
try: try:
# Obtener el reporte actual para saber el usuario creador
report = report_repo.find_by_id(report_id) report = report_repo.find_by_id(report_id)
if not report: if not report:
raise HTTPException( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Reporte con ID {report_id} no encontrado")
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Reporte con ID {report_id} no encontrado"
)
# Actualizar el estado
update_use_case = UpdateReportStatus(report_repo) update_use_case = UpdateReportStatus(report_repo)
result = update_use_case.execute( result = update_use_case.execute(report_id=report_id, new_estado=status_data.estado)
report_id=report_id,
new_estado=status_data.estado
)
if result["status"] == "error": if result["status"] == "error":
message = result["message"] message = result["message"]
if "no existe" in message: if "no existe" in message:
# 404 Not Found: reporte no existe raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=message)
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
status_code=status.HTTP_404_NOT_FOUND,
detail=message _publish_report_event(ReportEventType.UPDATE_STATUS, id_reporte=report_id, id_usuario=report.id_usuario, estado=status_data.estado)
)
else:
# 400 Bad Request: error de validación
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=message
)
# Enviar notificación al usuario creador del reporte
try: try:
notification_message = NotificationMessage( notification_message = NotificationMessage(
event_type=NotificationEventType.REPORT_STATUS_CHANGE, event_type=NotificationEventType.REPORT_STATUS_CHANGE,
@@ -280,25 +212,16 @@ async def update_report_status(report_id: str, status_data: ReportUpdateStatusRe
estado_reporte=status_data.estado, estado_reporte=status_data.estado,
fecha_creacion=datetime.utcnow().isoformat() fecha_creacion=datetime.utcnow().isoformat()
) )
send_to_queue('notifications_queue', notification_message.to_dict())
# Enviar a la cola de notificaciones send_to_queue('metrics_notifications_queue', notification_message.to_dict())
send_to_queue(
queue_name='notifications_queue',
message=notification_message.to_dict()
)
logger.info(f"Notification sent to user {report.id_usuario} for report {report_id}") logger.info(f"Notification sent to user {report.id_usuario} for report {report_id}")
except Exception as notification_error: except Exception as notification_error:
logger.warning(f"Error sending notification for report {report_id}: {notification_error}") logger.warning(f"Error sending notification for report {report_id}: {notification_error}")
# No fallar la actualización si hay error en notificación
# 200 OK: estado actualizado correctamente
return result return result
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error al actualizar estado del reporte {report_id}: {e}", exc_info=True) logger.error(f"Error al actualizar estado del reporte {report_id}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)

View File

@@ -1,6 +1,6 @@
from fastapi import APIRouter, HTTPException, status, Depends from fastapi import APIRouter, HTTPException, status, Depends
from infrastructure.api.users.schemas import ( from infrastructure.api.users.schemas import (
UserCreateRequest, UserUpdateRequest, UserResponse, UserCreateRequest, UserUpdateRequest, UserResponse,
UserLoginRequest, UserLoginResponse UserLoginRequest, UserLoginResponse
) )
from application.services.user_services import ( from application.services.user_services import (
@@ -8,21 +8,35 @@ from application.services.user_services import (
) )
from infrastructure.adapters.persistence.user_repository_sql import UserRepositorySQL from infrastructure.adapters.persistence.user_repository_sql import UserRepositorySQL
from infrastructure.api.users.auth_service import auth_service from infrastructure.api.users.auth_service import auth_service
from infrastructure.adapters.rabbitmq.sender import send_to_queue
from infrastructure.adapters.rabbitmq.messages import UserMessage, UserEventType
import logging import logging
from datetime import datetime
router = APIRouter() router = APIRouter()
user_repo = UserRepositorySQL() user_repo = UserRepositorySQL()
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def _publish_user_event(event_type: UserEventType, **kwargs):
try:
msg = UserMessage(event_type=event_type, fecha_creacion=datetime.utcnow().isoformat(), **kwargs)
payload = msg.to_dict()
send_to_queue('users_queue', payload)
send_to_queue('metrics_users_queue', payload)
except Exception as e:
logger.warning(f"Error publicando evento de usuario a RabbitMQ: {e}")
@router.post("/login", response_model=UserLoginResponse, status_code=status.HTTP_200_OK) @router.post("/login", response_model=UserLoginResponse, status_code=status.HTTP_200_OK)
async def login_user(credentials: UserLoginRequest): async def login_user(credentials: UserLoginRequest):
""" """
Autentica un usuario y retorna un token JWT Autentica un usuario y retorna un token JWT
**Parámetros:** **Parámetros:**
- email: Email del usuario - email: Email del usuario
- contraseña: Contraseña del usuario - contraseña: Contraseña del usuario
**Retorna:** **Retorna:**
- access_token: Token JWT para usar en requests autenticados - access_token: Token JWT para usar en requests autenticados
- token_type: Tipo de token (bearer) - token_type: Tipo de token (bearer)
@@ -30,55 +44,26 @@ async def login_user(credentials: UserLoginRequest):
- email: Email confirmado - email: Email confirmado
""" """
try: try:
# Obtener usuario por email
get_use_case = GetUserByEmail(user_repo) get_use_case = GetUserByEmail(user_repo)
user = get_use_case.execute(credentials.email) user = get_use_case.execute(credentials.email)
if not user: if not user:
raise HTTPException( raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Email o contraseña incorrectos", headers={"WWW-Authenticate": "Bearer"})
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Email o contraseña incorrectos",
headers={"WWW-Authenticate": "Bearer"}
)
# Verificar contraseña
# Necesitamos obtener el hash de contraseña del modelo
user_model = user_repo.find_by_email_with_password(credentials.email) user_model = user_repo.find_by_email_with_password(credentials.email)
if not user_model or not auth_service.verify_password(credentials.contraseña, user_model.contraseña_hash): if not user_model or not auth_service.verify_password(credentials.contraseña, user_model.contraseña_hash):
raise HTTPException( raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Email o contraseña incorrectos", headers={"WWW-Authenticate": "Bearer"})
status_code=status.HTTP_401_UNAUTHORIZED, access_token = auth_service.create_access_token(user_id=user.user_id, email=user.email)
detail="Email o contraseña incorrectos", return {"access_token": access_token, "token_type": "bearer", "user_id": user.user_id, "email": user.email, "is_admin": user.is_admin}
headers={"WWW-Authenticate": "Bearer"}
)
# Crear token JWT
access_token = auth_service.create_access_token(
user_id=user.user_id,
email=user.email
)
return {
"access_token": access_token,
"token_type": "bearer",
"user_id": user.user_id,
"email": user.email,
"is_admin": user.is_admin
}
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error en login_user: {e}", exc_info=True) logger.error(f"Error en login_user: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.post("/", status_code=status.HTTP_202_ACCEPTED) @router.post("/", status_code=status.HTTP_202_ACCEPTED)
async def create_user(user_data: UserCreateRequest): async def create_user(user_data: UserCreateRequest):
""" """
Crea un nuevo usuario - envía a cola de procesamiento con validaciones previas Crea un nuevo usuario - envía a cola de procesamiento con validaciones previas
**Parámetros:** **Parámetros:**
- nombre: Nombre del usuario (requerido) - nombre: Nombre del usuario (requerido)
- apellido: Apellido del usuario (requerido) - apellido: Apellido del usuario (requerido)
@@ -91,42 +76,22 @@ async def create_user(user_data: UserCreateRequest):
try: try:
create_use_case = CreateUser(user_repo) create_use_case = CreateUser(user_repo)
result = create_use_case.execute( result = create_use_case.execute(
nombre=user_data.nombre, nombre=user_data.nombre, apellido=user_data.apellido, email=user_data.email,
apellido=user_data.apellido, contraseña=user_data.contraseña, fecha_nacimiento=user_data.fecha_nacimiento,
email=user_data.email, url_foto_perfil=user_data.url_foto_perfil, biografia=user_data.biografia
contraseña=user_data.contraseña,
fecha_nacimiento=user_data.fecha_nacimiento,
url_foto_perfil=user_data.url_foto_perfil,
biografia=user_data.biografia
) )
if result["status"] == "error": if result["status"] == "error":
# Detectar tipo de error para código HTTP apropiado
message = result["message"] message = result["message"]
if "ya está registrado" in message: if "ya está registrado" in message:
# 409 Conflict: email duplicado raise HTTPException(status_code=status.HTTP_409_CONFLICT, detail=message)
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
status_code=status.HTTP_409_CONFLICT, _publish_user_event(UserEventType.CREATE, user_id=result.get("user_id"), email=user_data.email, nombre=user_data.nombre, apellido=user_data.apellido)
detail=message
)
else:
# 400 Bad Request: error de validación
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=message
)
# 202 Accepted: enviado a la cola correctamente
return result return result
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error inesperado en create_user: {e}", exc_info=True) logger.error(f"Error inesperado en create_user: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.get("/{user_id}", response_model=UserResponse) @router.get("/{user_id}", response_model=UserResponse)
async def get_user(user_id: int): async def get_user(user_id: int):
@@ -135,19 +100,13 @@ async def get_user(user_id: int):
get_use_case = GetUserById(user_repo) get_use_case = GetUserById(user_repo)
user = get_use_case.execute(user_id) user = get_use_case.execute(user_id)
if not user: if not user:
raise HTTPException( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Usuario con ID {user_id} no encontrado")
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Usuario con ID {user_id} no encontrado"
)
return user return user
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error al obtener usuario {user_id}: {e}", exc_info=True) logger.error(f"Error al obtener usuario {user_id}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.get("/email/{email}", response_model=UserResponse) @router.get("/email/{email}", response_model=UserResponse)
async def get_user_by_email(email: str): async def get_user_by_email(email: str):
@@ -156,19 +115,13 @@ async def get_user_by_email(email: str):
get_use_case = GetUserByEmail(user_repo) get_use_case = GetUserByEmail(user_repo)
user = get_use_case.execute(email) user = get_use_case.execute(email)
if not user: if not user:
raise HTTPException( raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=f"Usuario con email {email} no encontrado")
status_code=status.HTTP_404_NOT_FOUND,
detail=f"Usuario con email {email} no encontrado"
)
return user return user
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error al obtener usuario por email {email}: {e}", exc_info=True) logger.error(f"Error al obtener usuario por email {email}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.get("/") @router.get("/")
async def list_users(): async def list_users():
@@ -178,50 +131,26 @@ async def list_users():
return list_use_case.execute() return list_use_case.execute()
except Exception as e: except Exception as e:
logger.error(f"Error al listar usuarios: {e}", exc_info=True) logger.error(f"Error al listar usuarios: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.put("/{user_id}", status_code=status.HTTP_202_ACCEPTED) @router.put("/{user_id}", status_code=status.HTTP_202_ACCEPTED)
async def update_user(user_id: int, user_data: UserUpdateRequest): async def update_user(user_id: int, user_data: UserUpdateRequest):
"""Actualiza un usuario - envía a cola de procesamiento con validaciones previas""" """Actualiza un usuario - envía a cola de procesamiento con validaciones previas"""
try: try:
update_use_case = UpdateUser(user_repo) update_use_case = UpdateUser(user_repo)
result = update_use_case.execute( result = update_use_case.execute(user_id=user_id, nombre=user_data.nombre, apellido=user_data.apellido, url_foto_perfil=user_data.url_foto_perfil, biografia=user_data.biografia)
user_id=user_id,
nombre=user_data.nombre,
apellido=user_data.apellido,
url_foto_perfil=user_data.url_foto_perfil,
biografia=user_data.biografia
)
if result["status"] == "error": if result["status"] == "error":
message = result["message"] message = result["message"]
if "no existe" in message: if "no existe" in message:
# 404 Not Found: usuario no existe raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=message)
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
status_code=status.HTTP_404_NOT_FOUND, _publish_user_event(UserEventType.UPDATE, user_id=user_id, nombre=user_data.nombre, apellido=user_data.apellido)
detail=message
)
else:
# 400 Bad Request: error de validación
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=message
)
# 202 Accepted: enviado a la cola correctamente
return result return result
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error al actualizar usuario {user_id}: {e}", exc_info=True) logger.error(f"Error al actualizar usuario {user_id}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)
@router.delete("/{user_id}", status_code=status.HTTP_202_ACCEPTED) @router.delete("/{user_id}", status_code=status.HTTP_202_ACCEPTED)
async def delete_user(user_id: int): async def delete_user(user_id: int):
@@ -229,30 +158,15 @@ async def delete_user(user_id: int):
try: try:
delete_use_case = DeleteUser(user_repo) delete_use_case = DeleteUser(user_repo)
result = delete_use_case.execute(user_id) result = delete_use_case.execute(user_id)
if result["status"] == "error": if result["status"] == "error":
message = result["message"] message = result["message"]
if "no existe" in message: if "no existe" in message:
# 404 Not Found: usuario no existe raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=message)
raise HTTPException( raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=message)
status_code=status.HTTP_404_NOT_FOUND, _publish_user_event(UserEventType.DELETE, user_id=user_id)
detail=message
)
else:
# 400 Bad Request: error de validación
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail=message
)
# 202 Accepted: enviado a la cola correctamente
return result return result
except HTTPException: except HTTPException:
raise raise
except Exception as e: except Exception as e:
logger.error(f"Error al eliminar usuario {user_id}: {e}", exc_info=True) logger.error(f"Error al eliminar usuario {user_id}: {e}", exc_info=True)
raise HTTPException( raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="Error interno del servidor")
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Error interno del servidor"
)