240 lines
8.1 KiB
TypeScript
240 lines
8.1 KiB
TypeScript
import {
|
|
HttpException,
|
|
HttpStatus,
|
|
Inject,
|
|
Injectable,
|
|
Res,
|
|
UploadedFile,
|
|
} from '@nestjs/common';
|
|
import { ConfigService } from '@nestjs/config';
|
|
import { InjectRepository } from '@nestjs/typeorm';
|
|
import * as nodemailer from 'nodemailer';
|
|
import { EventosService } from 'src/eventos/eventos.service';
|
|
import { Participante } from 'src/participante/participante.entity';
|
|
import { Repository } from 'typeorm';
|
|
import { EmailDto } from './dto/emailDto.dto';
|
|
import * as xlsx from 'xlsx';
|
|
import * as pdfMake from 'pdfmake/build/pdfmake';
|
|
import * as pdfFonts from 'pdfmake/build/vfs_fonts';
|
|
/* import * as PDFDocument from 'pdfkit'; */
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
|
|
import { Response } from 'express';
|
|
import { format } from 'date-fns';
|
|
import * as ExcelJS from 'exceljs';
|
|
|
|
@Injectable()
|
|
export class EmailService {
|
|
private transporter;
|
|
|
|
constructor(
|
|
@InjectRepository(Participante)
|
|
private participanteRepository: Repository<Participante>,
|
|
private configService: ConfigService,
|
|
private readonly eventoService: EventosService,
|
|
) {
|
|
this.transporter = nodemailer.createTransport({
|
|
service: this.configService.get('NODEMAILER_SERVICE'),
|
|
auth: {
|
|
user: this.configService.get<string>('NODEMAILER_USER'),
|
|
pass: this.configService.get<string>('NODEMAILER_PASWORD'),
|
|
},
|
|
});
|
|
}
|
|
|
|
async sendEmail(id: number, message: EmailDto, res: Response) {
|
|
const participantes = await this.getEmailDeParticipantes(id);
|
|
participantes.map((res) => {
|
|
const emailDestinatario = res.email;
|
|
this.transporter.sendMail({
|
|
to: emailDestinatario,
|
|
subject: message.subject,
|
|
text: message.text,
|
|
});
|
|
});
|
|
return res.status(201).json({ message: 'Correos enviados con exito' });
|
|
}
|
|
|
|
async getEmailDeParticipantes(idEvento: number) {
|
|
const participantes = await this.participanteRepository
|
|
.createQueryBuilder('participante')
|
|
.innerJoin('participante.eventosParticipante', 'eventoParticipante')
|
|
.innerJoin('eventoParticipante.evento', 'evento')
|
|
.where('evento.id_evento = :idEvento', { idEvento })
|
|
.select(['participante.email'])
|
|
.getMany();
|
|
|
|
return participantes;
|
|
}
|
|
|
|
async getParticipantesExcel(idEvento: number, res: Response) {
|
|
console.log('entro a la funcion')
|
|
const participantes = await this.participanteRepository
|
|
.createQueryBuilder('participante')
|
|
.innerJoin('participante.eventosParticipante', 'eventoParticipante')
|
|
.innerJoin('eventoParticipante.evento', 'evento')
|
|
.where('evento.id_evento = :idEvento', { idEvento })
|
|
.select(['participante'])
|
|
.getMany();
|
|
|
|
// 1. Crear workbook y worksheet
|
|
const workbook = new ExcelJS.Workbook();
|
|
const worksheet = workbook.addWorksheet('Participantes');
|
|
|
|
// 2. Definir encabezados
|
|
worksheet.columns = [
|
|
{ header: 'ID', key: 'id', width: 10 },
|
|
{ header: 'Nombre', key: 'nombre', width: 30 },
|
|
{ header: 'Correo', key: 'correo', width: 30 },
|
|
{ header: 'Teléfono', key: 'telefono', width: 20 },
|
|
{ header: 'Carrera', key: 'carrera', width: 20 },
|
|
{ header: 'Institución', key: 'institucion_procedencia', width: 30 },
|
|
{ header: 'Apellido Paterno', key: 'apellido_paterno', width: 20 },
|
|
{ header: 'Apellido Materno', key: 'apellido_materno', width: 20 },
|
|
];
|
|
|
|
console.log(participantes);
|
|
|
|
// 3. Insertar datos
|
|
participantes.forEach((p) => {
|
|
worksheet.addRow({
|
|
nombre: p.nombre,
|
|
apellido_paterno: p.apellido_paterno,
|
|
apellido_materno: p.apellido_materno,
|
|
carrera: p.carrera,
|
|
institucion_procedencia: p.institucion_procedencia,
|
|
correo: p.email,
|
|
telefono: p.telefono,
|
|
|
|
});
|
|
});
|
|
|
|
// 4. Preparar la respuesta HTTP para enviar el Excel
|
|
res.setHeader(
|
|
'Content-Type',
|
|
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
);
|
|
res.setHeader(
|
|
'Content-Disposition',
|
|
'attachment; filename=participantes.xlsx',
|
|
);
|
|
|
|
|
|
await workbook.xlsx.write(res);
|
|
res.end();
|
|
}
|
|
|
|
|
|
async sendConstancias(file, id_evento, res: Response) {
|
|
//traemos la info del evento
|
|
const evento = await this.eventoService.getEventosPorId(id_evento);
|
|
|
|
const workbook = xlsx.read(file.buffer);
|
|
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
|
const data = xlsx.utils.sheet_to_json(worksheet);
|
|
|
|
// Carga el PDF pre-diseñado
|
|
const existingPdfBytes = fs.readFileSync('src/email/DisenoConstancia.pdf');
|
|
|
|
for (let i = 0; i < data.length; i++) {
|
|
const row = data[i] as Row;
|
|
|
|
if(row.Email == undefined){
|
|
return res.status(400).json({ message: `El correo del renglon ${ i + 2} puede estar vacio o ser undefined`, solucion: `Seleccione el renglon donde ocurre el problema y borre todo lo de este renglon, Aunque parezca que el renglon esta vacio puede que haya un espacio en algun campo y esto genera que el sistema haga la lectura de este renglon`});
|
|
}
|
|
|
|
// Carga el PDF pre-diseñado en un documento PDF editable
|
|
const pdfDoc = await PDFDocument.load(existingPdfBytes);
|
|
|
|
const pagina = pdfDoc.getPages();
|
|
const primeraPagina = pagina[0];
|
|
|
|
// Obtén el ancho y alto de la página
|
|
const { width, height } = primeraPagina.getSize();
|
|
|
|
// Obtén la fuente del texto
|
|
const font = await pdfDoc.embedFont(StandardFonts.Helvetica);
|
|
|
|
// Configura el tamaño del texto
|
|
const fontSize = 26;
|
|
|
|
const nombreParticipante = `${row['Apellido Paterno']} ${row['Apellido Materno']} ${row.Nombre}`;
|
|
|
|
// Calcula la anchura del texto del nombre del participante
|
|
const textWidth = font.widthOfTextAtSize(nombreParticipante, fontSize);
|
|
|
|
const centerX = (width - textWidth) / 2;
|
|
const centerY = (height - fontSize) / 2;
|
|
|
|
// Dibuja el texto centrado en la página
|
|
primeraPagina.drawText(nombreParticipante, {
|
|
x: centerX,
|
|
y: centerY + 70,
|
|
size: fontSize,
|
|
color: rgb(0, 0, 0),
|
|
});
|
|
|
|
const nombreCurso = `${evento.nombre}`;
|
|
|
|
// Calcula la anchura del texto del nombre del evento
|
|
const nombreCursoWidth = font.widthOfTextAtSize(nombreCurso, fontSize);
|
|
|
|
const centerXNombreCurso = (width - nombreCursoWidth) / 2;
|
|
const centerYNombreCurso = (height - fontSize) / 2;
|
|
|
|
primeraPagina.drawText(nombreCurso, {
|
|
x: centerXNombreCurso,
|
|
y: centerYNombreCurso - 17,
|
|
size: fontSize,
|
|
color: rgb(0, 0, 0),
|
|
});
|
|
|
|
const fechaCurso = `del ${this.formatoFecha(
|
|
evento.fecha_inicio,
|
|
)} al ${this.formatoFecha(evento.fecha_fin)}`;
|
|
|
|
// Calcula la anchura del texto del nombre del participante
|
|
const fechaCursoWidth = font.widthOfTextAtSize(fechaCurso, 20);
|
|
|
|
const centerXFechaCurso = (width - fechaCursoWidth) / 2;
|
|
const centerYFechaCurso = (height - 20) / 2;
|
|
|
|
primeraPagina.drawText(fechaCurso, {
|
|
x: centerXFechaCurso,
|
|
y: centerYFechaCurso - 55,
|
|
size: 20,
|
|
color: rgb(0, 0, 0),
|
|
});
|
|
|
|
// Guarda el PDF modificado en forma de bytes
|
|
const pdfBytes = await pdfDoc.save();
|
|
|
|
// Crea el objeto de adjunto con el nombre y contenido del PDF
|
|
const attachment = {
|
|
filename: `Constancia ${evento.nombre} - ${row.Nombre}_${row['Apellido Paterno']}.pdf`,
|
|
content: pdfBytes,
|
|
};
|
|
|
|
// Crea el objeto Blob y la URL para el PDF
|
|
const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
|
|
|
// Envía el correo electrónico con la constancia adjunta
|
|
this.transporter.sendMail({
|
|
to: row.Email,
|
|
subject: `Constancia de asistencia a "${evento.nombre}"`,
|
|
text: `¡Un saludo ${row.Nombre}! Aquí está tu constancia de asistencia por el evento "${evento.nombre}". Agradecemos tu asistencia y constancia, esperamos seguir viéndote en eventos futuros.`,
|
|
attachments: [attachment],
|
|
});
|
|
}
|
|
|
|
return res.status(200).json({ message: 'Constancias enviadas con éxito' });
|
|
}
|
|
|
|
|
|
formatoFecha(fecha: Date) {
|
|
const fechaLimpia = format(fecha, 'dd/MM/yyyy');
|
|
return fechaLimpia;
|
|
}
|
|
}
|