Modificaciones en la validacion del qr
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
+28
-13
@@ -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<Asistencia>,
|
||||
@InjectRepository(Cuestionario)
|
||||
private readonly cuestionarioRepository: Repository<Cuestionario>,
|
||||
@InjectRepository(Evento)
|
||||
private readonly eventoREpo: Repository<Evento>,
|
||||
) {}
|
||||
|
||||
/**
|
||||
@@ -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<string> {
|
||||
fecha_fin: Date,
|
||||
): string {
|
||||
const payload = {
|
||||
id_participante,
|
||||
id_evento,
|
||||
@@ -42,17 +46,10 @@ export class QrTokenService {
|
||||
this.configService.get<string>('QR_JWT_SECRET') ||
|
||||
this.configService.get<string>('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<string>('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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+2
-1
@@ -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],
|
||||
|
||||
Reference in New Issue
Block a user