Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
"postinstall": "npm run build",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"test": "jest",
"test:watch": "jest --watch",
Expand All @@ -36,15 +37,15 @@
"class-transformer": "^0.5.1",
"class-validator": "^0.14.2",
"dotenv": "^17.2.3",
"firebase-admin": "^12.7.0",
"form-data": "^4.0.5",
"joi": "^18.0.1",
"passport": "^0.7.0",
"passport-jwt": "^4.0.1",
"reflect-metadata": "^0.2.2",
"rxjs": "^7.8.1",
"swagger-ui-express": "^5.0.1",
"transformer": "^1.2.8",
"firebase-admin": "^12.6.0"
"transformer": "^1.2.8"
},
"devDependencies": {
"@eslint/eslintrc": "^3.2.0",
Expand Down
163 changes: 161 additions & 2 deletions src/comunidad/comunidad.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,30 @@ import {
Body,
UseGuards,
Req,
Patch,
Query,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiResponse,
ApiBearerAuth,
ApiParam,
ApiQuery,
} from '@nestjs/swagger';
import type { Request } from 'express';
import { ComunidadService } from './comunidad.service';
import { JwtAuthGuard } from '../auth';
import { CreateGroupDto, SendMessageBodyDto } from './dto';
import { JwtAuthGuard, Roles, RolesGuard } from '../auth';
import { Role } from '../common/dto';
import { CreateGroupDto,
SendMessageBodyDto,
CreateReportDto,
UpdateReportStatusDto,
EstadoReporte,
TipoContenido,
} from './dto';

// Main controller with root prefix for direct routes like /forums, /chats, etc
@ApiTags('Comunidad - Forums & Chats')
Expand Down Expand Up @@ -419,4 +431,151 @@ export class ComunidadController {
async getVotes(@Req() request: Request) {
return this.comunidadService.getVotes(request);
}

// ============ REPORTES - Direct routes ============
@Post('reportes')
@HttpCode(HttpStatus.CREATED)
@ApiOperation({
summary: 'Crear un nuevo reporte de contenido',
description:
'Permite a un usuario reportar contenido inapropiado de threads, respuestas o mensajes de chat.',
})
@ApiResponse({
status: 201,
description: 'El reporte fue enviado correctamente',
})
@ApiResponse({
status: 400,
description: 'Datos inválidos o incompletos',
})
@ApiResponse({
status: 404,
description: 'El contenido reportado no existe',
})
@ApiResponse({
status: 409,
description: 'Ya has reportado este contenido previamente',
})
async createReport(
@Body() createReportDto: CreateReportDto,
@Req() request: Request,
) {
return this.comunidadService.createReport(createReportDto, request);
}

@Get('reportes')
@UseGuards(RolesGuard)
@Roles(Role.ADMIN)
@ApiOperation({
summary: 'Obtener todos los reportes',
description:
'Obtiene la lista de todos los reportes. Solo accesible para administradores.',
})
@ApiQuery({
name: 'estado',
required: false,
enum: EstadoReporte,
description: 'Filtrar reportes por estado',
})
@ApiQuery({
name: 'tipoContenido',
required: false,
enum: TipoContenido,
description: 'Filtrar reportes por tipo de contenido',
})
@ApiResponse({
status: 200,
description: 'Lista de reportes obtenida exitosamente',
})
async getAllReports(
@Query('estado') estado?: EstadoReporte,
@Query('tipoContenido') tipoContenido?: TipoContenido,
@Req() request?: Request,
) {
return this.comunidadService.getAllReports(estado, tipoContenido, request);
}

@Get('reportes/estadisticas')
@UseGuards(RolesGuard)
@Roles(Role.ADMIN)
@ApiOperation({
summary: 'Obtener estadísticas de reportes',
description: 'Obtiene estadísticas generales sobre los reportes del sistema.',
})
@ApiResponse({
status: 200,
description: 'Estadísticas obtenidas exitosamente',
})
async getReportStatistics(@Req() request: Request) {
return this.comunidadService.getReportStatistics(request);
}

@Get('reportes/mis-reportes')
@ApiOperation({
summary: 'Obtener reportes del usuario autenticado',
description: 'Obtiene todos los reportes realizados por el usuario actual.',
})
@ApiResponse({
status: 200,
description: 'Lista de reportes del usuario obtenida exitosamente',
})
async getMyReports(@Req() request: Request) {
return this.comunidadService.getMyReports(request);
}

@Get('reportes/:id')
@UseGuards(RolesGuard)
@Roles(Role.ADMIN)
@ApiOperation({
summary: 'Obtener un reporte específico por ID',
description:
'Obtiene los detalles completos de un reporte incluyendo el historial de logs.',
})
@ApiParam({
name: 'id',
description: 'ID del reporte',
})
@ApiResponse({
status: 200,
description: 'Reporte obtenido exitosamente',
})
@ApiResponse({
status: 404,
description: 'Reporte no encontrado',
})
async getReportById(@Param('id') id: string, @Req() request: Request) {
return this.comunidadService.getReportById(id, request);
}

@Patch('reportes/:id/estado')
@UseGuards(RolesGuard)
@Roles(Role.ADMIN)
@ApiOperation({
summary: 'Actualizar el estado de un reporte',
description:
'Permite a administradores cambiar el estado de un reporte y añadir notas de moderación.',
})
@ApiParam({
name: 'id',
description: 'ID del reporte',
})
@ApiResponse({
status: 200,
description: 'Estado del reporte actualizado correctamente',
})
@ApiResponse({
status: 404,
description: 'Reporte no encontrado',
})
async updateReportStatus(
@Param('id') id: string,
@Body() updateStatusDto: UpdateReportStatusDto,
@Req() request: Request,
) {
return this.comunidadService.updateReportStatus(
id,
updateStatusDto,
request,
);
}
}
124 changes: 124 additions & 0 deletions src/comunidad/comunidad.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -529,4 +529,128 @@ export class ComunidadService {
throw error;
}
}

