Files
cedetec_api_nest/src/email/email.service.ts
T
2026-08-09 21:50:53 -06:00

256 lines
8.5 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 '../eventos/eventos.service';
import { Participante } from '../participante/participante.entity';
import { Repository } from 'typeorm';
import { EmailDto } from './dto/emailDto.dto';
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 = new ExcelJS.Workbook();
await workbook.xlsx.load(file.buffer);
const worksheet = workbook.worksheets[0];
const data: any[] = [];
const headers: string[] = [];
worksheet.eachRow((row, rowNumber) => {
const rowData: any = {};
row.eachCell({ includeEmpty: true }, (cell, colNumber) => {
if (rowNumber === 1) {
headers[colNumber] = String(cell.value).trim();
} else {
rowData[headers[colNumber]] = cell.value;
}
});
if (rowNumber > 1) {
data.push(rowData);
}
});
// Carga el PDF pre-diseñado
const existingPdfBytes = fs.readFileSync('../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;
}
}