diff --git a/src/cuestionario/cuestionario.controller.ts b/src/cuestionario/cuestionario.controller.ts index 5f472a1..437705f 100644 --- a/src/cuestionario/cuestionario.controller.ts +++ b/src/cuestionario/cuestionario.controller.ts @@ -12,6 +12,8 @@ import { UnprocessableEntityException, UploadedFiles, BadRequestException, + HttpStatus, + HttpException, } from '@nestjs/common'; import { CuestionarioService } from './cuestionario.service'; import { CreateCuestionarioDto } from './dto/create-cuestionario.dto'; @@ -21,9 +23,9 @@ 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'; +import * as fs from 'fs'; +import { NotFoundError } from 'rxjs'; +import { error } from 'console'; @Controller('cuestionario') @CuestionarioApiDocumentation.ApiController @@ -70,20 +72,56 @@ export class CuestionarioController { })); } - @Post('carga-masiva') - @UseInterceptors(FileInterceptor('file')) - async cargaMasiva(@UploadedFile() file: Express.Multer.File) { - if (!file) { - throw new BadRequestException('No se ha subido ningún archivo'); - } +@Post('carga-masiva') +@UseInterceptors( + FileInterceptor('file', { + storage: diskStorage({ + destination: (req, file, cb) => { + const uploadPath = './uploads/excel'; + // Crear la carpeta si no existe + fs.mkdirSync(uploadPath, { recursive: true }); + cb(null, uploadPath); + }, + filename: (req, file, cb) => { + // Guardar el archivo con timestamp + nombre original + const uniqueSuffix = Date.now() + '-' + Math.round(Math.random() * 1e9); + const fileExt = extname(file.originalname); // obtener extensión + cb(null, `${uniqueSuffix}${fileExt}`); + }, + }), + fileFilter: (req, file, cb) => { + // Solo permitir archivos .xlsx + if (file.mimetype === 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') { + cb(null, true); + } else { + cb(new BadRequestException('Solo se permiten archivos .xlsx'), false); + } + }, + }), +) +async cargaMasiva(@UploadedFile() file: Express.Multer.File) { + if (!file) { + throw new BadRequestException('No se ha subido ningún archivo'); + } - const cuestionarios = await this.cargaMasiva(file); + console.log('Archivo guardado en:', file.path); - return { - message: 'Carga masiva exitosa', - total: cuestionarios.length, - data: cuestionarios, - }; + // Leer el archivo con ExcelJS + + + // Aquí tu lógica para procesar las hojas del Excel + const cuestionarios = await this.cuestionarioService.cargarEventosDesdeExcel(file.path); // Ejemplo de arreglo de datos procesados + // ... + + if (!cuestionarios.data) { + throw error; + } + + return { + message: cuestionarios.message, + total: cuestionarios.data.length, + data: cuestionarios, + }; } diff --git a/src/cuestionario/cuestionario.service.ts b/src/cuestionario/cuestionario.service.ts index 7183ea2..bff0b4a 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: any[] = []; 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) { diff --git a/src/cuestionario_respondido/cuestionario_respondido.service.ts b/src/cuestionario_respondido/cuestionario_respondido.service.ts index 0de8073..47c120a 100644 --- a/src/cuestionario_respondido/cuestionario_respondido.service.ts +++ b/src/cuestionario_respondido/cuestionario_respondido.service.ts @@ -112,11 +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 @@ -337,11 +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/participante_evento/participante_evento.service.ts b/src/participante_evento/participante_evento.service.ts index 3625e16..f93433d 100644 --- a/src/participante_evento/participante_evento.service.ts +++ b/src/participante_evento/participante_evento.service.ts @@ -191,11 +191,13 @@ export class ParticipanteEventoService { const tokenData = await this.qrTokenService.validateQrToken(qrToken); if (!tokenData) { - throw new HttpException( - 'Token QR inválido o expirado', - HttpStatus.BAD_REQUEST, - ); + return{ + success: false, + message: + 'El token ya caduco', + data: {} } + } const { id_participante, id_evento, id_cuestionario } = tokenData; diff --git a/src/qr/qr-token.service.ts b/src/qr/qr-token.service.ts index 435cc6b..1d7a07c 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, ) {} /** @@ -24,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, @@ -42,17 +46,10 @@ 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 }, - select: ['fecha_fin'], - }); - - if (!fecha_fin) { - 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_fin.getTime() - Date.now()) / 1000); return this.jwtService.sign(payload, { secret, @@ -65,7 +62,6 @@ export class QrTokenService { * @param token Token JWT a validar * @returns Datos decodificados del token o null si es inválido */ - async validateQrToken(token: string): Promise<{ id_participante : number; @@ -80,9 +76,28 @@ 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 fechaFinEvento = new Date(fecha_fin2.fecha_fin); // UTC + const now = new Date(); + console.log(fechaFinEvento.getTime()) + console.log(now.getTime) + if (fechaFinEvento.getTime() < now.getTime()) { + throw new Error('El evento ha finalizado'); + } + // Verificar que sea un token de tipo QR de asistencia if (decoded.type !== 'qr_asistencia') { return null; 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 { 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],