From 1e09c147671dbd13d0de34d77025a2aeb8a2b67f Mon Sep 17 00:00:00 2001 From: evenegas Date: Wed, 1 Oct 2025 11:24:12 -0600 Subject: [PATCH 1/6] qr --- src/cuestionario/cuestionario.controller.ts | 6 ++---- src/qr/qr-token.service.ts | 12 +++++++++--- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/cuestionario/cuestionario.controller.ts b/src/cuestionario/cuestionario.controller.ts index ac2d9f0..e8c3eb5 100644 --- a/src/cuestionario/cuestionario.controller.ts +++ b/src/cuestionario/cuestionario.controller.ts @@ -21,9 +21,7 @@ import { diskStorage } from 'multer'; import { extname } from 'path'; import { CuestionarioApiDocumentation } from './docs/cuestionario.documentation'; import { CreateEventoWithCuestionarioDto } from './dto/create-evento-cuestionario.dto'; -import * as ExcelJS from 'exceljs'; -import { TipoPreguntaEnum } from 'src/tipo_pregunta/entities/tipo_pregunta.entity'; -import { TiposValidacion } from 'src/pregunta/entities/pregunta.entity'; + @Controller('cuestionario') @CuestionarioApiDocumentation.ApiController @@ -70,7 +68,7 @@ export class CuestionarioController { })); } - @Post('carga-masiva') +@Post('carga-masiva') @UseInterceptors(FileInterceptor('file')) async cargaMasiva(@UploadedFile() file: Express.Multer.File) { if (!file) { diff --git a/src/qr/qr-token.service.ts b/src/qr/qr-token.service.ts index 189b182..6c8c1f6 100644 --- a/src/qr/qr-token.service.ts +++ b/src/qr/qr-token.service.ts @@ -5,6 +5,7 @@ import { InjectRepository } from '@nestjs/typeorm'; import { Repository } from 'typeorm'; import { Asistencia } from 'src/asistencia/entities/asistencia.entity'; import { Cuestionario } from 'src/cuestionario/entities/cuestionario.entity'; +import { Evento } from 'src/evento/entities/evento.entity'; @Injectable() export class QrTokenService { @@ -15,6 +16,8 @@ export class QrTokenService { private readonly asistenciaRepository: Repository, @InjectRepository(Cuestionario) private readonly cuestionarioRepository: Repository, + @InjectRepository(Evento) + private readonly eventoREpo: Repository, ) {} /** @@ -44,14 +47,17 @@ export class QrTokenService { const fecha_fin = await this.cuestionarioRepository.findOne({ where: { id_cuestionario }, - select: ['fecha_fin'], + }); + const fecha_fin2 = await this.eventoREpo.findOne({ + where: { id_evento }, + }); - if (!fecha_fin) { + if (!fecha_fin2) { throw new UnauthorizedException('Cuestionario no encontrado'); } // Convert fecha_fin to seconds from now for expiresIn - const expiresInSeconds = Math.floor((fecha_fin?.fecha_fin.getTime() - Date.now()) / 1000); + const expiresInSeconds = Math.floor((fecha_fin2?.fecha_fin.getTime() - Date.now()) / 1000); return this.jwtService.sign(payload, { secret, From 8690c8242999db3049e5ae11b290128e773f3c1d Mon Sep 17 00:00:00 2001 From: evenegas Date: Wed, 1 Oct 2025 11:33:17 -0600 Subject: [PATCH 2/6] qr --- src/qr/qr.module.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/qr/qr.module.ts b/src/qr/qr.module.ts index 9960373..658d954 100644 --- a/src/qr/qr.module.ts +++ b/src/qr/qr.module.ts @@ -8,9 +8,10 @@ import { AuthModule } from '../auth/auth.module'; import { ConfigModule } from '@nestjs/config'; import { Asistencia } from 'src/asistencia/entities/asistencia.entity'; import { Cuestionario } from 'src/cuestionario/entities/cuestionario.entity'; +import { Evento } from 'src/evento/entities/evento.entity'; @Module({ - imports: [TypeOrmModule.forFeature([Qr, Asistencia, Cuestionario]), AuthModule, ConfigModule], + imports: [TypeOrmModule.forFeature([Qr, Asistencia, Cuestionario, Evento]), AuthModule, ConfigModule], controllers: [QrController], providers: [QrService, QrTokenService], exports: [QrTokenService], From f346da673b469c09486d199d680caf7988b32aa1 Mon Sep 17 00:00:00 2001 From: evenegas Date: Wed, 1 Oct 2025 12:00:14 -0600 Subject: [PATCH 3/6] qr --- .../cuestionario_respondido.service.ts | 2 -- src/qr/qr-token.service.ts | 18 ++++++++++++++---- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/cuestionario_respondido/cuestionario_respondido.service.ts b/src/cuestionario_respondido/cuestionario_respondido.service.ts index 0de8073..0f8d78a 100644 --- a/src/cuestionario_respondido/cuestionario_respondido.service.ts +++ b/src/cuestionario_respondido/cuestionario_respondido.service.ts @@ -116,7 +116,6 @@ export class CuestionarioRespondidoService { participante.id_participante, cuestionario.evento.id_evento, cuestionario.id_cuestionario, - ); // Generar código QR con el token @@ -341,7 +340,6 @@ export class CuestionarioRespondidoService { participante.id_participante, cuestionario.evento.id_evento, cuestionario.id_cuestionario, - ); qrBuffer = await QRCode.toBuffer(qrToken); diff --git a/src/qr/qr-token.service.ts b/src/qr/qr-token.service.ts index 6c8c1f6..5e8b934 100644 --- a/src/qr/qr-token.service.ts +++ b/src/qr/qr-token.service.ts @@ -45,9 +45,7 @@ export class QrTokenService { this.configService.get('QR_JWT_SECRET') || this.configService.get('JWT_SECRET', 'tu_clave_secreta'); - const fecha_fin = await this.cuestionarioRepository.findOne({ - where: { id_cuestionario }, - }); + const fecha_fin2 = await this.eventoREpo.findOne({ where: { id_evento }, @@ -84,9 +82,21 @@ export class QrTokenService { this.configService.get('JWT_SECRET', 'tu_clave_secreta'); console.log(secret) - + const decoded = this.jwtService.verify(token, { secret }); + const fecha_fin2 = await this.eventoREpo.findOne({ + where: { id_evento:decoded.id_evento }, + + }); + if (!fecha_fin2) { + throw new UnauthorizedException('Evento no encontrado'); + } + const now = new Date(); + if (fecha_fin2.fecha_fin < now) { + throw new Error('El evento ha finalizado'); + } + // Verificar que sea un token de tipo QR de asistencia if (decoded.type !== 'qr_asistencia') { return null; From 0745ddc8b4030eac256e99021fa100ea4f51882f Mon Sep 17 00:00:00 2001 From: evenegas Date: Wed, 1 Oct 2025 12:11:44 -0600 Subject: [PATCH 4/6] qr --- .../cuestionario_respondido.service.ts | 6 ++++-- src/qr/qr-token.service.ts | 16 ++++++---------- src/qr/qr.controller.ts | 5 ++++- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/cuestionario_respondido/cuestionario_respondido.service.ts b/src/cuestionario_respondido/cuestionario_respondido.service.ts index 0f8d78a..47c120a 100644 --- a/src/cuestionario_respondido/cuestionario_respondido.service.ts +++ b/src/cuestionario_respondido/cuestionario_respondido.service.ts @@ -112,10 +112,11 @@ export class CuestionarioRespondidoService { if (cuestionario && cuestionario.evento) { try { // Generar token para el QR - qrToken = await this.qrTokenService.generateQrToken( + qrToken = this.qrTokenService.generateQrToken( participante.id_participante, cuestionario.evento.id_evento, cuestionario.id_cuestionario, + cuestionario.fecha_fin ); // Generar código QR con el token @@ -336,10 +337,11 @@ export class CuestionarioRespondidoService { } // Generar token para el QR - qrToken = await this.qrTokenService.generateQrToken( + qrToken = this.qrTokenService.generateQrToken( participante.id_participante, cuestionario.evento.id_evento, cuestionario.id_cuestionario, + cuestionario.fecha_fin ); qrBuffer = await QRCode.toBuffer(qrToken); diff --git a/src/qr/qr-token.service.ts b/src/qr/qr-token.service.ts index 5e8b934..ae13a9d 100644 --- a/src/qr/qr-token.service.ts +++ b/src/qr/qr-token.service.ts @@ -27,11 +27,12 @@ export class QrTokenService { * @param id_cuestionario ID del cuestionario * @returns Token JWT codificado */ - async generateQrToken( + generateQrToken( id_participante: number, id_evento: number, id_cuestionario: number, - ): Promise { + fecha_fin: Date, + ): string { const payload = { id_participante, id_evento, @@ -46,16 +47,11 @@ export class QrTokenService { this.configService.get('JWT_SECRET', 'tu_clave_secreta'); - const fecha_fin2 = await this.eventoREpo.findOne({ - where: { id_evento }, + - }); - - if (!fecha_fin2) { - throw new UnauthorizedException('Cuestionario no encontrado'); - } + // Convert fecha_fin to seconds from now for expiresIn - const expiresInSeconds = Math.floor((fecha_fin2?.fecha_fin.getTime() - Date.now()) / 1000); + const expiresInSeconds = Math.floor((fecha_fin.getTime() - Date.now()) / 1000); return this.jwtService.sign(payload, { secret, diff --git a/src/qr/qr.controller.ts b/src/qr/qr.controller.ts index b435d5f..f64f9c8 100644 --- a/src/qr/qr.controller.ts +++ b/src/qr/qr.controller.ts @@ -69,20 +69,23 @@ export class QrController { }; } - @Get('generate-token/:idParticipante/:idEvento/:idCuestionario') + @Get('generate-token/:idParticipante/:idEvento/:idCuestionario/:fechaFin') @ApiOperation({ summary: 'Genera un token JWT para QR de asistencia' }) @ApiParam({ name: 'idParticipante', description: 'ID del participante' }) @ApiParam({ name: 'idEvento', description: 'ID del evento' }) @ApiParam({ name: 'idCuestionario', description: 'ID del cuestionario' }) + @ApiParam({ name: 'fechaFin', description: 'Fecha de fin de validez del token (ISO 8601)' }) async generateQrToken( @Param('idParticipante', ParseIntPipe) idParticipante: number, @Param('idEvento', ParseIntPipe) idEvento: number, @Param('idCuestionario', ParseIntPipe) idCuestionario: number, + @Param('fechaFin') fechaFin: Date, ) { const token = await this.qrTokenService.generateQrToken( idParticipante, idEvento, idCuestionario, + fechaFin ); return { From 7b8c19fbd135ffa97f0c5958222d7ecf1766d487 Mon Sep 17 00:00:00 2001 From: evenegas Date: Wed, 1 Oct 2025 12:23:14 -0600 Subject: [PATCH 5/6] qr --- .../cuestionario_respondido.service.ts | 2 -- src/qr/qr-token.service.ts | 5 ++--- src/qr/qr.controller.ts | 8 +++----- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/cuestionario_respondido/cuestionario_respondido.service.ts b/src/cuestionario_respondido/cuestionario_respondido.service.ts index 47c120a..9f3f426 100644 --- a/src/cuestionario_respondido/cuestionario_respondido.service.ts +++ b/src/cuestionario_respondido/cuestionario_respondido.service.ts @@ -116,7 +116,6 @@ export class CuestionarioRespondidoService { participante.id_participante, cuestionario.evento.id_evento, cuestionario.id_cuestionario, - cuestionario.fecha_fin ); // Generar código QR con el token @@ -341,7 +340,6 @@ export class CuestionarioRespondidoService { participante.id_participante, cuestionario.evento.id_evento, cuestionario.id_cuestionario, - cuestionario.fecha_fin ); qrBuffer = await QRCode.toBuffer(qrToken); diff --git a/src/qr/qr-token.service.ts b/src/qr/qr-token.service.ts index ae13a9d..529e835 100644 --- a/src/qr/qr-token.service.ts +++ b/src/qr/qr-token.service.ts @@ -31,7 +31,6 @@ export class QrTokenService { id_participante: number, id_evento: number, id_cuestionario: number, - fecha_fin: Date, ): string { const payload = { id_participante, @@ -51,11 +50,11 @@ export class QrTokenService { // Convert fecha_fin to seconds from now for expiresIn - const expiresInSeconds = Math.floor((fecha_fin.getTime() - Date.now()) / 1000); + return this.jwtService.sign(payload, { secret, - expiresIn: expiresInSeconds > 0 ? expiresInSeconds : 0, // El QR puede ser válido por 30 días + expiresIn: "30d", // El QR puede ser válido por 30 días }); } diff --git a/src/qr/qr.controller.ts b/src/qr/qr.controller.ts index f64f9c8..a926727 100644 --- a/src/qr/qr.controller.ts +++ b/src/qr/qr.controller.ts @@ -69,23 +69,21 @@ export class QrController { }; } - @Get('generate-token/:idParticipante/:idEvento/:idCuestionario/:fechaFin') + @Get('generate-token/:idParticipante/:idEvento/:idCuestionario') @ApiOperation({ summary: 'Genera un token JWT para QR de asistencia' }) @ApiParam({ name: 'idParticipante', description: 'ID del participante' }) @ApiParam({ name: 'idEvento', description: 'ID del evento' }) @ApiParam({ name: 'idCuestionario', description: 'ID del cuestionario' }) - @ApiParam({ name: 'fechaFin', description: 'Fecha de fin de validez del token (ISO 8601)' }) + async generateQrToken( @Param('idParticipante', ParseIntPipe) idParticipante: number, @Param('idEvento', ParseIntPipe) idEvento: number, @Param('idCuestionario', ParseIntPipe) idCuestionario: number, - @Param('fechaFin') fechaFin: Date, ) { - const token = await this.qrTokenService.generateQrToken( + const token = this.qrTokenService.generateQrToken( idParticipante, idEvento, idCuestionario, - fechaFin ); return { From be72ee5fa5f0d2b7105c24028a469a2223ccba76 Mon Sep 17 00:00:00 2001 From: evenegas Date: Wed, 1 Oct 2025 12:43:37 -0600 Subject: [PATCH 6/6] qr --- src/cuestionario/cuestionario.controller.ts | 6 +++--- src/cuestionario/cuestionario.service.ts | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/cuestionario/cuestionario.controller.ts b/src/cuestionario/cuestionario.controller.ts index e8c3eb5..64f43c6 100644 --- a/src/cuestionario/cuestionario.controller.ts +++ b/src/cuestionario/cuestionario.controller.ts @@ -75,12 +75,12 @@ async cargaMasiva(@UploadedFile() file: Express.Multer.File) { throw new BadRequestException('No se ha subido ningún archivo'); } - const cuestionarios = await this.cargaMasiva(file); + const cuestionarios = await this.cuestionarioService.cargarEventosDesdeExcel(file.path); return { message: 'Carga masiva exitosa', - total: cuestionarios.length, - data: cuestionarios, + total: cuestionarios.data.length, + data: cuestionarios.data, }; } diff --git a/src/cuestionario/cuestionario.service.ts b/src/cuestionario/cuestionario.service.ts index fe01f30..2c71201 100644 --- a/src/cuestionario/cuestionario.service.ts +++ b/src/cuestionario/cuestionario.service.ts @@ -96,6 +96,7 @@ export class CuestionarioService { 2: 'registro-comunidad-trabajadores', 3: 'registro-general', }; + let data const workbook = new ExcelJS.Workbook(); await workbook.xlsx.readFile(filePath); @@ -193,11 +194,11 @@ export class CuestionarioService { }; // Llamada a tu servicio para guardar en DB - await this.createCuestionarioEvento(createDto); + data.push(await this.createCuestionarioEvento(createDto)); } } - return { message: 'Carga masiva completada' }; + return { message: 'Carga masiva completada', data }; } async create(createCuestionarioDto: CreateCuestionarioDto) {