envio de constancias pdf, se añadio banner
This commit is contained in:
Generated
+2737
-63
File diff suppressed because it is too large
Load Diff
+10
-1
@@ -26,24 +26,32 @@
|
||||
"@nestjs/jwt": "^10.0.2",
|
||||
"@nestjs/passport": "^9.0.3",
|
||||
"@nestjs/platform-express": "^9.0.0",
|
||||
"@nestjs/serve-static": "^3.0.1",
|
||||
"@nestjs/swagger": "^6.2.1",
|
||||
"@nestjs/typeorm": "^9.0.1",
|
||||
"@types/multer": "^1.4.7",
|
||||
"bcrypt": "^5.1.0",
|
||||
"blob-stream": "^0.1.3",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.0",
|
||||
"exceljs": "^4.3.0",
|
||||
"moment": "^2.29.4",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"mysql2": "^3.1.2",
|
||||
"nodemailer": "^6.9.1",
|
||||
"passport": "^0.6.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"pdfkit": "^0.13.0",
|
||||
"pg": "^8.9.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rxjs": "^7.2.0",
|
||||
"swagger-ui-express": "^4.6.1",
|
||||
"sybase": "^1.2.3",
|
||||
"typeorm": "^0.3.12"
|
||||
"typeorm": "^0.3.12",
|
||||
"uuid": "^9.0.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jridgewell/sourcemap-codec": "^1.4.14",
|
||||
@@ -61,6 +69,7 @@
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^4.0.0",
|
||||
"jest": "29.3.1",
|
||||
"pdfmake": "^0.2.7",
|
||||
"prettier": "^2.3.2",
|
||||
"source-map-support": "^0.5.20",
|
||||
"supertest": "^6.1.3",
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
+10
-1
@@ -45,6 +45,11 @@ import { EquipoMiembro } from './equipo-miembro/entity/equipo_miembro.entity';
|
||||
import { EmailService } from './email/email.service';
|
||||
import { EmailController } from './email/email.controller';
|
||||
import { EmailModule } from './email/email.module';
|
||||
import { MulterModule } from '@nestjs/platform-express';
|
||||
import { CarouselController } from './carousel/carousel.controller';
|
||||
import { CarouselService } from './carousel/carousel.service';
|
||||
import { ServeStaticModule } from '@nestjs/serve-static';
|
||||
import { join } from 'path';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
@@ -106,13 +111,17 @@ import { EmailModule } from './email/email.module';
|
||||
ProyectoActividadModule,
|
||||
ActividadModule,
|
||||
EmailModule,
|
||||
MulterModule.register({
|
||||
dest: './uploads', // directorio de destino para guardar los archivos cargados
|
||||
}),
|
||||
],
|
||||
controllers: [
|
||||
AppController,
|
||||
ParticipanteController,
|
||||
InscripcionStatusController,
|
||||
CarouselController,
|
||||
],
|
||||
providers: [AppService, InscripcionStatusService],
|
||||
providers: [AppService, InscripcionStatusService, CarouselService],
|
||||
})
|
||||
export class AppModule {
|
||||
static port: number;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CarouselController } from './carousel.controller';
|
||||
|
||||
describe('CarouselController', () => {
|
||||
let controller: CarouselController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [CarouselController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<CarouselController>(CarouselController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,60 @@
|
||||
import {
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
Post,
|
||||
Res,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { Response } from 'express';
|
||||
import * as fs from 'fs';
|
||||
import { diskStorage } from 'multer';
|
||||
import * as path from 'path';
|
||||
|
||||
@Controller('carousel')
|
||||
export class CarouselController {
|
||||
private images: string[] = [];
|
||||
|
||||
@Post('/imagenes')
|
||||
@UseInterceptors(
|
||||
FileInterceptor('file', {
|
||||
storage: diskStorage({
|
||||
destination: './public',
|
||||
filename: function (req, file, cb) {
|
||||
cb(null, file.originalname);
|
||||
},
|
||||
}),
|
||||
}),
|
||||
)
|
||||
async addImage(@UploadedFile() file) {
|
||||
return {msg: 'Imagen guardada con exito :)'}
|
||||
}
|
||||
|
||||
@Delete('/imagenes/:nombreArchivo')
|
||||
async deleteImage(@Param('nombreArchivo') nombreArchivo: string) {
|
||||
try {
|
||||
await fs.promises.unlink(`./public/${nombreArchivo}`);
|
||||
return { message: 'Imagen eliminada exitosamente' };
|
||||
} catch (err) {
|
||||
throw new Error('Error al eliminar la imagen');
|
||||
}
|
||||
}
|
||||
|
||||
@Get('/imagenes')
|
||||
async getImages(@Res() res: Response) {
|
||||
// Leer el directorio 'public' para obtener el nombre de todos los archivos
|
||||
const fileNames = await fs.promises.readdir('./public');
|
||||
// Generar la URL para cada archivo y agregarla a un arreglo
|
||||
const baseUrl = 'http://localhost:5089'; // reemplaza con la URL de tu servidor
|
||||
const imageUrls = fileNames.map(
|
||||
(fileName) => `${baseUrl}/public/${fileName}`,
|
||||
);
|
||||
// Devolver el arreglo de URLs como respuesta
|
||||
return res.json(imageUrls);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { CarouselService } from './carousel.service';
|
||||
|
||||
describe('CarouselService', () => {
|
||||
let service: CarouselService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [CarouselService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<CarouselService>(CarouselService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
@Injectable()
|
||||
export class CarouselService {
|
||||
|
||||
uploadImage(file){
|
||||
console.log(file)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
interface Row {
|
||||
Asistencia: number
|
||||
Nombre: string
|
||||
'Apellido Paterno': string
|
||||
'Apellido Materno': string
|
||||
Carrera: string
|
||||
'Institución Procedencia': string
|
||||
Email: string
|
||||
}
|
||||
@@ -1,6 +1,21 @@
|
||||
import { Body, Controller, Param, ParseIntPipe, Post } from '@nestjs/common';
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Param,
|
||||
ParseIntPipe,
|
||||
Post,
|
||||
Res,
|
||||
UploadedFile,
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { EmailDto } from './dto/emailDto.dto';
|
||||
import { EmailService } from './email.service';
|
||||
import { Response } from 'express';
|
||||
import * as xlsx from 'xlsx';
|
||||
|
||||
|
||||
@Controller('email')
|
||||
export class EmailController {
|
||||
@@ -13,4 +28,14 @@ export class EmailController {
|
||||
): Promise<any> {
|
||||
return this.emailService.sendEmail(id, emailDto);
|
||||
}
|
||||
|
||||
@Post('constancias')
|
||||
@UseInterceptors(FileInterceptor('file'))
|
||||
async sendConstancias(@UploadedFile() file, @Body('nombreEvento') nombreEvento: string,) {
|
||||
if(!file) {
|
||||
throw new HttpException("No se ha cargado ningun archivo", HttpStatus.BAD_REQUEST)
|
||||
}
|
||||
console.log(file)
|
||||
return this.emailService.sendConstancias(file, nombreEvento);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
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';
|
||||
@@ -6,6 +6,13 @@ 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';
|
||||
|
||||
@Injectable()
|
||||
export class EmailService {
|
||||
@@ -49,4 +56,80 @@ export class EmailService {
|
||||
|
||||
return participantes;
|
||||
}
|
||||
|
||||
async sendConstancias(file, nombreEvento) {
|
||||
console.log(file)
|
||||
const workbook = xlsx.read(file.buffer);
|
||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||
const data = xlsx.utils.sheet_to_json(worksheet);
|
||||
|
||||
for (const row of data as Row[]) {
|
||||
const pdfDoc = await PDFDocument.create();
|
||||
const page = pdfDoc.addPage();
|
||||
page.setSize(792, 612);
|
||||
|
||||
page.drawRectangle({
|
||||
x: 0,
|
||||
y: page.getHeight() - 60,
|
||||
width: page.getWidth(),
|
||||
height: 60,
|
||||
color: rgb(0 / 255, 61 / 255, 121 / 255),
|
||||
});
|
||||
|
||||
page.drawRectangle({
|
||||
x: 0,
|
||||
y: 0,
|
||||
width: page.getWidth(),
|
||||
height: 60,
|
||||
color: rgb(213 / 255, 159 / 255, 15 / 255),
|
||||
});
|
||||
|
||||
const font = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
||||
const fontSize = 30;
|
||||
const text = 'Constancia de asistencia';
|
||||
const textWidth = font.widthOfTextAtSize(text, fontSize);
|
||||
const textHeight = font.heightAtSize(fontSize);
|
||||
|
||||
page.drawText(text, {
|
||||
x: (page.getWidth() - textWidth) / 2,
|
||||
y: (page.getHeight() - textHeight) / 2,
|
||||
size: fontSize,
|
||||
font: font,
|
||||
color: rgb(0/255, 0/255, 0/255),
|
||||
});
|
||||
|
||||
const { width, height } = page.getSize();
|
||||
page.drawText(
|
||||
`Se le otorga la constancia por su asistencia al evento: ${nombreEvento}`,
|
||||
{
|
||||
x: 50,
|
||||
y: height - 50,
|
||||
size: 12,
|
||||
font: await pdfDoc.embedFont('Helvetica'),
|
||||
},
|
||||
);
|
||||
page.drawText(
|
||||
`A ${row['Apellido Paterno']} ${row['Apellido Materno']} ${row.Nombre}`,
|
||||
{
|
||||
x: 50,
|
||||
y: height - 80,
|
||||
size: 12,
|
||||
font: await pdfDoc.embedFont('Helvetica'),
|
||||
},
|
||||
);
|
||||
const pdfBytes = await pdfDoc.save();
|
||||
const attachment = {
|
||||
filename: `Constancia ${nombreEvento} - ${row.Nombre}_${row['Apellido Paterno']}.pdf`,
|
||||
content: pdfBytes,
|
||||
};
|
||||
/* const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
||||
const url = URL.createObjectURL(blob); */
|
||||
this.transporter.sendMail({
|
||||
to: row.Email,
|
||||
subject: 'Constancia de asistencia',
|
||||
text: 'Hola',
|
||||
attachments: [attachment],
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,4 +58,9 @@ export class EventosController {
|
||||
return this.eventoService.getEventosDisponibles()
|
||||
}
|
||||
|
||||
@Post('disponibles/pendientesDconstancia')
|
||||
getEventosDisponiblesPendientes(){
|
||||
return this.eventoService.geteventosDisponiblesMasDosDias()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { EventoParticipante } from 'src/evento-participante/evento-participante/eventoParticipante.entity';
|
||||
import { Participante } from 'src/participante/participante.entity';
|
||||
import { MoreThanOrEqual, Repository } from 'typeorm';
|
||||
import { Between, MoreThanOrEqual, Repository } from 'typeorm';
|
||||
import { actualizarEventoDto } from './dto/actualizarEvento.dto';
|
||||
import { crearEventoDto } from './dto/crearEvento.dto';
|
||||
import { Evento } from './evento.entity';
|
||||
@@ -186,4 +186,12 @@ export class EventosService {
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
geteventosDisponiblesMasDosDias(){
|
||||
const hoy = new Date();
|
||||
return this.eventoRepository.createQueryBuilder("evento")
|
||||
.where(`DATE_ADD(evento.fecha_fin, INTERVAL 2 DAY) >= :hoy`, { hoy })
|
||||
.andWhere('fecha_fin <= :hoy', {hoy})
|
||||
.getMany();
|
||||
}
|
||||
}
|
||||
|
||||
+7
-1
@@ -1,9 +1,11 @@
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||
import { join } from 'path';
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
||||
|
||||
const config = new DocumentBuilder()
|
||||
.addBearerAuth()
|
||||
@@ -14,6 +16,10 @@ async function bootstrap() {
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api', app, document);
|
||||
|
||||
app.useStaticAssets(join(__dirname, '..', 'public'), {
|
||||
prefix: '/public/',
|
||||
});
|
||||
|
||||
app.enableCors();
|
||||
await app.listen(AppModule.port);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user