//Reportes//
/**
* Proxy para crear un nuevo reporte de contenido
*/
async createReport(createReportDto: any, request: Request) {
const config = JwtForwardingHelper.getAxiosConfig(request);
const url = `${this.comunidadServiceUrl}/reportes`;

try {
this.logger.log(`Forwarding POST request to: ${url}`);
const response = await firstValueFrom(
this.httpService.post(url, createReportDto, config),
);
return response.data;
} catch (error) {
this.logger.error(`Error forwarding create report request`, error);
throw error;
}
}

/**
* Proxy para obtener todos los reportes (solo admins)
*/
async getAllReports(
estado?: string,
tipoContenido?: string,
request?: Request,
) {
const config = request ? JwtForwardingHelper.getAxiosConfig(request) : {};
let url = `${this.comunidadServiceUrl}/reportes`;

// Agregar query params si existen
const params = new URLSearchParams();
if (estado) params.append('estado', estado);
if (tipoContenido) params.append('tipoContenido', tipoContenido);

if (params.toString()) {
url += `?${params.toString()}`;
}

try {
this.logger.log(`Forwarding GET request to: ${url}`);
const response = await firstValueFrom(this.httpService.get(url, config));
return response.data;
} catch (error) {
this.logger.error(`Error forwarding get all reports request`, error);
throw error;
}
}

/**
* Proxy para obtener estadísticas de reportes (solo admins)
*/
async getReportStatistics(request: Request) {
const config = JwtForwardingHelper.getAxiosConfig(request);
const url = `${this.comunidadServiceUrl}/reportes/estadisticas`;

try {
this.logger.log(`Forwarding GET request to: ${url}`);
const response = await firstValueFrom(this.httpService.get(url, config));
return response.data;
} catch (error) {
this.logger.error(`Error forwarding get statistics request`, error);
throw error;
}
}

/**
* Proxy para obtener los reportes del usuario autenticado
*/
async getMyReports(request: Request) {
const config = JwtForwardingHelper.getAxiosConfig(request);
const url = `${this.comunidadServiceUrl}/reportes/mis-reportes`;

try {
this.logger.log(`Forwarding GET request to: ${url}`);
const response = await firstValueFrom(this.httpService.get(url, config));
return response.data;
} catch (error) {
this.logger.error(`Error forwarding get my reports request`, error);
throw error;
}
}

/**
* Proxy para obtener un reporte por ID (solo admins)
*/
async getReportById(reporteId: string, request: Request) {
const config = JwtForwardingHelper.getAxiosConfig(request);
const url = `${this.comunidadServiceUrl}/reportes/${reporteId}`;

try {
this.logger.log(`Forwarding GET request to: ${url}`);
const response = await firstValueFrom(this.httpService.get(url, config));
return response.data;
} catch (error) {
this.logger.error(`Error forwarding get report by id request`, error);
throw error;
}
}

/**
* Proxy para actualizar el estado de un reporte (solo admins)
*/
async updateReportStatus(
reporteId: string,
updateStatusDto: any,
request: Request,
) {
const config = JwtForwardingHelper.getAxiosConfig(request);
const url = `${this.comunidadServiceUrl}/reportes/${reporteId}/estado`;

try {
this.logger.log(`Forwarding PATCH request to: ${url}`);
const response = await firstValueFrom(
this.httpService.patch(url, updateStatusDto, config),
);
return response.data;
} catch (error) {
this.logger.error(`Error forwarding update report status request`, error);
throw error;
}
}
}
Loading