Compare commits
54 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 535e030927 | |||
| a53d9ddabe | |||
| fb31589c78 | |||
| 6238c82296 | |||
| fa472fcbf1 | |||
| da05d88923 | |||
| 20f7688f8b | |||
| 05e4443c1b | |||
| a41b30b94a | |||
| aa8efa74d9 | |||
| 98ca9f7792 | |||
| 2c953b58da | |||
| 0f986a2f7f | |||
| eaa5921c69 | |||
| b6f4084185 | |||
| 0aa0d514cb | |||
| a88db620f3 | |||
| d6f28003b3 | |||
| 5c68472a85 | |||
| a5da262469 | |||
| 562642648d | |||
| 0b591d51bd | |||
| f628d2a3b4 | |||
| 71866ffe86 | |||
| 2a33f20681 | |||
| 0b48fde321 | |||
| ef643019bb | |||
| 47747c3bed | |||
| 0eebc186b2 | |||
| 6fe658f668 | |||
| d6a63dae13 | |||
| f5ae636a7f | |||
| d6d54d17f0 | |||
| ff158b0d32 | |||
| f924401e7b | |||
| 73ef0d982b | |||
| 95b287292f | |||
| e4a56184d5 | |||
| e2afc63e28 | |||
| 0a70bf2af2 | |||
| c80590b838 | |||
| 537ef3484f | |||
| d07c4b4a41 | |||
| 291a2ff75e | |||
| 34ecc34fbe | |||
| 89066bc769 | |||
| 9d910e247c | |||
| d4b6655880 | |||
| 4a69baa07e | |||
| e29aacfc8f | |||
| b8daadd0e3 | |||
| 01e7bfd580 | |||
| a46ba72fe4 | |||
| d277503ff9 |
@@ -1,22 +0,0 @@
|
|||||||
PORT=5089
|
|
||||||
|
|
||||||
# DB_HOST=132.248.180.82
|
|
||||||
# DB_USER=user_cedetec
|
|
||||||
# DB_PASSWORD=c3t3d3c
|
|
||||||
# DB_NAME=Betelgeuse_Cedetec_Prueba
|
|
||||||
# DB_PORT=
|
|
||||||
|
|
||||||
DB_HOST=localhost
|
|
||||||
DB_USER=root
|
|
||||||
DB_PASSWORD=Dosmiluno2001
|
|
||||||
DB_NAME=cedetec_proyectos
|
|
||||||
DB_PORT=3306
|
|
||||||
|
|
||||||
DB_HOST_DSC = 132.248.180.82
|
|
||||||
DB_USER_DSC = sites_user
|
|
||||||
DB_PASS_DSC = F0rTest
|
|
||||||
DB_NAME_DSC = sat
|
|
||||||
|
|
||||||
NODEMAILER_SERVICE = gmail
|
|
||||||
NODEMAILER_USER = cidwa1@pcpuma.acatlan.unam.mx
|
|
||||||
NODEMAILER_PASWORD = cidwaxd1,
|
|
||||||
@@ -13,3 +13,18 @@ DB_NAME_DSC=
|
|||||||
NODEMAILER_SERVICE=
|
NODEMAILER_SERVICE=
|
||||||
NODEMAILER_USER=
|
NODEMAILER_USER=
|
||||||
NODEMAILER_PASWORD=
|
NODEMAILER_PASWORD=
|
||||||
|
|
||||||
|
#132.248.180.82
|
||||||
|
#3306
|
||||||
|
#usr_tl
|
||||||
|
#A1m_p@$
|
||||||
|
#Betelgeuse_TL_test
|
||||||
|
HOST_TYL=132.248.180.82
|
||||||
|
PORT_TYL=3306
|
||||||
|
USUARIO_TYL=usr_tl
|
||||||
|
CONTRASENA_TYL=A1m_p@$
|
||||||
|
DATABASE_TYL=Betelgeuse_TL_test
|
||||||
|
|
||||||
|
#Funciones para la api
|
||||||
|
API_URL=
|
||||||
|
TOKEN=
|
||||||
|
|||||||
Generated
+2230
-1644
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -8,7 +8,7 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "nest build",
|
"build": "nest build",
|
||||||
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
"start": "nest start",
|
"start": "source ~/.nvm/nvm.sh && nvm use 22.10.0 && npm install && nest start",
|
||||||
"start:dev": "nest start --watch",
|
"start:dev": "nest start --watch",
|
||||||
"start:debug": "nest start --debug --watch",
|
"start:debug": "nest start --debug --watch",
|
||||||
"start:prod": "node dist/main",
|
"start:prod": "node dist/main",
|
||||||
@@ -34,10 +34,11 @@
|
|||||||
"blob-stream": "^0.1.3",
|
"blob-stream": "^0.1.3",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.0",
|
"class-validator": "^0.14.0",
|
||||||
"exceljs": "^4.3.0",
|
"date-fns": "^2.30.0",
|
||||||
|
"exceljs": "^4.4.0",
|
||||||
"moment": "^2.29.4",
|
"moment": "^2.29.4",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"mysql2": "^3.1.2",
|
"mysql2": "^3.14.3",
|
||||||
"nodemailer": "^6.9.1",
|
"nodemailer": "^6.9.1",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Controller, Post, Get, Body } from '@nestjs/common';
|
||||||
|
import { AlmacenamientoService } from './almacenamiento.service';
|
||||||
|
|
||||||
|
@Controller('almacenamiento')
|
||||||
|
export class AlmacenamientoController {
|
||||||
|
constructor(private readonly almacenamientoService: AlmacenamientoService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
async create(@Body() body: { correo: string; agree: boolean }) {
|
||||||
|
return this.almacenamientoService.create(body.correo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
async findAll() {
|
||||||
|
const data = await this.almacenamientoService.findAll();
|
||||||
|
return { message: 'GET endpoint reached', data };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { Almacenamiento } from './entity/almacenamiento.entity';
|
||||||
|
import { AlmacenamientoService } from './almacenamiento.service';
|
||||||
|
import { AlmacenamientoController } from './almacenamiento.controller';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TypeOrmModule.forRootAsync({
|
||||||
|
name: 'almacenamiento',
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: async (configService: ConfigService) => ({
|
||||||
|
name: 'almacenamiento',
|
||||||
|
type: 'mariadb',
|
||||||
|
host: configService.get('HOST_TYL'),
|
||||||
|
port: Number(configService.get('PORT_TYL')),
|
||||||
|
username: configService.get('USUARIO_TYL'),
|
||||||
|
password: configService.get('CONTRASENA_TYL'),
|
||||||
|
database: configService.get('DATABASE_TYL'),
|
||||||
|
entities: [Almacenamiento],
|
||||||
|
synchronize: true,
|
||||||
|
ssl: false, // <- importante
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
TypeOrmModule.forFeature([Almacenamiento], 'almacenamiento'),
|
||||||
|
],
|
||||||
|
providers: [AlmacenamientoService],
|
||||||
|
controllers: [AlmacenamientoController],
|
||||||
|
})
|
||||||
|
export class AlmacenamientoModule {}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { Almacenamiento } from './entity/almacenamiento.entity';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AlmacenamientoService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Almacenamiento, 'almacenamiento')
|
||||||
|
private readonly almacenamientoRepository: Repository<Almacenamiento>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
// Example method to create a new record
|
||||||
|
async create(correo: string): Promise<any> {
|
||||||
|
console.log(correo)
|
||||||
|
const existente = await this.almacenamientoRepository.findOne({ where:{correo} });
|
||||||
|
console.log(existente)
|
||||||
|
if (!existente) {
|
||||||
|
// Maneja el error aquí mismo
|
||||||
|
throw new HttpException(
|
||||||
|
{ error: 'Este usuario no tiene acceso' },
|
||||||
|
HttpStatus.NOT_FOUND
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { mensaje: 'Usuario verificado correctamente' };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Example method to get all records
|
||||||
|
async findAll(): Promise<Almacenamiento[]> {
|
||||||
|
return this.almacenamientoRepository.find();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
|
||||||
|
@Entity('almacenamiento')
|
||||||
|
export class Almacenamiento {
|
||||||
|
@PrimaryGeneratedColumn({ type: 'int', name: 'id_almacenamiento' })
|
||||||
|
idAlmacenamiento: number;
|
||||||
|
|
||||||
|
@Column('varchar', { name: 'correo', length: 100 })
|
||||||
|
correo: string;
|
||||||
|
|
||||||
|
@Column('timestamp', {
|
||||||
|
name: 'fecha_ampliacion',
|
||||||
|
})
|
||||||
|
fechaConsulta: Date;
|
||||||
|
|
||||||
|
}
|
||||||
+17
-6
@@ -46,10 +46,14 @@ import { EmailService } from './email/email.service';
|
|||||||
import { EmailController } from './email/email.controller';
|
import { EmailController } from './email/email.controller';
|
||||||
import { EmailModule } from './email/email.module';
|
import { EmailModule } from './email/email.module';
|
||||||
import { MulterModule } from '@nestjs/platform-express';
|
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 { ServeStaticModule } from '@nestjs/serve-static';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
import { CarruselModule } from './carrusel/carrusel.module';
|
||||||
|
import { Carrusel } from './carrusel/entity/carrusel.entity';
|
||||||
|
import { ComentariosModule } from './comentarios/comentarios.module';
|
||||||
|
import { Comentarios } from './comentarios/entity/comentarios.entity';
|
||||||
|
import { AlmacenamientoModule } from './almacenamiento/almacenamiento.module';
|
||||||
|
import { Almacenamiento } from './almacenamiento/entity/almacenamiento.entity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
@@ -71,6 +75,7 @@ import { join } from 'path';
|
|||||||
username: configService.get('DB_USER'),
|
username: configService.get('DB_USER'),
|
||||||
password: configService.get('DB_PASSWORD'),
|
password: configService.get('DB_PASSWORD'),
|
||||||
database: configService.get('DB_NAME'),
|
database: configService.get('DB_NAME'),
|
||||||
|
synchronize: true,
|
||||||
entities: [
|
entities: [
|
||||||
Evento,
|
Evento,
|
||||||
Participante,
|
Participante,
|
||||||
@@ -93,8 +98,11 @@ import { join } from 'path';
|
|||||||
Equipo,
|
Equipo,
|
||||||
EquipoEvento,
|
EquipoEvento,
|
||||||
EquipoMiembro,
|
EquipoMiembro,
|
||||||
|
/* Agregado el 2 de junio */
|
||||||
|
Carrusel,
|
||||||
|
/* No recuerdo porque empece a ponerle fechas */
|
||||||
|
Comentarios,
|
||||||
],
|
],
|
||||||
synchronize: true,
|
|
||||||
}),
|
}),
|
||||||
}),
|
}),
|
||||||
EventosModule,
|
EventosModule,
|
||||||
@@ -115,21 +123,24 @@ import { join } from 'path';
|
|||||||
ProyectoActividadModule,
|
ProyectoActividadModule,
|
||||||
ActividadModule,
|
ActividadModule,
|
||||||
EmailModule,
|
EmailModule,
|
||||||
|
ComentariosModule,
|
||||||
MulterModule.register({
|
MulterModule.register({
|
||||||
dest: './uploads', // directorio de destino para guardar los archivos cargados
|
dest: './uploads', // directorio de destino para guardar los archivos cargados
|
||||||
}),
|
}),
|
||||||
|
CarruselModule,
|
||||||
|
ComentariosModule,
|
||||||
|
AlmacenamientoModule,
|
||||||
],
|
],
|
||||||
controllers: [
|
controllers: [
|
||||||
AppController,
|
AppController,
|
||||||
ParticipanteController,
|
|
||||||
InscripcionStatusController,
|
InscripcionStatusController,
|
||||||
CarouselController,
|
|
||||||
],
|
],
|
||||||
providers: [AppService, InscripcionStatusService, CarouselService],
|
providers: [AppService, InscripcionStatusService],
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
static port: number;
|
static port: number;
|
||||||
constructor(private readonly configService: ConfigService) {
|
constructor(private readonly configService: ConfigService) {
|
||||||
|
console.log('PORT', this.configService.get('PORT'));
|
||||||
AppModule.port = this.configService.get('PORT');
|
AppModule.port = this.configService.get('PORT');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +1,21 @@
|
|||||||
import { Body, Controller, Post, Request, UseGuards } from '@nestjs/common';
|
import { Body, Controller, Param, Post } from '@nestjs/common';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
|
||||||
import { Repository } from 'typeorm';
|
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
import { LoginMiembroDto } from './dto/loginMiembro.dto';
|
import { LoginMiembroDto } from './dto/loginMiembro.dto';
|
||||||
import { RegistrarMiembroDto } from './dto/registrarMiembro.dto';
|
import { RegistrarMiembroDto } from './dto/registrarMiembro.dto';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { ApiPostRegistro } from './auth.docs';
|
||||||
|
import { jwtStrategy } from './jwt.strategy';
|
||||||
|
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
|
@ApiTags('auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
constructor(
|
constructor(
|
||||||
private authService: AuthService,
|
private authService: AuthService,
|
||||||
|
private authValidar: jwtStrategy,
|
||||||
) /* @InjectRepository(Usuario) private usuarioRepository: Repository<Usuario> */ {}
|
) /* @InjectRepository(Usuario) private usuarioRepository: Repository<Usuario> */ {}
|
||||||
|
|
||||||
@Post('registro')
|
@Post('registro')
|
||||||
|
@ApiPostRegistro()
|
||||||
registrarUsuario(@Body() registrarMiembro: RegistrarMiembroDto) {
|
registrarUsuario(@Body() registrarMiembro: RegistrarMiembroDto) {
|
||||||
return this.authService.registrar(registrarMiembro);
|
return this.authService.registrar(registrarMiembro);
|
||||||
}
|
}
|
||||||
@@ -21,4 +24,9 @@ export class AuthController {
|
|||||||
login(@Body() LoginUsuario: LoginMiembroDto) {
|
login(@Body() LoginUsuario: LoginMiembroDto) {
|
||||||
return this.authService.login(LoginUsuario);
|
return this.authService.login(LoginUsuario);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Post('validate')
|
||||||
|
validar(@Param() token: any) {
|
||||||
|
return this.authValidar.validate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,78 @@
|
|||||||
|
import { applyDecorators } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
ApiOperation,
|
||||||
|
ApiResponse,
|
||||||
|
ApiTags,
|
||||||
|
ApiBody,
|
||||||
|
} from '@nestjs/swagger';
|
||||||
|
import { RegistrarMiembroDto } from './dto/registrarMiembro.dto';
|
||||||
|
|
||||||
|
export function ApiPostRegistro() {
|
||||||
|
return applyDecorators(
|
||||||
|
ApiTags('Auth'),
|
||||||
|
ApiOperation({
|
||||||
|
summary: 'Registrar nuevo miembro',
|
||||||
|
description:
|
||||||
|
'Registra un nuevo usuario en la plataforma. No se permite duplicidad de correo electrónico.',
|
||||||
|
}),
|
||||||
|
ApiBody({
|
||||||
|
type: RegistrarMiembroDto,
|
||||||
|
examples: {
|
||||||
|
requeridos: {
|
||||||
|
summary: 'Solo datos obligatorios',
|
||||||
|
description: 'Ejemplo básico con los campos mínimos requeridos para registrar un usuario.',
|
||||||
|
value: {
|
||||||
|
nombre: 'Luis',
|
||||||
|
apellido_paterno: 'Martínez',
|
||||||
|
apellido_materno: 'Lopez',
|
||||||
|
id_tipo_miembro: 1,
|
||||||
|
password: 'contraseñaSegura123',
|
||||||
|
cuenta: 421010101,
|
||||||
|
email: 'luis@example.com',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
completo: {
|
||||||
|
summary: 'Todos los campos llenos',
|
||||||
|
description: 'Ejemplo de un usuario con carrera, semestre y descripción.',
|
||||||
|
value: {
|
||||||
|
nombre: 'Ana',
|
||||||
|
apellido_paterno: 'Torres',
|
||||||
|
apellido_materno: 'Hernández',
|
||||||
|
id_tipo_miembro: 2,
|
||||||
|
password: 'AnaSegura456',
|
||||||
|
descripcion: 'Estudiante interesada en programación web',
|
||||||
|
id_carrera: 3,
|
||||||
|
semestre: '4°',
|
||||||
|
cuenta: 421020202,
|
||||||
|
email: 'ana.torres@example.com',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
invalido: {
|
||||||
|
summary: 'Ejemplo con datos inválidos',
|
||||||
|
description: 'Muestra un correo mal formado y una contraseña demasiado corta.',
|
||||||
|
value: {
|
||||||
|
nombre: 'Mario',
|
||||||
|
apellido_paterno: 'Gómez',
|
||||||
|
apellido_materno: 'Ramírez',
|
||||||
|
id_tipo_miembro: 1,
|
||||||
|
password: '123',
|
||||||
|
cuenta: 421000000,
|
||||||
|
email: 'mario-sin-arroba.com',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
ApiResponse({
|
||||||
|
status: 201,
|
||||||
|
description: 'Usuario registrado exitosamente.',
|
||||||
|
}),
|
||||||
|
ApiResponse({
|
||||||
|
status: 403,
|
||||||
|
description: 'El usuario ya existe.',
|
||||||
|
}),
|
||||||
|
ApiResponse({
|
||||||
|
status: 400,
|
||||||
|
description: 'Datos inválidos o faltantes.',
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -54,9 +54,11 @@ export class AuthService {
|
|||||||
/* console.log(token); */
|
/* console.log(token); */
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
|
user: {
|
||||||
usuario: usuario.nombre,
|
usuario: usuario.nombre,
|
||||||
id: usuario.id_miembro,
|
id: usuario.id_miembro,
|
||||||
email: usuario.email,
|
email: usuario.email,
|
||||||
|
},
|
||||||
token,
|
token,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
import {
|
|
||||||
BadRequestException,
|
|
||||||
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) {
|
|
||||||
const directory = './public';
|
|
||||||
const filePath = `${directory}/${file.originalname}`;
|
|
||||||
if (fs.existsSync(filePath)) {
|
|
||||||
cb(
|
|
||||||
new BadRequestException(
|
|
||||||
`Ya existe un archivo con el nombre: '${file.originalname}'. Si está seguro de que no es el mismo, cambie el nombre del archivo.`,
|
|
||||||
),
|
|
||||||
null,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
cb(null, file.originalname);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
fileFilter: function (req, file, cb) {
|
|
||||||
const filetypes = /jpeg|jpg|webp|png/;
|
|
||||||
const extname = filetypes.test(
|
|
||||||
path.extname(file.originalname).toLowerCase(),
|
|
||||||
);
|
|
||||||
const mimetype = filetypes.test(file.mimetype);
|
|
||||||
if (mimetype && extname) {
|
|
||||||
return cb(null, true);
|
|
||||||
}
|
|
||||||
cb(
|
|
||||||
new BadRequestException(
|
|
||||||
'Lo sentimos, para mejorar la calidad de las imagenes en el carousel solo se aceptan imagenes con terminación: .jpeg .jpg .png .webp',
|
|
||||||
),
|
|
||||||
false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
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
|
|
||||||
|
|
||||||
// Filtrar solo las imágenes con las terminaciones "jpg", "webp" y "png"
|
|
||||||
const imageNames = fileNames.filter((fileName) =>
|
|
||||||
['.jpg', '.webp', '.png'].includes(path.extname(fileName)),
|
|
||||||
);
|
|
||||||
|
|
||||||
const imageUrls = imageNames.map((imageName) => `public/${imageName}`);
|
|
||||||
// Devolver el arreglo de URLs como respuesta
|
|
||||||
return res.json(imageUrls);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class CarouselService {
|
|
||||||
|
|
||||||
uploadImage(file){
|
|
||||||
/* console.log(file) */
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
ParseIntPipe,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
Res,
|
||||||
|
UploadedFile,
|
||||||
|
UseInterceptors,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { CarruselService } from './carrusel.service';
|
||||||
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
|
import { diskStorage } from 'multer';
|
||||||
|
import { actualizarContador, contador } from './utils';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { carruselDto } from './dto/carruselDto.dto';
|
||||||
|
|
||||||
|
@Controller('carrusel')
|
||||||
|
export class CarruselController {
|
||||||
|
constructor(private carruselService: CarruselService) {}
|
||||||
|
|
||||||
|
@Post('addImage')
|
||||||
|
@UseInterceptors(
|
||||||
|
FileInterceptor('imagen', {
|
||||||
|
storage: diskStorage({
|
||||||
|
destination: './public/carrusel',
|
||||||
|
filename: function (req, file, cb) {
|
||||||
|
actualizarContador();
|
||||||
|
const { ext } = path.parse(file.originalname);
|
||||||
|
const filename = `carrusel${contador}${ext}`;
|
||||||
|
return cb(null, filename);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
fileFilter: function (req, file, cb) {
|
||||||
|
if (!file.originalname.match(/\.(jpg|jpeg|png|webp)$/)) {
|
||||||
|
return {
|
||||||
|
message:
|
||||||
|
'Lo sentimos los archivos permitidos para usar el carrusel son jpg jpeg png webp',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
cb(null, true);
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
addImagenToCarrusel(@Body() carruselDto: carruselDto, @UploadedFile() file) {
|
||||||
|
return this.carruselService.addImagenToCarrusel(carruselDto, file.filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Patch('update/:id')
|
||||||
|
updateImagenCarrusel(
|
||||||
|
@Param('id', ParseIntPipe) id: number,
|
||||||
|
@Body() carruselDto: carruselDto,
|
||||||
|
) {
|
||||||
|
return this.carruselService.updateImagenCarrusel(id, carruselDto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Put('updateOrden')
|
||||||
|
async updateOrden(@Body() banners: { id: number; orden: number}[]) {
|
||||||
|
return this.carruselService.updateOrden(banners);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete('delete/:id')
|
||||||
|
deleteImagenByRuta(@Param('id', ParseIntPipe) id: number) {
|
||||||
|
return this.carruselService.deleteImagenByRuta(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('imagenes')
|
||||||
|
getAllImagenes() {
|
||||||
|
return this.carruselService.getAllImages();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete('deleteAll')
|
||||||
|
deleteAllCarrusel() {
|
||||||
|
return this.carruselService.deleteAllCarrusel();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CarruselController } from './carrusel.controller';
|
||||||
|
import { CarruselService } from './carrusel.service';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { Carrusel } from 'src/carrusel/entity/carrusel.entity';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([Carrusel])],
|
||||||
|
controllers: [CarruselController],
|
||||||
|
providers: [CarruselService],
|
||||||
|
})
|
||||||
|
export class CarruselModule {}
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
import {
|
||||||
|
BadRequestException,
|
||||||
|
Injectable,
|
||||||
|
InternalServerErrorException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Carrusel } from 'src/carrusel/entity/carrusel.entity';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { carruselDto } from './dto/carruselDto.dto';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CarruselService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Carrusel)
|
||||||
|
private carruselRepository: Repository<Carrusel>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async addImagenToCarrusel(
|
||||||
|
carruselDto: carruselDto,
|
||||||
|
filename: string,
|
||||||
|
): Promise<{ message: string; carrusel: Carrusel }> {
|
||||||
|
if (!filename) {
|
||||||
|
throw new BadRequestException({
|
||||||
|
message: 'No se ha subido la imagen',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!carruselDto.link) {
|
||||||
|
throw new BadRequestException({
|
||||||
|
message: 'El campo "link" es obligatorio',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (carruselDto.link.length > 150) {
|
||||||
|
throw new BadRequestException({
|
||||||
|
message: 'El campo "link" no puede tener más de 100 caracteres',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
carruselDto.ruta = `/public/carrusel/${filename}`;
|
||||||
|
const carruselCreate = this.carruselRepository.create(carruselDto);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const carrusel = await this.carruselRepository.save(carruselCreate);
|
||||||
|
return {
|
||||||
|
message: 'Imagen agregada correctamente',
|
||||||
|
carrusel,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error al guardar carrusel en base de datos');
|
||||||
|
console.table({
|
||||||
|
code: error.code,
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new InternalServerErrorException({
|
||||||
|
message: 'Ocurrió un error inesperado al guardar la imagen',
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
async updateImagenCarrusel(
|
||||||
|
id: number,
|
||||||
|
carruselDto: carruselDto,
|
||||||
|
): Promise<{ message: string; carrusel: Carrusel }> {
|
||||||
|
const carrusel = await this.carruselRepository.findOne({
|
||||||
|
where: { id_imagen_carrusel: id },
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!carrusel) {
|
||||||
|
throw new BadRequestException({
|
||||||
|
message: `No se encontró una imagen de carrusel con el id ${id}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.assign(carrusel, carruselDto);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const carruselUpdated = await this.carruselRepository.save(carrusel);
|
||||||
|
return {
|
||||||
|
message: 'Imagen de carrusel actualizada correctamente',
|
||||||
|
carrusel: carruselUpdated,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error al actualizar carrusel en base de datos');
|
||||||
|
console.table({
|
||||||
|
code: error.code,
|
||||||
|
message: error.message,
|
||||||
|
});
|
||||||
|
|
||||||
|
throw new InternalServerErrorException({
|
||||||
|
message: 'Ocurrió un error inesperado al actualizar la imagen',
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Actualizar todas las imagenes por orden
|
||||||
|
async updateOrden(banners: { id: number; orden: number}[]) {
|
||||||
|
for (const banner of banners ) {
|
||||||
|
await this.carruselRepository.update(
|
||||||
|
{ id_imagen_carrusel: banner.id },
|
||||||
|
{ orden: banner.orden },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: 'Orden actualizado correctamente',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteImagenByRuta(id: number) {
|
||||||
|
try {
|
||||||
|
const res = await this.carruselRepository.findOne({
|
||||||
|
where: { id_imagen_carrusel: id },
|
||||||
|
});
|
||||||
|
|
||||||
|
await fs.promises.unlink(`./${res.ruta}`);
|
||||||
|
|
||||||
|
return this.carruselRepository.delete({ id_imagen_carrusel: id });
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getAllImages(): Promise<Carrusel[]> {
|
||||||
|
// return this.carruselRepository.find();
|
||||||
|
return this.carruselRepository.find({
|
||||||
|
order: {
|
||||||
|
orden: 'ASC',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
// return this.carruselRepository.find();
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteAllCarrusel(): Promise<{ message: string }> {
|
||||||
|
try {
|
||||||
|
// Obtener todas las imágenes del carrusel
|
||||||
|
const images = await this.carruselRepository.find();
|
||||||
|
|
||||||
|
// Eliminar archivos físicos de la carpeta public/carrusel
|
||||||
|
const carruselDir = './public/carrusel';
|
||||||
|
|
||||||
|
if (fs.existsSync(carruselDir)) {
|
||||||
|
// Leer todos los archivos en el directorio
|
||||||
|
const files = await fs.promises.readdir(carruselDir);
|
||||||
|
|
||||||
|
// Eliminar cada archivo
|
||||||
|
for (const file of files) {
|
||||||
|
const filePath = path.join(carruselDir, file);
|
||||||
|
try {
|
||||||
|
await fs.promises.unlink(filePath);
|
||||||
|
console.log(`✅ Archivo eliminado: ${filePath}`);
|
||||||
|
} catch (fileError) {
|
||||||
|
console.warn(
|
||||||
|
`⚠️ No se pudo eliminar el archivo: ${filePath}`,
|
||||||
|
fileError.message,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Eliminar todos los registros de la base de datos
|
||||||
|
await this.carruselRepository.delete({});
|
||||||
|
|
||||||
|
return {
|
||||||
|
message: `Carrusel limpiado exitosamente. Se eliminaron ${images.length} imágenes y registros.`,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.error('❌ Error al limpiar el carrusel:', error);
|
||||||
|
throw new InternalServerErrorException({
|
||||||
|
message: 'Ocurrió un error inesperado al limpiar el carrusel',
|
||||||
|
error: error.message,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
import { IsNotEmpty, IsOptional, IsNumber } from "class-validator";
|
||||||
|
|
||||||
|
export class carruselDto {
|
||||||
|
@IsNotEmpty()
|
||||||
|
ruta: string
|
||||||
|
|
||||||
|
@IsNotEmpty()
|
||||||
|
link: string
|
||||||
|
|
||||||
|
@IsOptional()
|
||||||
|
@IsNumber()
|
||||||
|
orden?: number
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import { Column, Entity, PrimaryGeneratedColumn } from "typeorm";
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Carrusel {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id_imagen_carrusel: number
|
||||||
|
|
||||||
|
@Column({length: 40})
|
||||||
|
ruta: string
|
||||||
|
/* "/public/carrusel/carrusel1.jpg" */
|
||||||
|
|
||||||
|
@Column({length: 150})
|
||||||
|
link: string
|
||||||
|
/* "https://www.acatlan.unam.mx/" */
|
||||||
|
|
||||||
|
@Column({ type: 'int', default: 0 })
|
||||||
|
orden: number
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export let contador = 0
|
||||||
|
|
||||||
|
export const actualizarContador = () => {
|
||||||
|
contador++
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
export class ComentarioDto {
|
||||||
|
from: string;
|
||||||
|
comentario: string;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
ParseIntPipe,
|
||||||
|
Post,
|
||||||
|
Query,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { ComentariosService } from './comentarios.service';
|
||||||
|
import { ComentarioDto } from './comentario.dto';
|
||||||
|
|
||||||
|
@Controller('comentarios')
|
||||||
|
export class ComentariosController {
|
||||||
|
constructor(private comentariosService: ComentariosService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
postComentario(@Body() comentario: ComentarioDto) {
|
||||||
|
return this.comentariosService.postComentario(comentario);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getComentarios() {
|
||||||
|
return this.comentariosService.getComentarios();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('by-date')
|
||||||
|
getComentariosByDate(
|
||||||
|
@Query('before') before?: string,
|
||||||
|
@Query('after') after?: string,
|
||||||
|
@Query('from') from?: string,
|
||||||
|
@Query('to') to?: string,
|
||||||
|
) {
|
||||||
|
return this.comentariosService.getComentariosByDate(before, after, from, to);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Delete(':id')
|
||||||
|
deleteComentario(@Param('id', ParseIntPipe) id: number) {
|
||||||
|
return this.comentariosService.deleteComentario(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ComentariosController } from './comentarios.controller';
|
||||||
|
import { ComentariosService } from './comentarios.service';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
import { Comentarios } from './entity/comentarios.entity';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [TypeOrmModule.forFeature([Comentarios])],
|
||||||
|
controllers: [ComentariosController],
|
||||||
|
providers: [ComentariosService],
|
||||||
|
})
|
||||||
|
export class ComentariosModule {}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { BadRequestException, Injectable } from '@nestjs/common';
|
||||||
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
|
import { Comentarios } from './entity/comentarios.entity';
|
||||||
|
import { Repository } from 'typeorm';
|
||||||
|
import { ComentarioDto } from './comentario.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ComentariosService {
|
||||||
|
constructor(
|
||||||
|
@InjectRepository(Comentarios)
|
||||||
|
private comentariosRepository: Repository<Comentarios>,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
postComentario(comentario: ComentarioDto) {
|
||||||
|
if (comentario.from !== 'CEDETEC' && comentario.from !== 'PCPUMA') {
|
||||||
|
throw new BadRequestException('Mensaje no autorizado');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const nuevoComentario = this.comentariosRepository.create(comentario);
|
||||||
|
return this.comentariosRepository.save(nuevoComentario);
|
||||||
|
}
|
||||||
|
|
||||||
|
getComentarios() {
|
||||||
|
return this.comentariosRepository.find();
|
||||||
|
}
|
||||||
|
|
||||||
|
async getComentariosByDate(
|
||||||
|
before?: string,
|
||||||
|
after?: string,
|
||||||
|
from?: string,
|
||||||
|
to?: string,
|
||||||
|
) {
|
||||||
|
const query = this.comentariosRepository.createQueryBuilder('comentarios');
|
||||||
|
|
||||||
|
if (before) {
|
||||||
|
query.andWhere('comentarios.fecha_registro < :before', {
|
||||||
|
before: new Date(before),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (after) {
|
||||||
|
query.andWhere('comentarios.fecha_registro > :after', {
|
||||||
|
after: new Date(after),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (from) {
|
||||||
|
query.andWhere('comentarios.fecha_registro >= :from', {
|
||||||
|
from: new Date(from),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (to) {
|
||||||
|
query.andWhere('comentarios.fecha_registro <= :to', { to: new Date(to) });
|
||||||
|
}
|
||||||
|
|
||||||
|
query.orderBy('comentarios.fecha_registro', 'DESC');
|
||||||
|
|
||||||
|
return await query.getMany();
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteComentario(id: number) {
|
||||||
|
return this.comentariosRepository.delete({ id_comentario: id });
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class Comentarios {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id_comentario: number;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
from: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
comentario: string;
|
||||||
|
|
||||||
|
@CreateDateColumn({ type: 'timestamp' })
|
||||||
|
fecha_registro: Date;
|
||||||
|
}
|
||||||
Binary file not shown.
@@ -1,6 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
Body,
|
Body,
|
||||||
Controller,
|
Controller,
|
||||||
|
Get,
|
||||||
HttpException,
|
HttpException,
|
||||||
HttpStatus,
|
HttpStatus,
|
||||||
Param,
|
Param,
|
||||||
@@ -8,6 +9,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
Res,
|
Res,
|
||||||
UploadedFile,
|
UploadedFile,
|
||||||
|
UseGuards,
|
||||||
UseInterceptors,
|
UseInterceptors,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { FileInterceptor } from '@nestjs/platform-express';
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
@@ -15,27 +17,49 @@ import { EmailDto } from './dto/emailDto.dto';
|
|||||||
import { EmailService } from './email.service';
|
import { EmailService } from './email.service';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import * as xlsx from 'xlsx';
|
import * as xlsx from 'xlsx';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
|
||||||
@Controller('email')
|
@Controller('email')
|
||||||
export class EmailController {
|
export class EmailController {
|
||||||
constructor(private readonly emailService: EmailService) {}
|
constructor(private readonly emailService: EmailService) { }
|
||||||
|
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
@Post('/evento/:id')
|
@Post('/evento/:id')
|
||||||
async sendEmail(
|
async sendEmail(
|
||||||
@Param('id', ParseIntPipe) id: number,
|
@Param('id', ParseIntPipe) id: number,
|
||||||
@Body() emailDto: EmailDto,
|
@Body() emailDto: EmailDto,
|
||||||
|
@Res() res,
|
||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
return this.emailService.sendEmail(id, emailDto);
|
return this.emailService.sendEmail(id, emailDto, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('constancias')
|
@UseGuards(AuthGuard('jwt'))
|
||||||
|
@Post('constancias/:id')
|
||||||
@UseInterceptors(FileInterceptor('file'))
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
async sendConstancias(@UploadedFile() file, @Body('nombreEvento') nombreEvento: string,) {
|
async sendConstancias(
|
||||||
if(!file) {
|
@UploadedFile() file,
|
||||||
throw new HttpException("No se ha cargado ningun archivo", HttpStatus.BAD_REQUEST)
|
@Param('id', ParseIntPipe) id: number,
|
||||||
|
@Res() res,
|
||||||
|
) {
|
||||||
|
if (!file) {
|
||||||
|
throw new HttpException(
|
||||||
|
'No se ha cargado ningun archivo',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
/* console.log(file) */
|
return this.emailService.sendConstancias(file, id, res);
|
||||||
return this.emailService.sendConstancias(file, nombreEvento);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//@UseGuards(AuthGuard('jwt'))
|
||||||
|
@Get(':id/participantes/xlsx')
|
||||||
|
async descargarExcel(
|
||||||
|
@Param('id') idEvento: number,
|
||||||
|
@Res() res: Response,
|
||||||
|
) {
|
||||||
|
console.log('entro en el controller')
|
||||||
|
return this.emailService.getParticipantesExcel(idEvento, res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,10 +4,13 @@ import { EventosModule } from 'src/eventos/eventos.module';
|
|||||||
import { Participante } from 'src/participante/participante.entity';
|
import { Participante } from 'src/participante/participante.entity';
|
||||||
import { EmailController } from './email.controller';
|
import { EmailController } from './email.controller';
|
||||||
import { EmailService } from './email.service';
|
import { EmailService } from './email.service';
|
||||||
|
import { EventosService } from 'src/eventos/eventos.service';
|
||||||
|
import { EventoParticipante } from 'src/evento-participante/evento-participante/eventoParticipante.entity';
|
||||||
|
import { Evento } from 'src/eventos/evento.entity';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports:[TypeOrmModule.forFeature([Participante])],
|
imports:[TypeOrmModule.forFeature([Participante, EventoParticipante, Evento])],
|
||||||
controllers:[EmailController],
|
controllers:[EmailController],
|
||||||
providers:[EmailService]
|
providers:[EmailService, EventosService]
|
||||||
})
|
})
|
||||||
export class EmailModule {}
|
export class EmailModule {}
|
||||||
|
|||||||
+163
-43
@@ -1,4 +1,11 @@
|
|||||||
import { HttpException, HttpStatus, Inject, Injectable, Res, UploadedFile } from '@nestjs/common';
|
import {
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Inject,
|
||||||
|
Injectable,
|
||||||
|
Res,
|
||||||
|
UploadedFile,
|
||||||
|
} from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import * as nodemailer from 'nodemailer';
|
import * as nodemailer from 'nodemailer';
|
||||||
@@ -13,6 +20,9 @@ import * as pdfFonts from 'pdfmake/build/vfs_fonts';
|
|||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
|
import { PDFDocument, StandardFonts, rgb } from 'pdf-lib';
|
||||||
|
import { Response } from 'express';
|
||||||
|
import { format } from 'date-fns';
|
||||||
|
import * as ExcelJS from 'exceljs';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class EmailService {
|
export class EmailService {
|
||||||
@@ -22,6 +32,7 @@ export class EmailService {
|
|||||||
@InjectRepository(Participante)
|
@InjectRepository(Participante)
|
||||||
private participanteRepository: Repository<Participante>,
|
private participanteRepository: Repository<Participante>,
|
||||||
private configService: ConfigService,
|
private configService: ConfigService,
|
||||||
|
private readonly eventoService: EventosService,
|
||||||
) {
|
) {
|
||||||
this.transporter = nodemailer.createTransport({
|
this.transporter = nodemailer.createTransport({
|
||||||
service: this.configService.get('NODEMAILER_SERVICE'),
|
service: this.configService.get('NODEMAILER_SERVICE'),
|
||||||
@@ -32,7 +43,7 @@ export class EmailService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendEmail(id: number, message: EmailDto) {
|
async sendEmail(id: number, message: EmailDto, res: Response) {
|
||||||
const participantes = await this.getEmailDeParticipantes(id);
|
const participantes = await this.getEmailDeParticipantes(id);
|
||||||
participantes.map((res) => {
|
participantes.map((res) => {
|
||||||
const emailDestinatario = res.email;
|
const emailDestinatario = res.email;
|
||||||
@@ -41,8 +52,8 @@ export class EmailService {
|
|||||||
subject: message.subject,
|
subject: message.subject,
|
||||||
text: message.text,
|
text: message.text,
|
||||||
});
|
});
|
||||||
/*console.log("Email enviado a: ", res)*/
|
|
||||||
});
|
});
|
||||||
|
return res.status(201).json({ message: 'Correos enviados con exito' });
|
||||||
}
|
}
|
||||||
|
|
||||||
async getEmailDeParticipantes(idEvento: number) {
|
async getEmailDeParticipantes(idEvento: number) {
|
||||||
@@ -57,63 +68,172 @@ export class EmailService {
|
|||||||
return participantes;
|
return participantes;
|
||||||
}
|
}
|
||||||
|
|
||||||
async sendConstancias(file, nombreEvento) {
|
async getParticipantesExcel(idEvento: number, res: Response) {
|
||||||
/* console.log(file) */
|
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 workbook = xlsx.read(file.buffer);
|
||||||
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
|
||||||
const data = xlsx.utils.sheet_to_json(worksheet);
|
const data = xlsx.utils.sheet_to_json(worksheet);
|
||||||
|
|
||||||
for (const row of data as Row[]) {
|
// Carga el PDF pre-diseñado
|
||||||
const pdfDoc = await PDFDocument.create();
|
const existingPdfBytes = fs.readFileSync('src/email/DisenoConstancia.pdf');
|
||||||
const page = pdfDoc.addPage();
|
|
||||||
page.setSize(792, 612);
|
|
||||||
|
|
||||||
const font = await pdfDoc.embedFont(StandardFonts.HelveticaBold);
|
for (let i = 0; i < data.length; i++) {
|
||||||
const fontSize = 30;
|
const row = data[i] as Row;
|
||||||
const text = 'Constancia de asistencia';
|
|
||||||
const textWidth = font.widthOfTextAtSize(text, fontSize);
|
|
||||||
const textHeight = font.heightAtSize(fontSize);
|
|
||||||
|
|
||||||
page.drawText(text, {
|
if(row.Email == undefined){
|
||||||
x: (page.getWidth() - textWidth) / 2,
|
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`});
|
||||||
y: (page.getHeight() - textHeight) / 2,
|
}
|
||||||
|
|
||||||
|
// 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,
|
size: fontSize,
|
||||||
font: font,
|
color: rgb(0, 0, 0),
|
||||||
color: rgb(0/255, 0/255, 0/255),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const { width, height } = page.getSize();
|
const nombreCurso = `${evento.nombre}`;
|
||||||
page.drawText(
|
|
||||||
`Se le otorga la constancia por su asistencia al evento: ${nombreEvento}`,
|
// Calcula la anchura del texto del nombre del evento
|
||||||
{
|
const nombreCursoWidth = font.widthOfTextAtSize(nombreCurso, fontSize);
|
||||||
x: 50,
|
|
||||||
y: height - 50,
|
const centerXNombreCurso = (width - nombreCursoWidth) / 2;
|
||||||
size: 12,
|
const centerYNombreCurso = (height - fontSize) / 2;
|
||||||
font: await pdfDoc.embedFont('Helvetica'),
|
|
||||||
},
|
primeraPagina.drawText(nombreCurso, {
|
||||||
);
|
x: centerXNombreCurso,
|
||||||
page.drawText(
|
y: centerYNombreCurso - 17,
|
||||||
`A ${row['Apellido Paterno']} ${row['Apellido Materno']} ${row.Nombre}`,
|
size: fontSize,
|
||||||
{
|
color: rgb(0, 0, 0),
|
||||||
x: 50,
|
});
|
||||||
y: height - 80,
|
|
||||||
size: 12,
|
const fechaCurso = `del ${this.formatoFecha(
|
||||||
font: await pdfDoc.embedFont('Helvetica'),
|
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();
|
const pdfBytes = await pdfDoc.save();
|
||||||
|
|
||||||
|
// Crea el objeto de adjunto con el nombre y contenido del PDF
|
||||||
const attachment = {
|
const attachment = {
|
||||||
filename: `Constancia ${nombreEvento} - ${row.Nombre}_${row['Apellido Paterno']}.pdf`,
|
filename: `Constancia ${evento.nombre} - ${row.Nombre}_${row['Apellido Paterno']}.pdf`,
|
||||||
content: pdfBytes,
|
content: pdfBytes,
|
||||||
};
|
};
|
||||||
/* const blob = new Blob([pdfBytes], { type: 'application/pdf' });
|
|
||||||
const url = URL.createObjectURL(blob); */
|
// 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({
|
this.transporter.sendMail({
|
||||||
to: row.Email,
|
to: row.Email,
|
||||||
subject: `Constancia de asistencia a "${nombreEvento}"`,
|
subject: `Constancia de asistencia a "${evento.nombre}"`,
|
||||||
text: `¡Un saludo ${row.Nombre}! Aquí está tu constancia de asistencia por el evento "${nombreEvento}". Agradecemos tu asistencia y constancia, esperamos seguir viéndote en eventos futuros.`,
|
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],
|
attachments: [attachment],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return res.status(200).json({ message: 'Constancias enviadas con éxito' });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
formatoFecha(fecha: Date) {
|
||||||
|
const fechaLimpia = format(fecha, 'dd/MM/yyyy');
|
||||||
|
return fechaLimpia;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Body, Controller, Param, ParseIntPipe, Post } from '@nestjs/common';
|
import { Body, Controller, Get, Param, ParseIntPipe, Post } from '@nestjs/common';
|
||||||
import { RegistrarParticipanteDto } from './dto/registrarParticipanteDto.dto';
|
import { RegistrarParticipanteDto } from './dto/registrarParticipanteDto.dto';
|
||||||
import { EventoParticipanteService } from './evento-participante.service';
|
import { EventoParticipanteService } from './evento-participante.service';
|
||||||
import { EventoParticipante } from './eventoParticipante.entity';
|
import { EventoParticipante } from './eventoParticipante.entity';
|
||||||
@@ -12,5 +12,8 @@ export class EventoParticipanteController {
|
|||||||
return this.eventoParticipanteService.registrarParticipante(id, datos)
|
return this.eventoParticipanteService.registrarParticipante(id, datos)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getEventoParticipante(): Promise<EventoParticipante[]> {
|
||||||
|
return this.eventoParticipanteService.getEventoParticipantes();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -75,4 +75,8 @@ export class EventoParticipanteService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getEventoParticipantes() {
|
||||||
|
return this.eventoParticipanteRepository.find()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export class actualizarEventoDto {
|
|||||||
proyecto?: string;
|
proyecto?: string;
|
||||||
modalidad?: string;
|
modalidad?: string;
|
||||||
lugar?: string;
|
lugar?: string;
|
||||||
requisitos?: string;
|
observaciones?: string;
|
||||||
cuota_inscripcion?: number;
|
cuota_inscripcion?: number;
|
||||||
patrocinador?: string;
|
patrocinador?: string;
|
||||||
tipo_acreditacion?: string;
|
tipo_acreditacion?: string;
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export class crearEventoDto {
|
|||||||
modalidad: string;
|
modalidad: string;
|
||||||
lugar: string;
|
lugar: string;
|
||||||
cupo: number;
|
cupo: number;
|
||||||
requisitos: string;
|
observaciones: string;
|
||||||
cuota_inscripcion: number;
|
cuota_inscripcion: number;
|
||||||
patrocinador: string;
|
patrocinador: string;
|
||||||
tipo_acreditacion: string;
|
tipo_acreditacion: string;
|
||||||
|
|||||||
@@ -14,41 +14,41 @@ export class Evento {
|
|||||||
@PrimaryGeneratedColumn()
|
@PrimaryGeneratedColumn()
|
||||||
id_evento: number;
|
id_evento: number;
|
||||||
|
|
||||||
@Column({length: 70, nullable: false })
|
@Column({ length: 70, nullable: false })
|
||||||
nombre: string;
|
nombre: string;
|
||||||
|
|
||||||
@Column({length: 15, nullable: false })
|
@Column({ length: 15, nullable: false })
|
||||||
tipo_evento: string;
|
tipo_evento: string;
|
||||||
|
|
||||||
@Column({length: 100,nullable: false})
|
@Column({ length: 100, nullable: false })
|
||||||
organizadores: string
|
organizadores: string;
|
||||||
|
|
||||||
@Column({length:30 ,nullable: true})
|
@Column({ length: 30, nullable: true })
|
||||||
proyecto: string
|
proyecto: string;
|
||||||
|
|
||||||
@Column({length: 40,nullable: false})
|
@Column({ length: 40, nullable: false })
|
||||||
modalidad: string
|
modalidad: string;
|
||||||
|
|
||||||
@Column({length: 60,nullable: false })
|
@Column({ length: 60, nullable: false })
|
||||||
lugar: string;
|
lugar: string;
|
||||||
|
|
||||||
@Column({nullable: false})
|
@Column({ nullable: false })
|
||||||
cupo: number;
|
cupo: number;
|
||||||
|
|
||||||
@Column({length: 60, nullable: false })
|
@Column({ length: 500, nullable: false })
|
||||||
requisitos: string;
|
observaciones: string;
|
||||||
|
|
||||||
@Column({type:'decimal', precision: 7, scale: 2, nullable: true })
|
@Column({ type: 'decimal', precision: 7, scale: 2, nullable: true })
|
||||||
cuota_inscripcion: number;
|
cuota_inscripcion: number;
|
||||||
|
|
||||||
@Column({length: 50, nullable: true })
|
@Column({ length: 50, nullable: true })
|
||||||
patrocinador: string;
|
patrocinador: string;
|
||||||
|
|
||||||
@Column({length: 40, nullable: true })
|
@Column({ length: 40, nullable: true })
|
||||||
tipo_acreditacion: string;
|
tipo_acreditacion: string;
|
||||||
|
|
||||||
@Column({length: 200 ,nullable: false})
|
@Column({ length: 500, nullable: false })
|
||||||
objetivo: string
|
objetivo: string;
|
||||||
|
|
||||||
@Column({ nullable: false, type: 'datetime' })
|
@Column({ nullable: false, type: 'datetime' })
|
||||||
fecha_inicio: Date;
|
fecha_inicio: Date;
|
||||||
@@ -59,7 +59,7 @@ export class Evento {
|
|||||||
@Column({ nullable: false, type: 'datetime' })
|
@Column({ nullable: false, type: 'datetime' })
|
||||||
fecha_limite_inscripcion: Date;
|
fecha_limite_inscripcion: Date;
|
||||||
|
|
||||||
@Column({length:40 ,nullable: true })
|
@Column({ length: 40, nullable: true })
|
||||||
horario: string;
|
horario: string;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
@@ -71,9 +71,6 @@ export class Evento {
|
|||||||
)
|
)
|
||||||
eventoParticipantes: EventoParticipante[];
|
eventoParticipantes: EventoParticipante[];
|
||||||
|
|
||||||
@OneToMany(
|
@OneToMany(() => EquipoEvento, (equipoEvento) => equipoEvento.evento)
|
||||||
() => EquipoEvento, (equipoEvento) => equipoEvento.evento
|
equipoEvento: EquipoEvento;
|
||||||
)
|
|
||||||
equipoEvento: EquipoEvento
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Res } from '@nestjs/common';
|
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Res, UseGuards } from '@nestjs/common';
|
||||||
import { Response } from 'express';
|
import { Response } from 'express';
|
||||||
import { InjectRepository } from '@nestjs/typeorm';
|
import { InjectRepository } from '@nestjs/typeorm';
|
||||||
import { EventoParticipante } from 'src/evento-participante/evento-participante/eventoParticipante.entity';
|
import { EventoParticipante } from 'src/evento-participante/evento-participante/eventoParticipante.entity';
|
||||||
@@ -7,6 +7,7 @@ import { actualizarEventoDto } from './dto/actualizarEvento.dto';
|
|||||||
import { crearEventoDto } from './dto/crearEvento.dto';
|
import { crearEventoDto } from './dto/crearEvento.dto';
|
||||||
import { Evento } from './evento.entity';
|
import { Evento } from './evento.entity';
|
||||||
import { EventosService } from './eventos.service';
|
import { EventosService } from './eventos.service';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
|
||||||
@Controller('eventos')
|
@Controller('eventos')
|
||||||
export class EventosController {
|
export class EventosController {
|
||||||
@@ -18,21 +19,29 @@ export class EventosController {
|
|||||||
return this.eventoService.getEventos()
|
return this.eventoService.getEventos()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Get('disponibles')
|
||||||
|
getEventosDisponibles (){
|
||||||
|
return this.eventoService.getEventosDisponibles()
|
||||||
|
}
|
||||||
|
|
||||||
@Get(':id')
|
@Get(':id')
|
||||||
getEventosPorId(@Param('id', ParseIntPipe)id: number):Promise<Evento>{
|
getEventosPorId(@Param('id', ParseIntPipe)id: number):Promise<Evento>{
|
||||||
return this.eventoService.getEventosPorId(id)
|
return this.eventoService.getEventosPorId(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
@Post()
|
@Post()
|
||||||
postEvento(@Body() nuevoEvento: crearEventoDto){
|
postEvento(@Body() nuevoEvento: crearEventoDto){
|
||||||
return this.eventoService.postEvento(nuevoEvento)
|
return this.eventoService.postEvento(nuevoEvento)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
@Patch(':id')
|
@Patch(':id')
|
||||||
patchEvento(@Param('id', ParseIntPipe)id:number, @Body() actualizacion: actualizarEventoDto){
|
patchEvento(@Param('id', ParseIntPipe)id:number, @Body() actualizacion: actualizarEventoDto){
|
||||||
return this.eventoService.updateEvento(id,actualizacion)
|
return this.eventoService.updateEvento(id,actualizacion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
@Delete(':id')
|
@Delete(':id')
|
||||||
deleteEvento(@Param('id', ParseIntPipe)id: number){
|
deleteEvento(@Param('id', ParseIntPipe)id: number){
|
||||||
return this.eventoService.deleteEventoParticipante(id)
|
return this.eventoService.deleteEventoParticipante(id)
|
||||||
@@ -43,21 +52,18 @@ export class EventosController {
|
|||||||
return this.eventoService.getCupos(id)
|
return this.eventoService.getCupos(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
@Get(':id/participante')
|
@Get(':id/participante')
|
||||||
async getParticipnates(@Param('id', ParseIntPipe)id:number){
|
async getParticipnates(@Param('id', ParseIntPipe)id:number){
|
||||||
return this.eventoService.getParticipantes(id)
|
return this.eventoService.getParticipantes(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
@Get(':id/exportExcel')
|
@Get(':id/exportExcel')
|
||||||
async exportarExcel (@Param('id', ParseIntPipe)id:number ,@Res() res: Response){
|
async exportarExcel (@Param('id', ParseIntPipe)id:number ,@Res() res: Response){
|
||||||
this.eventoService.exportarParticipantes(id, res)
|
this.eventoService.exportarParticipantes(id, res)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Post('disponibles')
|
|
||||||
getEventosDisponibles (){
|
|
||||||
return this.eventoService.getEventosDisponibles()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Post('disponibles/pendientesDconstancia')
|
@Post('disponibles/pendientesDconstancia')
|
||||||
getEventosDisponiblesPendientes(){
|
getEventosDisponiblesPendientes(){
|
||||||
return this.eventoService.geteventosDisponiblesMasDosDias()
|
return this.eventoService.geteventosDisponiblesMasDosDias()
|
||||||
|
|||||||
@@ -20,10 +20,14 @@ export class EventosService {
|
|||||||
) {}
|
) {}
|
||||||
|
|
||||||
getEventos() {
|
getEventos() {
|
||||||
return this.eventoRepository.find();
|
return this.eventoRepository.find({
|
||||||
|
order: {
|
||||||
|
fecha_inicio: 'DESC',
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getEventosPorId(id: number) {
|
public getEventosPorId(id: number) {
|
||||||
return this.eventoRepository.findOne({
|
return this.eventoRepository.findOne({
|
||||||
where: {
|
where: {
|
||||||
id_evento: id,
|
id_evento: id,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import {
|
|||||||
Post,
|
Post,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { InscripcionDscService } from './inscripcion-dsc.service';
|
import { InscripcionDscService } from './inscripcion-dsc.service';
|
||||||
|
import { And } from 'typeorm';
|
||||||
|
|
||||||
@Controller('inscripcion-dsc')
|
@Controller('inscripcion-dsc')
|
||||||
export class InscripcionDscController {
|
export class InscripcionDscController {
|
||||||
@@ -29,7 +30,7 @@ export class InscripcionDscController {
|
|||||||
|
|
||||||
try{
|
try{
|
||||||
/* console.log("verificando confirmacion:", userInformation);
|
/* console.log("verificando confirmacion:", userInformation);
|
||||||
*/ if (userInformation[0].confirmacion) {
|
*/ if (userInformation[0].confirmacion && userInformation[1].confirmacion) {
|
||||||
return {
|
return {
|
||||||
error: true,
|
error: true,
|
||||||
msj: 'El usuario ya hizo la confirmación anteriormente',
|
msj: 'El usuario ya hizo la confirmación anteriormente',
|
||||||
|
|||||||
@@ -4,16 +4,21 @@ import { createConnection } from 'mysql2/promise';
|
|||||||
@Injectable()
|
@Injectable()
|
||||||
export class InscripcionDscService {
|
export class InscripcionDscService {
|
||||||
async userVerification(accountNumber: string) {
|
async userVerification(accountNumber: string) {
|
||||||
|
let connection;
|
||||||
try {
|
try {
|
||||||
const connection = await createConnection({
|
connection = await createConnection({
|
||||||
host: process.env.DB_HOST_DSC,
|
host: process.env.DB_HOST_DSC,
|
||||||
user: process.env.DB_USER_DSC,
|
user: process.env.DB_USER_DSC,
|
||||||
password: process.env.DB_PASS_DSC,
|
password: process.env.DB_PASS_DSC,
|
||||||
database: process.env.DB_NAME_DSC,
|
database: process.env.DB_NAME_DSC,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// const userInformationQuery = `SELECT A.id_cuenta, nombre, if(AI.platica=true,1,0) as confirmacion FROM alumno A
|
||||||
|
// JOIN alumno_inscrito AI ON (A.id_cuenta = AI.id_cuenta)
|
||||||
|
// JOIN periodo P ON (P.id_periodo = AI.id_periodo)
|
||||||
|
// WHERE A.id_cuenta = "${accountNumber}" and P.activo=true`;
|
||||||
const userInformationQuery = `SELECT A.id_cuenta, nombre, if(AI.platica=true,1,0) as confirmacion FROM alumno A
|
const userInformationQuery = `SELECT A.id_cuenta, nombre, if(AI.platica=true,1,0) as confirmacion FROM alumno A
|
||||||
JOIN alumno_inscrito AI ON (A.id_cuenta = AI.id_cuenta)
|
JOIN alumno_inscrito AI ON (A.id_cuenta = AI.id_cuenta) and (AI.id_plataforma = 1 || AI.id_plataforma = 2)
|
||||||
JOIN periodo P ON (P.id_periodo = AI.id_periodo)
|
JOIN periodo P ON (P.id_periodo = AI.id_periodo)
|
||||||
WHERE A.id_cuenta = "${accountNumber}" and P.activo=true`;
|
WHERE A.id_cuenta = "${accountNumber}" and P.activo=true`;
|
||||||
|
|
||||||
@@ -23,10 +28,15 @@ export class InscripcionDscService {
|
|||||||
const resultsArray = JSON.parse(JSON.stringify(results));
|
const resultsArray = JSON.parse(JSON.stringify(results));
|
||||||
if (resultsArray.length === 0) return null;
|
if (resultsArray.length === 0) return null;
|
||||||
/* console.log('The solution is: ', resultsArray); */
|
/* console.log('The solution is: ', resultsArray); */
|
||||||
|
console.log('The solution is: ', resultsArray);
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw error;
|
throw error;
|
||||||
|
} finally {
|
||||||
|
if (connection) {
|
||||||
|
await connection.end();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,7 +20,12 @@ async function bootstrap() {
|
|||||||
prefix: '/public/',
|
prefix: '/public/',
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
app.enableCors();
|
app.enableCors();
|
||||||
await app.listen(AppModule.port);
|
await app.listen(AppModule.port);
|
||||||
|
|
||||||
|
const port = AppModule.port;
|
||||||
|
console.log(`Documentacion disponible en: http://localhost:${port}/api`);
|
||||||
|
console.log(`Servidor desplegado en: http://localhost:${port}`);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
import { Controller, Post } from '@nestjs/common';
|
import { Controller, Get, Post } from '@nestjs/common';
|
||||||
import { ParticipanteService } from './participante.service';
|
import { ParticipanteService } from './participante.service';
|
||||||
|
import { Participante } from './participante.entity';
|
||||||
|
|
||||||
@Controller('participante')
|
@Controller('participante')
|
||||||
export class ParticipanteController {}
|
export class ParticipanteController {
|
||||||
|
constructor(private participanteService: ParticipanteService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getParticipantes(): Promise<Participante[]> {
|
||||||
|
return this.participanteService.getParticipantes();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -31,4 +31,8 @@ export class ParticipanteService {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getParticipantes() {
|
||||||
|
return this.participanteRepository.find();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user