diff --git a/.gitignore b/.gitignore index 792c018..f746f77 100644 --- a/.gitignore +++ b/.gitignore @@ -67,3 +67,4 @@ dmypy.json node_modules/ *.pem credentials.json +migrations/ diff --git a/src/domain/users.py b/src/domain/users.py index fee67f0..b5fbc4c 100644 --- a/src/domain/users.py +++ b/src/domain/users.py @@ -16,3 +16,4 @@ class User: numero_reportes: int = 0 url_foto_perfil: Optional[str] = None biografia: Optional[str] = None + is_admin: bool = False # Indica si el usuario tiene permisos de administrador diff --git a/src/infrastructure/adapters/persistence/models.py b/src/infrastructure/adapters/persistence/models.py index a5d3d91..63d401f 100644 --- a/src/infrastructure/adapters/persistence/models.py +++ b/src/infrastructure/adapters/persistence/models.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String, Float, DateTime +from sqlalchemy import Column, Integer, String, Float, DateTime, Boolean from infrastructure.adapters.persistence.db import Base from datetime import datetime @@ -17,3 +17,4 @@ class UserModel(Base): numero_reportes = Column(Integer, default=0, nullable=False) url_foto_perfil = Column(String(500), nullable=True) biografia = Column(String(1000), nullable=True) + is_admin = Column(Boolean, default=False, nullable=False, index=True) # Permisos de administrador diff --git a/src/infrastructure/adapters/persistence/user_repository_sql.py b/src/infrastructure/adapters/persistence/user_repository_sql.py index c4630e4..207d316 100644 --- a/src/infrastructure/adapters/persistence/user_repository_sql.py +++ b/src/infrastructure/adapters/persistence/user_repository_sql.py @@ -26,7 +26,8 @@ class UserRepositorySQL(UserRepository): calificacion=user.calificacion, numero_reportes=user.numero_reportes, url_foto_perfil=user.url_foto_perfil, - biografia=user.biografia + biografia=user.biografia, + is_admin=user.is_admin ) self.db.add(db_user) self.db.commit() @@ -160,5 +161,6 @@ class UserRepositorySQL(UserRepository): calificacion=db_user.calificacion, numero_reportes=db_user.numero_reportes, url_foto_perfil=db_user.url_foto_perfil, - biografia=db_user.biografia + biografia=db_user.biografia, + is_admin=db_user.is_admin ) diff --git a/src/infrastructure/api/moderations/moderations.py b/src/infrastructure/api/moderations/moderations.py index cc56d53..53ee083 100644 --- a/src/infrastructure/api/moderations/moderations.py +++ b/src/infrastructure/api/moderations/moderations.py @@ -1,5 +1,5 @@ """Endpoints de moderación para gestión de reportes, cuentas y usuarios""" -from fastapi import APIRouter, HTTPException +from fastapi import APIRouter, HTTPException, Depends from fastapi.responses import JSONResponse from infrastructure.api.moderations.schemas import ( DeleteReportRequest, @@ -9,6 +9,7 @@ from infrastructure.api.moderations.schemas import ( ReviewContentRequest, ModerationActionResponse ) +from infrastructure.api.auth import get_current_admin_user from application.services.moderation_services import ( DeleteReportUseCase, CloseAccountUseCase, @@ -19,6 +20,7 @@ from application.services.moderation_services import ( from infrastructure.adapters.persistence.mongodb import mongodb from infrastructure.adapters.persistence.db import get_db from infrastructure.adapters.moderation_repository_mongo import ModerationRepositoryMongo +from domain.users import User from sqlalchemy.orm import Session import logging @@ -32,11 +34,13 @@ moderation_repo = ModerationRepositoryMongo() @router.post("/reports/delete", response_model=ModerationActionResponse) -async def delete_report(request: DeleteReportRequest): +async def delete_report( + request: DeleteReportRequest, + current_admin: User = Depends(get_current_admin_user) +): """ - Eliminar un reporte como moderador + Eliminar un reporte como moderador (requiere permisos de admin) - - **moderator_id**: ID del moderador que ejecuta la acción - **report_id**: ID del reporte a eliminar - **reason**: Razón de la eliminación (mínimo 5 caracteres) - **description**: Descripción adicional (opcional) @@ -44,7 +48,7 @@ async def delete_report(request: DeleteReportRequest): try: use_case = DeleteReportUseCase(moderation_repo) result = use_case.execute( - moderator_id=request.moderator_id, + moderator_id=current_admin.user_id, report_id=request.report_id, reason=request.reason, description=request.description @@ -61,11 +65,13 @@ async def delete_report(request: DeleteReportRequest): @router.post("/accounts/close", response_model=ModerationActionResponse) -async def close_account(request: CloseAccountRequest): +async def close_account( + request: CloseAccountRequest, + current_admin: User = Depends(get_current_admin_user) +): """ - Cerrar una cuenta de usuario + Cerrar una cuenta de usuario (requiere permisos de admin) - - **moderator_id**: ID del moderador - **user_id**: ID del usuario cuya cuenta cerrar - **reason**: Razón del cierre (mínimo 5 caracteres) - **description**: Descripción adicional (opcional) @@ -74,7 +80,7 @@ async def close_account(request: CloseAccountRequest): try: use_case = CloseAccountUseCase(moderation_repo) result = use_case.execute( - moderator_id=request.moderator_id, + moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, description=request.description, @@ -92,11 +98,13 @@ async def close_account(request: CloseAccountRequest): @router.post("/users/ban", response_model=ModerationActionResponse) -async def ban_user(request: BanUserRequest): +async def ban_user( + request: BanUserRequest, + current_admin: User = Depends(get_current_admin_user) +): """ - Banear a un usuario + Banear a un usuario (requiere permisos de admin) - - **moderator_id**: ID del moderador - **user_id**: ID del usuario a banear - **reason**: Razón del ban (mínimo 5 caracteres) - **duration_days**: Duración del ban en días (None para permanente) @@ -105,7 +113,7 @@ async def ban_user(request: BanUserRequest): try: use_case = BanUserUseCase(moderation_repo) result = use_case.execute( - moderator_id=request.moderator_id, + moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, duration_days=request.duration_days, @@ -123,11 +131,13 @@ async def ban_user(request: BanUserRequest): @router.post("/users/warn", response_model=ModerationActionResponse) -async def warn_user(request: WarnUserRequest): +async def warn_user( + request: WarnUserRequest, + current_admin: User = Depends(get_current_admin_user) +): """ - Advertir a un usuario + Advertir a un usuario (requiere permisos de admin) - - **moderator_id**: ID del moderador - **user_id**: ID del usuario a advertir - **reason**: Razón de la advertencia (mínimo 5 caracteres) - **description**: Descripción adicional (opcional) @@ -135,7 +145,7 @@ async def warn_user(request: WarnUserRequest): try: use_case = WarnUserUseCase(moderation_repo) result = use_case.execute( - moderator_id=request.moderator_id, + moderator_id=current_admin.user_id, user_id=request.user_id, reason=request.reason, description=request.description @@ -152,11 +162,13 @@ async def warn_user(request: WarnUserRequest): @router.post("/content/review", response_model=ModerationActionResponse) -async def review_content(request: ReviewContentRequest): +async def review_content( + request: ReviewContentRequest, + current_admin: User = Depends(get_current_admin_user) +): """ - Revisar y actuar sobre contenido reportado + Revisar y actuar sobre contenido reportado (requiere permisos de admin) - - **moderator_id**: ID del moderador - **report_id**: ID del reporte a revisar - **action**: Acción a tomar (approve, reject, needs_more_info) - **reason**: Razón de la decisión (opcional) @@ -165,7 +177,7 @@ async def review_content(request: ReviewContentRequest): try: use_case = ReviewContentUseCase(moderation_repo) result = use_case.execute( - moderator_id=request.moderator_id, + moderator_id=current_admin.user_id, report_id=request.report_id, action=request.action, reason=request.reason, diff --git a/src/infrastructure/api/users/schemas.py b/src/infrastructure/api/users/schemas.py index d309412..53db010 100644 --- a/src/infrastructure/api/users/schemas.py +++ b/src/infrastructure/api/users/schemas.py @@ -46,6 +46,7 @@ class UserResponse(BaseModel): numero_reportes: int url_foto_perfil: Optional[str] biografia: Optional[str] + is_admin: bool class Config: from_attributes = True