77 Commits

Author SHA1 Message Date
Patriots01 87f11ae968 Se termino de actualizar las dependencias faltantes que no suponen riesgo para el proyecto, a versiones estables, asi como dependencias de desarrollo, se hicieron pruebas de esta version y es estable y apta para migrar al sistema de pruebas, falta actualizar las dependencias criticas como lo son: nestjs, TypeScript, etc, estas versiones se recomienda hacer un plan de trabajo para que no causen conflicto al actualizar el sistema 2026-08-03 15:33:47 -06:00
Patriots01 becf48529d Se implemento la carga de banner con orden en automatico, asi como la funcion de poder actualizar el orden de los banner, ademas de algunas actualizaciones de dependencias, todo esto se probo y funciona correctamente, se mandan estos cambios por que es una version estable a la cual se puede actualizar antes de seguir actualizando las dependencias faltantes 2026-08-03 15:28:04 -06:00
Patriots01 028a8b277d Modificaciones del carrusel 2026-08-03 11:14:41 -06:00
Patriots01 535e030927 Modificaciones en los archivos de carrusel, entity, controller, servicio, se agrego un nuevo endpoint para actualizar los banner y se agrego un campo de orden en la entity 2026-02-18 21:16:38 +00:00
Patriots01 a53d9ddabe Se actualizo la infromacion de comentarios y participantes en el orden de enviar la infromacion y se documento el campo de orden en carrusel, para no causar conflicto falta arreglar esta parte 2026-02-17 22:32:20 +00:00
santiago f02dd40d51 Se quito el orden para no causar conflicto 2026-02-09 12:22:33 -06:00
santiago b607cc2ccb Se quito el orden en los banners 2026-02-09 12:15:50 -06:00
santiago b5a832e179 Modificaciones 2026-02-09 12:13:50 -06:00
Patriots01 fb31589c78 Se implemento el cierre de la conexion en la base de datos 2026-02-07 00:40:01 +00:00
jalvarado 6238c82296 Merge pull request 'feat: ampliacion de campos' (#17) from develop into master
Reviewed-on: #17
2026-02-04 18:35:27 -06:00
miguel fa472fcbf1 feat: ampliacion de campos 2026-02-04 18:33:09 -06:00
Patriots01 da05d88923 Se modifico la confirmacion del alumno 2026-02-04 20:39:58 +00:00
santiago 20f7688f8b Modificaciones en produccion 2025-09-12 20:12:08 -06:00
santiago 05e4443c1b Mandar modificaciones a produccion 2025-09-12 20:09:28 -06:00
santiago a41b30b94a Modificacion y refactorizacion del codigo 2025-08-27 12:44:22 -06:00
santiago aa8efa74d9 Modificacion en la peticion para verificar si existe el correo 2025-08-27 12:30:46 -06:00
santiago 98ca9f7792 Modificacion para agegar la descarga de participantes de eventos 2025-08-23 09:57:16 -06:00
santiago 2c953b58da Subir cambios a develop 2025-08-22 12:48:25 -06:00
evenegas 0f986a2f7f Descarga de participantes 2025-08-22 12:14:54 -06:00
evenegas eaa5921c69 se agregó fecha de consulta a la api 2025-08-20 11:43:26 -06:00
evenegas b6f4084185 se agregó fecha de consulta a la api 2025-08-20 11:42:55 -06:00
evenegas 0aa0d514cb se agregó implementacion de Api para ampliación 2025-08-19 12:16:17 -06:00
jalvarado a88db620f3 Merge pull request 'develop' (#16) from develop into master
Reviewed-on: #16
2025-08-12 14:58:18 -06:00
miguel d6f28003b3 Implement deleteAllCarrusel method to remove all images and database records from the carrusel 2025-08-12 14:56:34 -06:00
santiago 5c68472a85 Agregar funcionalidad para agregar almacenamiento en la base de cedetec de T&L 2025-07-30 14:16:14 -06:00
miguel a5da262469 Add Almacenamiento module, controller, service, and entity for data management 2025-07-28 21:52:03 -06:00
miguel 562642648d Merge branch 'develop' 2025-07-28 10:56:37 -06:00
miguel 0b591d51bd Refactor eventos controller to remove duplicate getEventosDisponibles method 2025-07-28 10:49:25 -06:00
stern f628d2a3b4 Modificación a script start en package.json para uso de Node 22 2025-05-14 11:41:55 -06:00
stern 71866ffe86 Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/cedetec_api_nest into develop 2025-05-14 11:39:47 -06:00
stern 2a33f20681 Modificación a script start en package.json para uso de Node 22 2025-05-14 11:39:04 -06:00
jalvarado 0b48fde321 Increase maximum link length validation to 150 characters in CarruselService 2025-05-14 11:39:02 -06:00
jalvarado ef643019bb Merge pull request 'ampliacion link' (#14) from master into develop
Reviewed-on: #14
2025-05-14 11:35:03 -06:00
jalvarado 47747c3bed ampliacion link 2025-05-14 11:33:45 -06:00
jalvarado 0eebc186b2 Merge pull request 'master' (#12) from master into develop
Reviewed-on: #12
2025-05-13 20:54:12 -06:00
jorgemike 6fe658f668 Refactor authentication and carousel services; enhance error handling and validation 2025-05-13 19:42:36 -06:00
jalvarado d6a63dae13 Merge pull request 'Merge pull request 'master' (#10) from master into develop' (#11) from develop into master
Reviewed-on: #11
2024-07-24 19:34:50 -06:00
mike f5ae636a7f Seguridaad 2024-07-24 14:34:01 -06:00
jalvarado d6d54d17f0 Merge pull request 'master' (#10) from master into develop
Reviewed-on: #10
2024-07-23 19:56:34 -06:00
mike ff158b0d32 CRUD de comentarios 2024-07-23 19:00:51 -06:00
Jorge Miguel Alvarado Reyes f924401e7b Observaciones 2024-01-17 16:37:40 -06:00
Jorge Miguel Alvarado Reyes 73ef0d982b Requisitos -> Obervaciones, longitud 60 -> 300 2024-01-17 16:35:20 -06:00
jalvarado 95b287292f Merge pull request 'develop' (#9) from develop into master
Reviewed-on: #9
2023-08-04 09:42:53 -06:00
jalvarado e4a56184d5 Carrusel envia mensaje de error enlugar de detener el servicio 2023-06-26 12:51:29 -06:00
JorgeMike e2afc63e28 Muchos cambios 2023-06-26 08:33:18 -06:00
JorgeMike 0a70bf2af2 Se puede agregar links al carrusel 2023-06-06 14:07:49 -06:00
jalvarado c80590b838 Merge pull request 'email' (#8) from email into develop
Reviewed-on: #8
2023-05-03 20:19:14 +00:00
jalvarado d237c594ca Merge branch 'email' of https://repositorio.acatlan.unam.mx/CIDWA/cedetec_api_nest into email 2023-05-03 13:53:03 -06:00
jalvarado 42652f4fc1 ya se ejecuta el query correcto 2023-05-03 13:52:09 -06:00
JorgeMike 537ef3484f .env removed 2023-03-27 09:24:45 -06:00
JorgeMike d07c4b4a41 the banner edit is ready and name of participant its getting update in eache event 2023-03-27 09:24:11 -06:00
JorgeMike 7a712928bd the banner edit is ready and name of participant its getting update in eache event 2023-03-26 19:16:06 -06:00
jalvarado e098a52fd8 envio de constancias pdf, se añadio banner 2023-03-24 15:00:03 -06:00
jalvarado 291a2ff75e deleted .env 2023-03-21 14:38:54 -06:00
jalvarado 34ecc34fbe se agrego el crear excels con los participantes 2023-03-21 14:36:38 -06:00
jalvarado 6d6eb825d2 El excel ya no se guarda en el servidor 2023-03-21 14:23:35 -06:00
JorgeMike 357121500b eliminacion de excel 2023-03-20 20:01:29 -06:00
JorgeMike 4d6435ac8a se agrego controller para traer solo eventos disponibles y no todos 2023-03-20 20:00:10 -06:00
jalvarado 569dbca11f se modifico la base de datos y se agrego los excel 2023-03-17 13:38:37 -06:00
jalvarado 89066bc769 removed .env original and added new variables 2023-03-14 15:56:05 -06:00
jalvarado 9d910e247c Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/cedetec_api_nest into develop 2023-03-14 15:52:20 -06:00
jalvarado d4b6655880 Merge branch 'email' into develop 2023-03-14 15:51:53 -06:00
jalvarado 4a69baa07e Actualizar '.env.example' 2023-03-14 21:46:05 +00:00
jalvarado 0158b6af98 add new info in env 2023-03-14 15:41:02 -06:00
jalvarado e29aacfc8f Actualizar '.env.example' 2023-03-14 21:38:33 +00:00
jalvarado 498e70e589 sending email is already in the project 2023-03-14 15:26:00 -06:00
jalvarado b8daadd0e3 eliminar el env 2023-03-10 10:01:24 -06:00
jalvarado 01e7bfd580 Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/cedetec_api_nest into develop 2023-03-10 10:00:15 -06:00
jalvarado a46ba72fe4 env example 2023-03-10 09:58:48 -06:00
jalvarado d277503ff9 Merge pull request 'cupo_evento' (#3) from cupo_evento into develop
Reviewed-on: #3
2023-03-10 15:56:55 +00:00
jalvarado 878bccde38 solucion conflicto con env 2023-03-10 09:55:34 -06:00
jalvarado 7556fec383 se agrego el envExample 2023-03-10 09:52:59 -06:00
jalvarado ba9d1b02f6 Merge pull request 'controlador para mostrar cupo en eventos' (#2) from cupo_evento into develop
Reviewed-on: #2
2023-03-10 15:39:05 +00:00
jalvarado 15fd67c7d9 Merge branch 'develop' into cupo_evento 2023-03-10 15:38:24 +00:00
jalvarado ca3e4c42bd controlador para mostrar cupo en eventos 2023-03-10 09:10:57 -06:00
jalvarado 97e1735481 Eliminar '.env' 2023-03-09 21:29:26 +00:00
jalvarado a2f83a7a00 Merge pull request 'primer_rama' (#1) from primer_rama into develop
Reviewed-on: #1
2023-03-09 21:24:02 +00:00
71 changed files with 8310 additions and 3523 deletions
-12
View File
@@ -1,12 +0,0 @@
PORT=5089
DB_HOST=localhost
DB_USER=root
DB_PASSWORD=password
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
+30
View File
@@ -0,0 +1,30 @@
PORT=
DB_HOST=
DB_USER=
DB_PASSWORD=
DB_NAME=
DB_HOST_DSC=
DB_USER_DSC=
DB_PASS_DSC=
DB_NAME_DSC=
NODEMAILER_SERVICE=
NODEMAILER_USER=
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=
+1 -1
View File
@@ -1,3 +1,4 @@
.env
# compiled output
/dist
/node_modules
@@ -34,4 +35,3 @@ lerna-debug.log*
!.vscode/launch.json
!.vscode/extensions.json
.env
+6765 -3287
View File
File diff suppressed because it is too large Load Diff
+24 -11
View File
@@ -8,7 +8,7 @@
"scripts": {
"build": "nest build",
"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:debug": "nest start --debug --watch",
"start:prod": "node dist/main",
@@ -26,31 +26,43 @@
"@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",
"bcrypt": "^5.1.0",
"blob-stream": "^0.1.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"moment": "^2.29.4",
"mysql2": "^3.1.2",
"class-validator": "^0.15.1",
"date-fns": "^2.30.0",
"exceljs": "^4.4.0",
"moment": "^2.30.1",
"multer": "^1.4.5-lts.1",
"mysql2": "^3.23.2",
"nodemailer": "^6.9.1",
"passport": "^0.6.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"pg": "^8.9.0",
"pdf-lib": "^1.17.1",
"pdfkit": "^0.13.0",
"pg": "^8.22.0",
"reflect-metadata": "^0.1.13",
"rxjs": "^7.2.0",
"rxjs": "^7.8.2",
"swagger-ui-express": "^4.6.1",
"sybase": "^1.2.3",
"typeorm": "^0.3.12"
"typeorm": "^0.3.12",
"uuid": "^9.0.1",
"xlsx": "^0.18.5"
},
"devDependencies": {
"@jridgewell/sourcemap-codec": "^1.4.14",
"@nestjs/cli": "^9.0.0",
"@nestjs/schematics": "^9.0.0",
"@nestjs/testing": "^9.0.0",
"@types/express": "^4.17.13",
"@types/express": "^5.0.6",
"@types/jest": "29.2.4",
"@types/multer": "^2.2.0",
"@types/node": "18.11.18",
"@types/passport-local": "^1.0.35",
"@types/passport-local": "^1.0.38",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.0.0",
"@typescript-eslint/parser": "^5.0.0",
@@ -58,12 +70,13 @@
"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",
"ts-jest": "29.0.3",
"ts-loader": "^9.2.3",
"ts-node": "^10.0.0",
"ts-loader": "^9.6.2",
"ts-node": "^10.9.2",
"tsconfig-paths": "4.1.1",
"typescript": "^4.7.4"
},
Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 KiB

View File
+1 -1
View File
@@ -1,2 +1,2 @@
INSERT INTO tipo_miembro(tipo_miembro) VALUES("Admin");
INSERT INTO tipo_miembro(tipo_miembro) VALUES("Alumno");
INSERT INTO tipo_miembro(tipo_miembro) VALUES("Miembro");
+1 -1
View File
@@ -1,2 +1,2 @@
INSERT INTO miembro (nombre, apellido_paterno, apellido_materno, id_tipo_miembro, email, password, descripcion, semestre, cuenta)
VALUES ('Jorge', 'Alvarado', 'Reyes', 1, 'Admin@unam.com', '$2b$10$1ExBjP1J1PET5N4m9Thv7O0l0nNjPsMYQW6.TZveaCFz0OkLh06Dy', 'Estudiante de Ingeniería en Sistemas', '4', 421010301);
VALUES ('Jorge', 'Alvarado', 'Reyes', 1, 'Admin@unam.com', '$2b$10$1ExBjP1J1PET5N4m9Thv7O0l0nNjPsMYQW6.TZveaCFz0OkLh06Dy', 'Estudiante de Ingeniería en Sistemas', '4', 123456789);
@@ -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;
}
+32 -6
View File
@@ -7,14 +7,12 @@ import { EventosModule } from './eventos/eventos.module';
import { ParticipanteController } from './participante/participante.controller';
import { ParticipanteModule } from './participante/participante.module';
import { EventoParticipanteModule } from './evento-participante/evento-participante/evento-participante.module';
import { UsuarioModule } from './usuario/usuario.module';
import { AuthModule } from './auth/auth.module';
import { PassportModule } from '@nestjs/passport';
import { JwtModule } from '@nestjs/jwt';
import { Evento } from './eventos/evento.entity';
import { Participante } from './participante/participante.entity';
import { EventoParticipante } from './evento-participante/evento-participante/eventoParticipante.entity';
import { Usuario } from './usuario/usuario.entity';
import { InscripcionDscModule } from './inscripcion-dsc/inscripcion-dsc.module';
import { InscripcionAreaModule } from './inscripcion-area/inscripcion-area.module';
import { InscripcionStatusService } from './inscripcion-status/inscripcion-status.service';
@@ -44,9 +42,26 @@ import { Contacto } from './contacto/entity/contacto.entity';
import { Equipo } from './equipo/entity/equipo.entity';
import { EquipoEvento } from './equipo-evento/entity/equipo_evento.entity';
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 { ServeStaticModule } from '@nestjs/serve-static';
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';
import { TylController } from './tyl/tyl.controller';
@Module({
imports: [
ServeStaticModule.forRoot({
rootPath: join(__dirname, '..', 'public'),
exclude: ['/api*'], // Excluir la ruta "/api" de servir archivos estáticos
}),
ConfigModule.forRoot({
envFilePath: '.env',
isGlobal: true,
@@ -61,11 +76,11 @@ import { EquipoMiembro } from './equipo-miembro/entity/equipo_miembro.entity';
username: configService.get('DB_USER'),
password: configService.get('DB_PASSWORD'),
database: configService.get('DB_NAME'),
synchronize: true,
entities: [
Evento,
Participante,
EventoParticipante,
Usuario,
/* Agregadas el 29 de febrero */
Miembro,
InscripcionArea,
@@ -84,14 +99,16 @@ import { EquipoMiembro } from './equipo-miembro/entity/equipo_miembro.entity';
Equipo,
EquipoEvento,
EquipoMiembro,
/* Agregado el 2 de junio */
Carrusel,
/* No recuerdo porque empece a ponerle fechas */
Comentarios,
],
synchronize: true,
}),
}),
EventosModule,
ParticipanteModule,
EventoParticipanteModule,
UsuarioModule,
AuthModule,
PassportModule,
JwtModule.register({}),
@@ -106,17 +123,26 @@ import { EquipoMiembro } from './equipo-miembro/entity/equipo_miembro.entity';
ProyectoModule,
ProyectoActividadModule,
ActividadModule,
EmailModule,
ComentariosModule,
MulterModule.register({
dest: './uploads', // directorio de destino para guardar los archivos cargados
}),
CarruselModule,
ComentariosModule,
AlmacenamientoModule,
],
controllers: [
AppController,
ParticipanteController,
InscripcionStatusController,
TylController,
],
providers: [AppService, InscripcionStatusService],
})
export class AppModule {
static port: number;
constructor(private readonly configService: ConfigService) {
console.log('PORT', this.configService.get('PORT'));
AppModule.port = this.configService.get('PORT');
}
}
+14 -8
View File
@@ -1,26 +1,32 @@
import { Body, Controller, Post, Request, UseGuards } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { InjectRepository } from '@nestjs/typeorm';
import { Usuario } from 'src/usuario/usuario.entity';
import { Repository } from 'typeorm';
import { Body, Controller, Param, Post } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LoginUsuarioDto } from './dto/loginUsuario.dto';
import { LoginMiembroDto } from './dto/loginMiembro.dto';
import { RegistrarMiembroDto } from './dto/registrarMiembro.dto';
import { RegistrarUsuarioDto } from './dto/registrarUsuario.dto';
import { ApiTags } from '@nestjs/swagger';
import { ApiPostRegistro } from './auth.docs';
import { jwtStrategy } from './jwt.strategy';
@Controller('auth')
@ApiTags('auth')
export class AuthController {
constructor(
private authService: AuthService,
private authValidar: jwtStrategy,
) /* @InjectRepository(Usuario) private usuarioRepository: Repository<Usuario> */ {}
@Post('registro')
@ApiPostRegistro()
registrarUsuario(@Body() registrarMiembro: RegistrarMiembroDto) {
return this.authService.registrar(registrarMiembro);
}
@Post('login')
login(@Body() LoginUsuario: LoginUsuarioDto) {
login(@Body() LoginUsuario: LoginMiembroDto) {
return this.authService.login(LoginUsuario);
}
@Post('validate')
validar(@Param() token: any) {
return this.authValidar.validate
}
}
+78
View File
@@ -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.',
}),
);
}
+5 -3
View File
@@ -54,9 +54,11 @@ export class AuthService {
/* console.log(token); */
const data = {
usuario: usuario.nombre,
id: usuario.id_miembro,
email: usuario.email,
user: {
usuario: usuario.nombre,
id: usuario.id_miembro,
email: usuario.email,
},
token,
};
+1 -1
View File
@@ -5,7 +5,7 @@ export class LoginMiembroDto {
@IsEmail()
email: string
@MinLength(10)
@MinLength(5)
@MaxLength(15)
password: string
}
-10
View File
@@ -1,10 +0,0 @@
import { IsEmail, MaxLength, MinLength } from "class-validator";
export class LoginUsuarioDto {
@IsEmail()
email: string
@MinLength(10)
@MaxLength(15)
password: string
}
+2 -2
View File
@@ -1,8 +1,8 @@
import { PartialType } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, MaxLength, MinLength } from 'class-validator';
import { LoginUsuarioDto } from './loginUsuario.dto';
import { LoginMiembroDto } from './loginMiembro.dto';
export class RegistrarMiembroDto extends PartialType(LoginUsuarioDto) {
export class RegistrarMiembroDto extends PartialType(LoginMiembroDto) {
@IsNotEmpty()
nombre: string;
-18
View File
@@ -1,18 +0,0 @@
import { PartialType } from '@nestjs/swagger';
import { IsEmail, IsNotEmpty, MaxLength, MinLength } from 'class-validator';
import { LoginUsuarioDto } from './loginUsuario.dto';
export class RegistrarUsuarioDto extends PartialType(LoginUsuarioDto) {
@IsNotEmpty()
name: string;
@IsNotEmpty()
lastName: string;
@IsEmail()
email: string;
@MinLength(10)
@MaxLength(15)
password: string;
}
+80
View 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();
}
}
+12
View File
@@ -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 {}
+208
View File
@@ -0,0 +1,208 @@
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}`;
// Obtener el banner con mayor orden
const ultimoCarrusel = await this.carruselRepository.find({
order: {
orden: 'DESC',
},
take: 1
});
const ultimoOrden = ultimoCarrusel.length > 0 ? ultimoCarrusel[0].orden : 0;
// const siguienteOrden = ultimoCarrusel
// ? ultimoCarrusel.orden + 1
// : 1;
// const carruselCreate = this.carruselRepository.create(carruselDto);
const carruselCreate = this.carruselRepository.create({
...carruselDto,
orden: ultimoOrden + 1,
});
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}[]) {
// Paso 1.- Mandar los banner a un orden extremo para que no causen conflicto al actualizar
for( const banner of banners ) {
await this.carruselRepository.update(
{ id_imagen_carrusel: banner.id },
{ orden: banner.orden + 1000 },
);
}
// Paso 2.- Actualizamos al nuevo orden
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,
});
}
}
}
+13
View File
@@ -0,0 +1,13 @@
import { IsNotEmpty, IsOptional, IsNumber } from "class-validator";
export class carruselDto {
@IsNotEmpty()
ruta: string
@IsNotEmpty()
link: string
@IsOptional()
@IsNumber()
orden?: number
}
+18
View File
@@ -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', unique: true })
orden: number
}
+5
View File
@@ -0,0 +1,5 @@
export let contador = 0
export const actualizarContador = () => {
contador++
}
+1 -1
View File
@@ -6,7 +6,7 @@ export class Area {
@PrimaryGeneratedColumn()
id_area: number
@Column({length:60, nullable:false})
@Column({length:20, nullable:false})
area: string
@OneToMany(
+1 -1
View File
@@ -6,7 +6,7 @@ export class Carrera {
@PrimaryGeneratedColumn()
id_carrera: number
@Column({ length:70, nullable: false})
@Column({ length:40, nullable: false})
carrera: string
@OneToMany(
+2 -2
View File
@@ -6,11 +6,11 @@ export class TipoMiembro {
@PrimaryGeneratedColumn()
id_tipo_miembro: number
@Column({length:60, nullable:false})
@Column({length:15, nullable:false})
tipo_miembro: string
@OneToMany(
() => Miembro, (miembro) => miembro.tipoMiembro
)
miembro: Miembro
miembro: Miembro[]
}
+4
View File
@@ -0,0 +1,4 @@
export class ComentarioDto {
from: string;
comentario: string;
}
+43
View File
@@ -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);
}
}
+12
View File
@@ -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 {}
+66
View File
@@ -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;
}
+3 -3
View File
@@ -10,13 +10,13 @@ export class Contacto {
@Column({nullable:false})
email: string
@Column()
@Column({length: 40})
horario: string
@Column({nullable:true})
@Column({length: 75, nullable:true})
facebook: string
@Column({nullable: true})
@Column({length: 60, nullable: true})
linkedin: string
@ManyToOne(
Binary file not shown.
+8
View File
@@ -0,0 +1,8 @@
import { IsEmail } from "class-validator"
export class EmailDto {
subject:string
text:string
}
+9
View 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
}
+18
View File
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { EmailController } from './email.controller';
describe('EmailController', () => {
let controller: EmailController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [EmailController],
}).compile();
controller = module.get<EmailController>(EmailController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
+65
View File
@@ -0,0 +1,65 @@
import {
Body,
Controller,
Get,
HttpException,
HttpStatus,
Param,
ParseIntPipe,
Post,
Res,
UploadedFile,
UseGuards,
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';
import { AuthGuard } from '@nestjs/passport';
@Controller('email')
export class EmailController {
constructor(private readonly emailService: EmailService) { }
@UseGuards(AuthGuard('jwt'))
@Post('/evento/:id')
async sendEmail(
@Param('id', ParseIntPipe) id: number,
@Body() emailDto: EmailDto,
@Res() res,
): Promise<any> {
return this.emailService.sendEmail(id, emailDto, res);
}
@UseGuards(AuthGuard('jwt'))
@Post('constancias/:id')
@UseInterceptors(FileInterceptor('file'))
async sendConstancias(
@UploadedFile() file,
@Param('id', ParseIntPipe) id: number,
@Res() res,
) {
if (!file) {
throw new HttpException(
'No se ha cargado ningun archivo',
HttpStatus.BAD_REQUEST,
);
}
return this.emailService.sendConstancias(file, id, res);
}
//@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);
}
}
+16
View File
@@ -0,0 +1,16 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EventosModule } from 'src/eventos/eventos.module';
import { Participante } from 'src/participante/participante.entity';
import { EmailController } from './email.controller';
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({
imports:[TypeOrmModule.forFeature([Participante, EventoParticipante, Evento])],
controllers:[EmailController],
providers:[EmailService, EventosService]
})
export class EmailModule {}
+18
View File
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { EmailService } from './email.service';
describe('EmailService', () => {
let service: EmailService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [EmailService],
}).compile();
service = module.get<EmailService>(EmailService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});
+239
View File
@@ -0,0 +1,239 @@
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;
}
}
+1 -1
View File
@@ -10,7 +10,7 @@ export class Equipo{
@Column({length:50, nullable:true})
nombre: string
@Column({length:250})
@Column({length:100})
descripcion:string
@OneToMany(
@@ -17,6 +17,11 @@ export class RegistrarParticipanteDto {
@IsNotEmpty()
carrera: string;
@IsString()
@IsNotEmpty()
institucion_procedencia: string;
@IsEmail()
email: string;
}
@@ -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 { EventoParticipanteService } from './evento-participante.service';
import { EventoParticipante } from './eventoParticipante.entity';
@@ -12,5 +12,8 @@ export class EventoParticipanteController {
return this.eventoParticipanteService.registrarParticipante(id, datos)
}
@Get()
getEventoParticipante(): Promise<EventoParticipante[]> {
return this.eventoParticipanteService.getEventoParticipantes();
}
}
@@ -46,12 +46,25 @@ export class EventoParticipanteService {
id_evento: number,
registrarParticipanteDto: RegistrarParticipanteDto,
) {
/* console.log(id_evento);
console.log(registrarParticipanteDto); */
return this.participanteExiste(registrarParticipanteDto.email).then(
(res) => {
if (res) {
this.registrarEnEvento(id_evento, res);
res.nombre = registrarParticipanteDto.nombre;
res.apellido_paterno = registrarParticipanteDto.apellido_paterno;
res.apellido_materno = registrarParticipanteDto.apellido_materno;
res.carrera = registrarParticipanteDto.carrera;
res.institucion_procedencia =
registrarParticipanteDto.institucion_procedencia;
//si el correo ya existe en la base de datos solo se le cambia el nombre, apellidos, carrera e institucion
return this.participanteRepository
.save(res)
.then((participanteActualizado) => {
this.registrarEnEvento(id_evento, participanteActualizado);
return participanteActualizado;
});
} else {
this.participanteRepository
.save(this.participanteRepository.create(registrarParticipanteDto))
@@ -62,4 +75,8 @@ export class EventoParticipanteService {
},
);
}
getEventoParticipantes() {
return this.eventoParticipanteRepository.find()
}
}
+4 -4
View File
@@ -1,11 +1,11 @@
export class actualizarEventoDto {
nombre?: string;
tipo_evento?: string;
organizadores?: string[];
proyecto?: string[];
modalidad?: string[];
organizadores?: string;
proyecto?: string;
modalidad?: string;
lugar?: string;
requisitos?: string;
observaciones?: string;
cuota_inscripcion?: number;
patrocinador?: string;
tipo_acreditacion?: string;
+5 -5
View File
@@ -1,16 +1,16 @@
export class crearEventoDto {
nombre: string;
tipo_evento: string;
organizadores: string[];
proyecto: string[];
modalidad: string[];
organizadores: string;
proyecto: string;
modalidad: string;
lugar: string;
cupo: number;
requisitos: string;
observaciones: string;
cuota_inscripcion: number;
patrocinador: string;
tipo_acreditacion: string;
descripcion: string;
objetivo: string;
fecha_inicio: Date;
fecha_fin: Date;
fecha_limite_inscripcion: Date;
+25 -31
View File
@@ -14,55 +14,52 @@ export class Evento {
@PrimaryGeneratedColumn()
id_evento: number;
@Column({ nullable: false })
@Column({ length: 70, nullable: false })
nombre: string;
@Column({ nullable: false })
@Column({ length: 15, nullable: false })
tipo_evento: string;
@Column("simple-array", {nullable: false})
organizadores: string[]
@Column({ length: 100, nullable: false })
organizadores: string;
@Column("simple-array", {nullable: false})
proyecto: string[]
@Column("simple-array", {nullable: false})
modalidad: string[]
@Column({ nullable: false })
@Column({ length: 30, nullable: true })
proyecto: string;
@Column({ length: 40, nullable: false })
modalidad: string;
@Column({ length: 60, nullable: false })
lugar: string;
@Column({nullable: false})
@Column({ nullable: false })
cupo: number;
@Column({ nullable: false })
requisitos: string;
@Column({ length: 500, nullable: false })
observaciones: string;
@Column({ nullable: true })
@Column({ type: 'decimal', precision: 7, scale: 2, nullable: true })
cuota_inscripcion: number;
@Column({ nullable: true })
@Column({ length: 50, nullable: true })
patrocinador: string;
@Column({ nullable: true })
@Column({ length: 40, nullable: true })
tipo_acreditacion: string;
@Column({nullable: false})
descripcion: string
@Column({ length: 500, nullable: false })
objetivo: string;
@Column({ nullable: false, type: 'datetime' })
fecha_inicio: Date;
@Column({ nullable: false, type: 'datetime' })
fecha_fin: Date;
@Column({ nullable: false, type: 'datetime' })
fecha_limite_inscripcion: Date;
@Column({ nullable: true })
evento_identificador: string;
@Column({ nullable: true })
@Column({ length: 40, nullable: true })
horario: string;
@Column({ nullable: true })
@@ -74,9 +71,6 @@ export class Evento {
)
eventoParticipantes: EventoParticipante[];
@OneToMany(
() => EquipoEvento, (equipoEvento) => equipoEvento.evento
)
equipoEvento: EquipoEvento
@OneToMany(() => EquipoEvento, (equipoEvento) => equipoEvento.evento)
equipoEvento: EquipoEvento;
}
+40 -4
View File
@@ -1,8 +1,13 @@
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post } from '@nestjs/common';
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, Res, UseGuards } from '@nestjs/common';
import { Response } from 'express';
import { InjectRepository } from '@nestjs/typeorm';
import { EventoParticipante } from 'src/evento-participante/evento-participante/eventoParticipante.entity';
import { Repository } from 'typeorm';
import { actualizarEventoDto } from './dto/actualizarEvento.dto';
import { crearEventoDto } from './dto/crearEvento.dto';
import { Evento } from './evento.entity';
import { EventosService } from './eventos.service';
import { AuthGuard } from '@nestjs/passport';
@Controller('eventos')
export class EventosController {
@@ -14,23 +19,54 @@ export class EventosController {
return this.eventoService.getEventos()
}
@Get('disponibles')
getEventosDisponibles (){
return this.eventoService.getEventosDisponibles()
}
@Get(':id')
getEventosPorId(@Param('id', ParseIntPipe)id: number):Promise<Evento>{
return this.eventoService.getEventosPorId(id)
}
@UseGuards(AuthGuard('jwt'))
@Post()
postEvento(@Body() nuevoEvento: crearEventoDto){
return this.eventoService.postEvento(nuevoEvento)
return this.eventoService.postEvento(nuevoEvento)
}
@UseGuards(AuthGuard('jwt'))
@Patch(':id')
patchEvento(@Param('id', ParseIntPipe)id:number, @Body() actualizacion: actualizarEventoDto){
return this.eventoService.updateEvento(id,actualizacion)
}
@UseGuards(AuthGuard('jwt'))
@Delete(':id')
deleteEvento(@Param('id', ParseIntPipe)id: number){
return this.eventoService.deleteEventoParticipante(id)
return this.eventoService.deleteEventoParticipante(id)
}
@Get(':id/cupos')
async getCupos(@Param('id', ParseIntPipe)id: number){
return this.eventoService.getCupos(id)
}
@UseGuards(AuthGuard('jwt'))
@Get(':id/participante')
async getParticipnates(@Param('id', ParseIntPipe)id:number){
return this.eventoService.getParticipantes(id)
}
@UseGuards(AuthGuard('jwt'))
@Get(':id/exportExcel')
async exportarExcel (@Param('id', ParseIntPipe)id:number ,@Res() res: Response){
this.eventoService.exportarParticipantes(id, res)
}
@Post('disponibles/pendientesDconstancia')
getEventosDisponiblesPendientes(){
return this.eventoService.geteventosDisponiblesMasDosDias()
}
}
+2 -1
View File
@@ -1,12 +1,13 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EventoParticipante } from 'src/evento-participante/evento-participante/eventoParticipante.entity';
import { Participante } from 'src/participante/participante.entity';
import { Evento } from './evento.entity';
import { EventosController } from './eventos.controller';
import { EventosService } from './eventos.service';
@Module({
imports:[TypeOrmModule.forFeature([Evento, EventoParticipante])],
imports:[TypeOrmModule.forFeature([Evento, EventoParticipante, Participante])],
controllers:[EventosController],
providers:[EventosService]
})
+141 -3
View File
@@ -2,10 +2,12 @@ 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 { 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';
import * as ExcelJS from 'exceljs';
import { Response } from 'express';
@Injectable()
export class EventosService {
@@ -13,13 +15,19 @@ export class EventosService {
@InjectRepository(Evento) private eventoRepository: Repository<Evento>,
@InjectRepository(EventoParticipante)
private eventoParticipanteRepository: Repository<EventoParticipante>,
@InjectRepository(Participante)
private participanteRepository: Repository<Participante>,
) {}
getEventos() {
return this.eventoRepository.find();
return this.eventoRepository.find({
order: {
fecha_inicio: 'DESC',
},
});
}
getEventosPorId(id: number) {
public getEventosPorId(id: number) {
return this.eventoRepository.findOne({
where: {
id_evento: id,
@@ -60,4 +68,134 @@ export class EventosService {
await queryRunner.release();
}
}
async getCupos(id: number) {
const evento = this.getEventosPorId(id);
if (!evento) throw new Error(`El evento con id:${id} no existe`);
const registrados = await this.eventoParticipanteRepository
.createQueryBuilder('ep')
.select('COUNT(ep.id_evento)', 'count')
.where('ep.id_evento = :eventoId', { eventoId: (await evento).id_evento })
.getRawOne();
const cupoOriginal = (await evento).cupo;
const cupoDisponible = cupoOriginal - registrados.count;
return { registrados: registrados.count, cupoDisponible, cupoOriginal };
}
async getParticipantes(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.nombre',
'participante.apellido_paterno',
'participante.apellido_materno',
'participante.carrera',
'participante.email',
'participante.institucion_procedencia',
])
.getMany();
return participantes;
}
async exportarParticipantes(idEvento: number, res: Response): Promise<void> {
const participantes: Participante[] = await this.getParticipantes(idEvento);
/*console.log(participantes);*/
// Crear el workbook y la worksheet
const workbook = new ExcelJS.Workbook();
const worksheet = workbook.addWorksheet('Participantes');
// Definir las columnas
worksheet.columns = [
{
header: 'Asistencia',
key: 'asistencia',
width: 10,
style: { alignment: { horizontal: 'center' } },
},
{
header: 'Nombre',
key: 'nombre',
width: 20,
style: { alignment: { horizontal: 'center' } },
},
{
header: 'Apellido Paterno',
key: 'apellido_paterno',
width: 20,
style: { alignment: { horizontal: 'center' } },
},
{
header: 'Apellido Materno',
key: 'apellido_materno',
width: 20,
style: { alignment: { horizontal: 'center' } },
},
{
header: 'Carrera',
key: 'carrera',
width: 20,
style: { alignment: { horizontal: 'center' } },
},
{
header: 'Email',
key: 'email',
width: 35,
style: { alignment: { horizontal: 'center' } },
},
{
header: 'Institución Procedencia',
key: 'institucion_procedencia',
width: 30,
style: { alignment: { horizontal: 'center' } },
},
];
// Agregar los datos a la worksheet
participantes.forEach((participante) => {
worksheet.addRow({
asistencia: '',
nombre: participante.nombre,
apellido_paterno: participante.apellido_paterno,
apellido_materno: participante.apellido_materno,
carrera: participante.carrera,
email: participante.email,
institucion_procedencia: participante.institucion_procedencia,
});
});
/* console.log(workbook.description); */
// Generar el archivo Excel y guardarlo en el servidor
const fileName = `participantes-${idEvento}.xlsx`;
// Configurar la respuesta HTTP para descargar el archivo
res.setHeader(
'Content-Type',
'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
);
res.setHeader('Content-Disposition', `attachment; filename="${fileName}"`);
// Escribir el archivo Excel en la respuesta HTTP
const buffer = await workbook.xlsx.writeBuffer();
res.send(buffer);
}
getEventosDisponibles() {
const hoy = new Date();
return this.eventoRepository.find({
where: {
fecha_fin: MoreThanOrEqual(hoy),
},
});
}
geteventosDisponiblesMasDosDias(){
const hoy = new Date();
return this.eventoRepository.createQueryBuilder("evento")
.where(`DATE_ADD(evento.fecha_fin, INTERVAL 3 DAY) >= :hoy`, { hoy })
.andWhere('fecha_fin <= :hoy', {hoy})
.getMany();
}
}
@@ -1,9 +1,11 @@
import {
Body,
Controller,
NotFoundException,
Post,
} from '@nestjs/common';
import { InscripcionDscService } from './inscripcion-dsc.service';
import { And } from 'typeorm';
@Controller('inscripcion-dsc')
export class InscripcionDscController {
@@ -12,15 +14,19 @@ export class InscripcionDscController {
@Post('confirmacion')
async confirmacionDsc(@Body() body: any) {
const { accountNumber } = body;
console.log('Entro a la confirmacion')
const userVerification = await this.inscripcionService.userVerification(accountNumber);
/* console.log(userVerification) */
console.log('Usuario verificacion', userVerification);
if(!userVerification){
return {
error: true,
msj: 'El usuario no se encuentra inscrito',
desc: '',
};
throw new NotFoundException('El usuario no se encuentra inscrito');
// return {
// error: true,
// msj: 'El usuario no se encuentra inscrito',
// desc: '',
// };
}
const userInformation = userVerification;
@@ -29,7 +35,7 @@ export class InscripcionDscController {
try{
/* console.log("verificando confirmacion:", userInformation);
*/ if (userInformation[0].confirmacion) {
*/ if (userInformation[0].confirmacion && userInformation[1].confirmacion) {
return {
error: true,
msj: 'El usuario ya hizo la confirmación anteriormente',
+18 -3
View File
@@ -4,31 +4,46 @@ import { createConnection } from 'mysql2/promise';
@Injectable()
export class InscripcionDscService {
async userVerification(accountNumber: string) {
let connection;
try {
const connection = await createConnection({
connection = await createConnection({
host: process.env.DB_HOST_DSC,
user: process.env.DB_USER_DSC,
password: process.env.DB_PASS_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
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)
WHERE A.id_cuenta = "${accountNumber}" and P.activo=true`;
console.log('despues de el select', userInformationQuery)
const qryEjemplo = `SELECT * FROM alumno WHERE id_cuenta="${accountNumber}";`;
const [results] = await connection.query(userInformationQuery);
const resultsArray = JSON.parse(JSON.stringify(results));
if (resultsArray.length === 0) return null;
/* console.log('The solution is: ', resultsArray); */
console.log('The solution is: ', resultsArray);
console.log('Resultados', results)
return results;
} catch (error) {
console.log('Entro al error', error)
throw error;
} finally {
if (connection) {
await connection.end();
}
}
}
async userConfirmation(accountNumber: string): Promise<any> {
try {
@@ -44,7 +59,7 @@ export class InscripcionDscService {
const qryEjemplo = `select platica from alumno_inscrito WHERE id_cuenta="${accountNumber}"`;
const [results] = await connection.query(qryEjemplo);
const [results] = await connection.query(userInformationQuery);
const resultsArray = JSON.parse(JSON.stringify(results));
if (resultsArray.length === 0) return null;
/* console.log('The solution is: ', resultsArray); */
+12 -1
View File
@@ -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,7 +16,16 @@ 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);
const port = AppModule.port;
console.log(`Documentacion disponible en: http://localhost:${port}/api`);
console.log(`Servidor desplegado en: http://localhost:${port}`);
}
bootstrap();
+6 -6
View File
@@ -19,26 +19,26 @@ export class Miembro {
@PrimaryGeneratedColumn()
id_miembro: number;
@Column({ nullable: false, length: 50 })
@Column({ nullable: false, length: 40 })
nombre: string;
@Column({ nullable: false, length: 50 })
@Column({ nullable: false, length: 30 })
apellido_paterno: string;
@Column({ nullable: false, length: 50 })
@Column({ nullable: false, length: 30 })
apellido_materno: string;
@Column({ nullable: false })
id_tipo_miembro: number;
@Column({ nullable: false })
@Column({length: 40, nullable: false })
@IsEmail()
email: string;
@Column({ nullable: false })
@Column({length: 180, nullable: false })
password: string;
@Column({ length: 260, nullable:true})
@Column({ length: 200, nullable:true})
descripcion: string;
@Column({nullable: true})
+11 -2
View File
@@ -1,4 +1,4 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { Controller, Delete, Get, Param, ParseIntPipe, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
import { Miembro } from './entity/miembro.entity';
import { MiembroService } from './miembro.service';
@@ -8,9 +8,18 @@ export class MiembroController {
constructor(private readonly miembroService: MiembroService){}
@UseGuards(JwtAuthGuard)
@Get()
getUsuarios(): Promise<Miembro[]>{
return this.miembroService.getUsuarios()
}
@Get(':tipo')
getMiembros(@Param('tipo', ParseIntPipe)tipo: number): Promise<Miembro[]>{
return this.miembroService.getMiembros(tipo)
}
@Delete(':id')
deleteMiembro(@Param('id', ParseIntPipe)id: number){
return this.miembroService.deleteMiembro(id)
}
}
+12
View File
@@ -20,4 +20,16 @@ export class MiembroService {
},
});
}
getMiembros(tipo: number){
return this.miembroRepository.find({
where:{
id_tipo_miembro: tipo
}
})
}
deleteMiembro(id: number){
return this.miembroRepository.delete({id_miembro: id})
}
}
+10 -2
View File
@@ -1,5 +1,13 @@
import { Controller, Post } from '@nestjs/common';
import { Controller, Get, Post } from '@nestjs/common';
import { ParticipanteService } from './participante.service';
import { Participante } from './participante.entity';
@Controller('participante')
export class ParticipanteController {}
export class ParticipanteController {
constructor(private participanteService: ParticipanteService) {}
@Get()
getParticipantes(): Promise<Participante[]> {
return this.participanteService.getParticipantes();
}
}
+9 -12
View File
@@ -6,34 +6,31 @@ export class Participante {
@PrimaryGeneratedColumn()
id_participante: number;
@Column({ type: String, nullable: false, length: 100 })
@Column({ type: String, nullable: false, length: 40 })
nombre: string;
@Column({ nullable: false })
@Column({ nullable: false, length: 30 })
apellido_paterno: string;
@Column({ nullable: false })
@Column({ nullable: false, length: 30 })
apellido_materno: string;
@Column({ nullable: true })
@Column({ nullable: true, length: 12 })
telefono: string;
@Column({ nullable: false })
@Column({ nullable: false, length: 40 })
email: string;
@Column({ nullable: true }) //
@Column({ nullable: true, length: 180}) //
password: string;
@Column({ nullable: true }) //
@Column({length:20, nullable: true }) //
tipo: string;
@Column({ nullable: true }) //
estado: string;
@Column({ nullable: true })
@Column({ nullable: true, length: 50})
institucion_procedencia: string;
@Column({ nullable: true })
@Column({ nullable: true, length:50})
carrera: string;
@OneToMany(
+4
View File
@@ -31,4 +31,8 @@ export class ParticipanteService {
},
);
}
getParticipantes() {
return this.participanteRepository.find();
}
}
+18
View File
@@ -0,0 +1,18 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TylController } from './tyl.controller';
describe('TylController', () => {
let controller: TylController;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
controllers: [TylController],
}).compile();
controller = module.get<TylController>(TylController);
});
it('should be defined', () => {
expect(controller).toBeDefined();
});
});
+40
View File
@@ -0,0 +1,40 @@
import { Body, Controller, InternalServerErrorException, Post } from '@nestjs/common';
@Controller('tyl')
export class TylController {
@Post('')
async enviarTyl(@Body() body: { email: string }) {
try {
const user = 'acatlan'
const api = 'LW2rtatYP8o2PhCkIqeJV94d6a60cSvTmJXPbRpP';
const params = new URLSearchParams();
params.append('user_name', user!);
params.append('email', body.email);
params.append('api_key', process.env.API_KEY!);
const response = await fetch(
'https://accede.estudio.app/ventas-acatlan',
{
method: 'POST',
// headers: {
// 'Content-Type': 'application/x-www-form-urlencoded',
// },
body: params,
redirect: 'manual',
}
);
console.log('respuesta', response);
const redirectUrl = response.headers.get('location');
console.log('redirect', redirectUrl)
return { redirectUrl };
} catch (error) {
throw new InternalServerErrorException('Error al conectar con API externa');
}
}
}
-16
View File
@@ -1,16 +0,0 @@
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export class Usuario {
@PrimaryGeneratedColumn()
id_usuario: number;
@Column({ nullable: false })
nombre: string;
@Column({ nullable: false })
password: string;
@Column({nullable:false})
email:string
}
-12
View File
@@ -1,12 +0,0 @@
import { Module } from '@nestjs/common';
import { UsuarioService } from './usuario.service';
import { UsuarioController } from './usuarios.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Usuario } from './usuario.entity';
@Module({
imports:[TypeOrmModule.forFeature([Usuario])],
providers: [UsuarioService],
controllers: [UsuarioController]
})
export class UsuarioModule {}
-23
View File
@@ -1,23 +0,0 @@
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Usuario } from './usuario.entity';
@Injectable()
export class UsuarioService {
constructor(
@InjectRepository(Usuario) private usuarioRepository: Repository<Usuario>,
) {}
getUsuarios(){
return this.usuarioRepository.find()
}
getUsuario(email: string) {
return this.usuarioRepository.findOne({
where: {
email,
},
});
}
}
-18
View File
@@ -1,18 +0,0 @@
import { Controller, Get, UseGuards } from '@nestjs/common';
import { ApiBearerAuth } from '@nestjs/swagger';
import { JwtAuthGuard } from 'src/auth/jwt-auth.guard';
import { Usuario } from './usuario.entity';
import { UsuarioService } from './usuario.service';
@ApiBearerAuth()
@Controller('usuarios')
export class UsuarioController {
constructor(private readonly usuarioService: UsuarioService){}
@UseGuards(JwtAuthGuard)
@Get()
getUsuarios(): Promise<Usuario[]>{
return this.usuarioService.getUsuarios()
}
}