127 Commits

Author SHA1 Message Date
evenegas 1abf4d4e9c Cambio de contraseña 2026-02-26 14:38:21 -06:00
evenegas c73121f7df se agregaron funciones para actualizar usuario 2026-02-26 14:22:33 -06:00
evenegas 1a0af95c4e se agregaron cambios 2026-01-29 12:10:46 -06:00
evenegas 3ef076111e se agregaron cambios 2026-01-29 12:09:40 -06:00
evenegas 313bc7e632 se agregaron cambios 2026-01-29 12:05:08 -06:00
evenegas 29b0f2b3e5 se agregó la relacion 2026-01-29 09:59:42 -06:00
evenegas a387f11302 se agregó la relacion 2026-01-29 09:58:13 -06:00
evenegas e336f0bb86 se agregó la relacion 2026-01-29 09:51:48 -06:00
evenegas 017e6836e9 se agregó la relacion 2026-01-29 09:50:55 -06:00
evenegas 1155735d9d log 2026-01-28 12:16:16 -06:00
evenegas e5f98c9596 se agrgó un log para depurar 2026-01-28 12:03:41 -06:00
evenegas 24717cf931 se agrgó un log para depurar 2026-01-28 11:37:36 -06:00
evenegas 02dd974fd6 enviar diferente el caso especial 2026-01-23 10:18:17 -06:00
evenegas b6e7525dc2 enviar diferente el caso especial 2026-01-23 10:17:25 -06:00
evenegas bc975bc295 gitignore 2026-01-22 14:34:27 -06:00
evenegas cb82dd3354 gitignore 2026-01-22 14:17:34 -06:00
evenegas a18fbe441d gitignore 2026-01-22 14:16:44 -06:00
evenegas c42c87e080 gitignore 2026-01-22 14:16:00 -06:00
evenegas 3bc245e1f7 creditos 2026-01-22 13:31:24 -06:00
evenegas 5c5e7b3f0f creditos 2026-01-22 12:12:59 -06:00
evenegas f50ce6d5f9 creditos 2026-01-22 12:10:38 -06:00
evenegas 7933605cb1 changes 2026-01-21 11:32:07 -06:00
evenegas 963ec5a765 changes 2026-01-21 10:53:53 -06:00
evenegas 308f910bee se agregaron cambios 2026-01-20 10:36:56 -06:00
evenegas 29fdbee734 se agregaron cambios 2026-01-20 10:33:30 -06:00
evenegas ea14bfc96c se agregaron cambios 2026-01-20 10:25:20 -06:00
evenegas 9c677fa479 se cambiaron configuraciones 2026-01-16 16:03:32 -06:00
evenegas 77f685e8d3 changes 2026-01-16 15:39:54 -06:00
evenegas 757fa541d8 ya no es necesario eliminar archivo 2026-01-16 15:08:30 -06:00
evenegas 0ee2f86c75 se agregó un console.log 2026-01-16 14:54:00 -06:00
evenegas 9d8c59d5ca se agregó un console.log 2026-01-16 14:46:19 -06:00
evenegas 592acee975 se agregó un console.log 2026-01-16 14:45:50 -06:00
evenegas c33817ace3 se agregó un console.log 2026-01-16 13:52:52 -06:00
evenegas bcb14ddeba se agregó un console.log 2026-01-16 13:31:44 -06:00
evenegas 0cf0c54879 Se actualizan los campos 2026-01-16 10:21:39 -06:00
evenegas eb787c21e6 busca todos los servicios 2026-01-16 10:15:47 -06:00
evenegas 141afc824a se agregó una relación 2026-01-16 09:58:40 -06:00
evenegas 617f4fe5bb se agregó una relación 2026-01-16 09:40:32 -06:00
evenegas ad7fd3b513 se agregó una relación 2026-01-16 09:06:40 -06:00
evenegas 71a24ca189 se cambio la cuenta de acatlan contigo 2026-01-15 17:54:17 -06:00
evenegas ed5b64afb8 se cambio la cuenta de acatlan contigo 2026-01-15 17:44:13 -06:00
evenegas c0178301ae busca las claves con año actual 2026-01-15 12:23:27 -06:00
evenegas f70aa0f92e busca las claves con año actual 2026-01-15 12:13:16 -06:00
evenegas 8762b51240 Se corrigio cuestionario alumno 2026-01-15 10:52:33 -06:00
IO c9cd073101 ya no se mandan correos 2026-01-14 18:14:32 -06:00
IO 688e13b30c se manda de forma correcta el password 2026-01-14 14:58:11 -06:00
IO 086af154cf se manda de forma correcta el password 2026-01-14 14:40:27 -06:00
IO 400bffa254 se restringio la busqueda 2026-01-14 13:19:49 -06:00
Patriots01 61c164225c Carga masiva completa 2026-01-14 19:03:53 +00:00
Patriots01 151d2e8718 Carga masiva completa 2026-01-14 19:02:47 +00:00
Patriots01 62b71060be modificaciones 2026-01-14 18:38:33 +00:00
IO 43abc72a38 se manda el id cuestionario 2026-01-14 12:24:25 -06:00
IO 04ad4923a5 se manda el id cuestionario 2026-01-14 11:49:07 -06:00
IO c40c82e7ba Cambio de id de status en update 2026-01-14 11:41:15 -06:00
IO 050c2b225c se manda el id cuestionario 2026-01-14 10:59:01 -06:00
IO ccd3781152 se agregò relacion en liberar servicio 2026-01-13 19:42:24 -06:00
IO 6a5d39d122 se agregó la funcion de validar preregistro en los cuestionarios 2026-01-13 19:38:57 -06:00
IO 53c53d62a0 se agregó la funcion de validar preregistro en los cuestionarios 2026-01-13 18:15:46 -06:00
IO 53ba7d45ff se agregó la funcion de validar preregistro en los cuestionarios 2026-01-13 18:00:47 -06:00
IO 0ede1e8372 se hizo la funcion diaria 2026-01-13 17:01:42 -06:00
IO 197b18910d se hizo la funcion diaria 2026-01-13 16:13:05 -06:00
IO cafbce392d se hizo la funcion diaria 2026-01-13 16:06:11 -06:00
IO a6efe25aa3 se hizo la funcion diaria 2026-01-13 16:03:39 -06:00
IO 7b6b29b75f se hizo la funcion diaria 2026-01-13 15:05:46 -06:00
IO 672f3579ac se hizo la funcion diaria 2026-01-13 14:55:58 -06:00
IO 786f56442c se hizo la funcion diaria 2026-01-13 14:49:22 -06:00
IO 90d791347e se hizo la funcion diaria 2026-01-13 14:25:01 -06:00
IO 0aa5180154 se hizo la funcion diaria 2026-01-13 14:23:15 -06:00
IO d6f5f645b8 se cambio el mensaje del correo 2026-01-13 13:20:37 -06:00
santiago 115265381b Modificaciones 2026-01-12 21:38:23 -06:00
evenegas a851d31d3b Se corrigio cuestionario alumno 2026-01-12 11:01:25 -06:00
evenegas 29e4b4a7ad Se corrigio cuestionario alumno 2026-01-12 10:34:39 -06:00
santiago cadb15a9cf Corrigi varios errores que se tenian en la parte de cuestionarios asi como en el envio de archivos, y agregue algunos campos en los dto 2026-01-12 10:14:24 -06:00
santiago 83bbcee4ad Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-10 09:36:28 -06:00
santiago 8fac3a823a Modificaciones en algunos endpoint 2026-01-10 09:35:58 -06:00
evenegas b7d22cc07e se creo el endpoint de registro 2026-01-09 13:26:46 -06:00
santiago bba0aa12cc Se modificaron varios endpoint que tenian un mal funcionamiento asi como la forma enq ue se reciben los archivos, falta por corregir un endpoint de cuestionario-alumno2 2026-01-08 13:00:15 -06:00
santiago ff6096f603 Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-08 12:16:54 -06:00
evenegas 53b7d23007 se agrego drive correctamente 2026-01-08 12:14:15 -06:00
evenegas 8f68aadd8a se agrego drive correctamente 2026-01-08 11:33:55 -06:00
evenegas 9386fbc88d se agrego drive correctamente 2026-01-08 11:24:18 -06:00
santiago 7686e26324 Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-08 11:22:12 -06:00
santiago 8f575aa662 MOdificaciones 2026-01-08 11:21:17 -06:00
evenegas 19303178aa se agrego drive correctamente 2026-01-08 11:13:36 -06:00
santiago 7d3a8ee5ed Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-08 09:08:04 -06:00
santiago 3aaf9ba0ed Modificaciones en el endpoint de envio carta de aceptacion 2026-01-08 09:06:12 -06:00
evenegas af28549a04 se agrego drive correctamente 2026-01-08 00:03:26 -06:00
evenegas 50487e4859 se agrego drive correctamente 2026-01-07 14:07:39 -06:00
santiago 6c22cfeeba Merge branch 'develop' of https://repositorio.acatlan.unam.mx/CIDWA/Iris_back into develop 2026-01-07 13:43:37 -06:00
santiago bf2367b366 Modificaciones en algunos endpoint y correcion de erroes 2026-01-07 13:43:12 -06:00
evenegas 0cb91d8b9e se agrego drive correctamente 2026-01-07 13:41:11 -06:00
evenegas 2ed657caf2 se agrego drive correctamente 2026-01-07 12:10:45 -06:00
evenegas 016297760f modificaciones 2026-01-07 01:50:01 -06:00
evenegas 5f7ba5ff69 se importop la parte de los correos 2026-01-06 10:03:14 -06:00
evenegas bc4146a07d Se realizaron correcciones 2026-01-06 07:20:11 -06:00
santiago 5567a26ed6 Se modificaron varias cosas y se agrego un endpoint nuevo para hacer pruebas 2025-12-10 10:59:26 -06:00
santiago 7a260169cc Se modificaron varias cosas en los endpoint y en algunas entities asi como en los dto 2025-12-09 09:03:52 -06:00
Tyrannuss 3e67102878 se agregaro un controller para pruebas , no subir en pruebas 2025-12-03 17:44:07 -06:00
Tyrannuss 1c40eca428 Merge remote-tracking branch 'origin/develop' into develop 2025-11-27 17:33:16 -06:00
Tyrannuss f62a868b97 cambios 2025-11-27 17:32:14 -06:00
Patriots01 65e0773337 Correciones en la forma en que se mandan las cosas y las relaciones 2025-11-27 23:31:50 +00:00
Tyrannuss 671f989976 Merge remote-tracking branch 'origin/develop' into develop 2025-11-27 14:44:37 -06:00
Tyrannuss 71e5ca35f9 . 2025-11-27 14:43:58 -06:00
Patriots01 6c0e263bf4 Modificaiones 2025-11-27 20:28:56 +00:00
Patriots01 aff3a44c09 Se modificaron cosas 2025-11-26 22:01:10 +00:00
Patriots01 083cefe84a Modificaciones, se agregaron las relaciones de tipoUsuario 2025-11-26 21:14:13 +00:00
evenegas 340c0d59fd se agregò endpoint para traer ususarios y funcion diaria 2025-11-26 13:57:34 -06:00
evenegas 1b80de961b se agregò endpoint para traer ususarios y funcion diaria 2025-11-26 13:45:25 -06:00
Patriots01 995a3a4e5e Modificaciones en la logica se agregaron las relaciones para traer los tipoUsuario y que no muestre error 2025-11-25 21:53:52 +00:00
Tyrannuss a9b9b39285 nuevo controller para obtener todos los servicios de admin 2025-11-24 16:52:52 -06:00
Tyrannuss f25b60eccf eliminacion de archivo inecesrio 2025-11-24 13:56:19 -06:00
Tyrannuss 0e6d6be3bb se arreglaron servicio y se agrego un controller para obtener todos los servicios 2025-11-20 18:05:36 -06:00
evenegas 324ab9d35a se cambiò query 2025-11-20 11:00:59 -06:00
Tyrannuss a6ee9decec se cambio el tipo de encriptacion y la manera en que se jala el JWT , ya se puede generar un jwt mediante inicio de sesion 2025-11-19 17:24:39 -06:00
Tyrannuss 75f55ba918 se comentaron unas lineass en servicio controller para hacer pruebas 2025-11-19 15:01:37 -06:00
Tyrannuss e5a455bbb1 eliminacion de archivo .sql 2025-11-10 16:50:51 -06:00
Tyrannuss 7b8514468f correccion de entities en caso-especial , cuestionario-programa, servicio, usuario 2025-11-10 16:49:44 -06:00
evenegas 9d9df020db cambio en algunas tablas 2025-11-06 14:01:53 -06:00
evenegas 4770e95420 se cambio el docker 2025-11-06 11:10:05 -06:00
stern c9c0a531f2 Modificación a script start en package.json para uso de Node 22 2025-10-31 12:22:24 -06:00
evenegas cea20f387f Se cambio algunas entidades 2025-10-31 12:18:28 -06:00
Tyrannuss 373b99a57a Controllers 2025-10-30 14:21:07 -06:00
stern 6a871d8fa1 Modificación a script start del package.json para uso de Node 22 2025-10-30 13:24:06 -06:00
evenegas 09e12161bd Se hicieron cambios en el app module 2025-10-30 11:34:55 -06:00
evenegas b771ca2e57 Se hicieron cambios en el app module 2025-10-30 11:32:38 -06:00
evenegas 4427804865 modificaciones sobre .env 2025-10-22 13:44:11 -06:00
evenegas f06decdc43 se corrigieron los modules 2025-10-22 10:30:23 -06:00
70 changed files with 47142 additions and 2132 deletions
+7 -1
View File
@@ -2,7 +2,8 @@
/dist
/node_modules
/build
/server/uploads
/server
# Logs
logs
*.log
@@ -12,6 +13,8 @@ yarn-debug.log*
yarn-error.log*
lerna-debug.log*
/initdb/init.sql
/initdb2
# OS
.DS_Store
@@ -19,6 +22,9 @@ lerna-debug.log*
/coverage
/.nyc_output
# Ignorar credenciales sensibles
cred.json
# IDEs and editors
/.idea
.project
View File
View File
+11 -26
View File
@@ -1,33 +1,18 @@
version: '3.8'
services:
mysql:
database:
image: mariadb:latest
container_name: Iris_db
restart: always
environment:
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: Iris
MYSQL_USER: user_crud
MYSQL_PASSWORD: root
MARIADB_ROOT_PASSWORD: root
MARIADB_DATABASE: Iris_testDB
MARIADB_USER: root
MARIADB_PASSWORD: root
ports:
- '3306:3306'
volumes:
- ./mysql:/var/lib/mysql
ports:
- "3307:3306"
- mariadb_data:/var/lib/mysql
- ./initdb2:/docker-entrypoint-initdb.dP
nest-app:
build:
context: .
dockerfile: Dockerfile
container_name: Iris_app
restart: always
depends_on:
- mysql
environment:
DATABASE_HOST: mysql
DATABASE_PORT: 3306
DATABASE_USER: user_crud
DATABASE_PASSWORD: root
DATABASE_NAME: Iris
ports:
- "3000:3000"
volumes:
mariadb_data:
+12
View File
@@ -0,0 +1,12 @@
DB_USERNAME=
DB_DATABASE=
DB_PASSWORD=
DB_PORT=
DB_HOST=
PORT=
JWT=
CARPETA=
MAIL_URL=
MAIL_TOKEN=
ESCOLARES=
ESCOLARES_PASS=
File diff suppressed because one or more lines are too long
+924 -1048
View File
File diff suppressed because it is too large Load Diff
+3 -1
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",
@@ -28,6 +28,7 @@
"@nestjs/mapped-types": "*",
"@nestjs/passport": "^11.0.5",
"@nestjs/platform-express": "^11.1.6",
"@nestjs/schedule": "^6.1.0",
"@nestjs/typeorm": "^11.0.0",
"argon2": "^0.44.0",
"axios": "^1.12.2",
@@ -59,6 +60,7 @@
"@swc/core": "^1.13.5",
"@types/express": "^5.0.3",
"@types/jest": "^30.0.0",
"@types/moment": "^2.11.29",
"@types/multer": "^2.0.0",
"@types/node": "^24.5.2",
"@types/supertest": "^6.0.3",
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+9
View File
@@ -0,0 +1,9 @@
numeroCuenta,nombre,licenciatura,correo,fechaInicio,fechaFin,fechaLiberacion,institucion,dependencia,programa,profesor,clave,status
315040357,RAMIREZ AVILA MELANY BETSABE,LIC. EN HISTORIA,424018289@pcpuma.acatlan.unam.mx,29/7/2024,3/12/2025,"",Instituto Nacional De Antropologia E Historia,Museo Nacional De Antropologia,"Investigación, conservación y difusión del patrimonio arqueológico y etnográfico de México","",2024-21/7-1471,Pre-Termino
319304899,ASCENCIO DIAZ ENRIQUE,LIC. EN ACTUARIA,424018289@pcpuma.acatlan.unam.mx,27/10/2024,1/12/2025,"",UNAM,FES ACATLAN,Acatlán Contigo,Act. Jorge Carmona Mendoza,2025-12/145-24,Cancelado
421039812,ARTEAGA GONZALEZ RUBEN MAURICIO,LIC. EN COMUNICACION,424018289@pcpuma.acatlan.unam.mx,29/12/2024,"","",UNAM,FES ACATLAN,Acatlán Contigo,MTRO. SERGIO ARTEMIO GUILLERMO VALENTÍN,2024-12/20-530,Pre-Termino
319302187,PEREZ CENTENO JORGE,LIC. EN CIENCIAS POLITICAS Y ADMON PUB,pcentenojorge15@gmail.com,1/12/2025,8/12/2025,"",UNAM,FES ACATLAN,Acatlán Contigo,Antonio Faustino Torres,2025-12/145-24,Pre-Registro
413076379,JIMENEZ GONZALEZ JAZMIN ALEJANDRA,LIC. EN MATEMATICAS APLICADAS Y COMP.,424018289@pcpuma.acatlan.unam.mx,1/12/2025,4/12/2025,"",UNAM,FES ACATLAN,Acatlán Contigo,xxxx,2025-12/145-24,Carta Aceptación Rechazada
421162888,DREW IBARRA EDUARDO CARLOS,LIC. ENSEÑANZA DE ESPAÑOL(LENG. EXTRANJ),424018289@pcpuma.acatlan.unam.mx,17/12/2025,11/12/2025,"",UNIVERSIDAD NACIONAL AUTONOMA DE MEXICO,ESCUELA DE EXTENSIÓN EN CHICAGO,Enseñanza de Español como Lengua Extranjera,"",2024-12/226-2509,Pre-Termino
419089577,GUTIERREZ GUTIERREZ DIANA ANGELICA,LIC. EN ARQUITECTURA,320154041@pcpuma.acatlan.unam.mx,19/12/2025,18/6/2026,"",UNAM,FES ACATLAN,Titulación por servicio social,LIC. MARCELA MARTÍNEZ RANGEL,2023-12/20-222,Cancelado
422116468,CONTRERAS GONZALEZ ANA CRISTINA,LIC. EN ECONOMIA,424018289@pcpuma.acatlan.unam.mx,28/12/2025,"","",Secretaria De Agricultura Y Desarrollo Rural,Liconsa. S.a De C.v.,SERVICIO SOCIAL LICONSA OFICINA CENTRAL,"",2025-192/1-2689,Pre-Registro
1 numeroCuenta nombre licenciatura correo fechaInicio fechaFin fechaLiberacion institucion dependencia programa profesor clave status
2 315040357 RAMIREZ AVILA MELANY BETSABE LIC. EN HISTORIA 424018289@pcpuma.acatlan.unam.mx 29/7/2024 3/12/2025 Instituto Nacional De Antropologia E Historia Museo Nacional De Antropologia Investigación, conservación y difusión del patrimonio arqueológico y etnográfico de México 2024-21/7-1471 Pre-Termino
3 319304899 ASCENCIO DIAZ ENRIQUE LIC. EN ACTUARIA 424018289@pcpuma.acatlan.unam.mx 27/10/2024 1/12/2025 UNAM FES ACATLAN Acatlán Contigo Act. Jorge Carmona Mendoza 2025-12/145-24 Cancelado
4 421039812 ARTEAGA GONZALEZ RUBEN MAURICIO LIC. EN COMUNICACION 424018289@pcpuma.acatlan.unam.mx 29/12/2024 UNAM FES ACATLAN Acatlán Contigo MTRO. SERGIO ARTEMIO GUILLERMO VALENTÍN 2024-12/20-530 Pre-Termino
5 319302187 PEREZ CENTENO JORGE LIC. EN CIENCIAS POLITICAS Y ADMON PUB pcentenojorge15@gmail.com 1/12/2025 8/12/2025 UNAM FES ACATLAN Acatlán Contigo Antonio Faustino Torres 2025-12/145-24 Pre-Registro
6 413076379 JIMENEZ GONZALEZ JAZMIN ALEJANDRA LIC. EN MATEMATICAS APLICADAS Y COMP. 424018289@pcpuma.acatlan.unam.mx 1/12/2025 4/12/2025 UNAM FES ACATLAN Acatlán Contigo xxxx 2025-12/145-24 Carta Aceptación Rechazada
7 421162888 DREW IBARRA EDUARDO CARLOS LIC. ENSEÑANZA DE ESPAÑOL(LENG. EXTRANJ) 424018289@pcpuma.acatlan.unam.mx 17/12/2025 11/12/2025 UNIVERSIDAD NACIONAL AUTONOMA DE MEXICO ESCUELA DE EXTENSIÓN EN CHICAGO Enseñanza de Español como Lengua Extranjera 2024-12/226-2509 Pre-Termino
8 419089577 GUTIERREZ GUTIERREZ DIANA ANGELICA LIC. EN ARQUITECTURA 320154041@pcpuma.acatlan.unam.mx 19/12/2025 18/6/2026 UNAM FES ACATLAN Titulación por servicio social LIC. MARCELA MARTÍNEZ RANGEL 2023-12/20-222 Cancelado
9 422116468 CONTRERAS GONZALEZ ANA CRISTINA LIC. EN ECONOMIA 424018289@pcpuma.acatlan.unam.mx 28/12/2025 Secretaria De Agricultura Y Desarrollo Rural Liconsa. S.a De C.v. SERVICIO SOCIAL LICONSA OFICINA CENTRAL 2025-192/1-2689 Pre-Registro
+32 -27
View File
@@ -14,51 +14,56 @@ import { ServicioModule } from './servicio/servicio.module';
import { StatusModule } from './status/status.module';
import { TipoUsuarioModule } from './tipo-usuario/tipo-usuario.module';
import { UsuarioModule } from './usuario/usuario.module';
import { AuthModule } from './auth/auth.module';
import { DriveModule } from './drive/drive.module';
import { ScheduleModule } from '@nestjs/schedule';
@Module({
imports: [
ConfigModule.forRoot(
{isGlobal: true}
),
ScheduleModule.forRoot(),
ConfigModule.forRoot({ isGlobal: true }),
TypeOrmModule.forRoot({
type: 'mysql',
host: process.env.db_host,
username: process.env.db_username,
database: process.env.db_database,
password: process.env.db_password,
port: Number(process.env.db_port),
synchronize: true,
dropSchema: false, // elimina la base de datos
// logging: true, // Habilita los logs para depuración
autoLoadEntities: true, // Carga automáticamente las entidades
type: 'mariadb',
host: process.env.DB_HOST,
username: process.env.DB_USERNAME,
database: process.env.DB_DATABASE,
password: process.env.DB_PASSWORD,
port: Number(process.env.DB_PORT),
synchronize: false, //Lo ponemos en false para no afectar
dropSchema: false,
autoLoadEntities: true,
logger: 'advanced-console',
logging: 'all',
}),
CarreraModule,
CasoEspecialModule,
CuestionarioAlumnoModule,
CuestionarioAlumno2Module,
CuestionarioProgramaModule,
CuestionarioPrograma2Module,
ProgramaModule,
ServicioModule,
StatusModule,
AuthModule,
TipoUsuarioModule,
UsuarioModule,
DriveModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
export class AppModule { }
+12 -1
View File
@@ -10,7 +10,7 @@ import { AuthService } from './auth.service';
@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}
constructor(private readonly authService: AuthService) { }
// ===================== LOGIN =====================
@Post('login')
@@ -36,4 +36,15 @@ export class AuthController {
throw new UnauthorizedException(error.message);
}
}
// ===================== ACTUALIZAR CONTRASEÑA ADMIN =====================
@Post('update-password')
async updatePassword() {
try {
const newPassword = await this.authService.update_password();
return { message: 'Contraseña actualizada correctamente.', contraseña: newPassword };
} catch (error) {
throw new UnauthorizedException(error.message);
}
}
}
+14 -5
View File
@@ -1,18 +1,27 @@
import { JwtModule } from '@nestjs/jwt';
import { JwtModule, JwtService } from '@nestjs/jwt';
import { Module } from '@nestjs/common';
import { AuthService } from './auth.service';
import { Usuario } from 'src/usuario/entities/usuario.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AuthController } from './auth.controller';
import { JwtStrategy } from './jwt.strategy';
import { ConfigModule, ConfigService } from '@nestjs/config';
@Module({
imports: [
ConfigModule.forRoot(),
TypeOrmModule.forFeature([Usuario]),
JwtModule.register({
secret: process.env.JWT, // Usa un .env en producción
signOptions: { expiresIn: '1h' },
JwtModule.registerAsync({
imports: [ConfigModule],
useFactory: async (configService: ConfigService) => ({
secret: configService.get('JWT') || 'clave_temporal_desarrollo',
signOptions: { expiresIn: '1h' },
}),
inject: [ConfigService],
}),
],
providers: [AuthService],
controllers: [AuthController],
providers: [AuthService, JwtStrategy],
exports: [AuthService],
})
export class AuthModule {}
+42 -8
View File
@@ -7,7 +7,6 @@ import {
import { JwtService } from '@nestjs/jwt';
import { InjectRepository } from '@nestjs/typeorm';
import axios from 'axios';
import { bcrypt } from 'bcrypt';
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { gmail } from 'src/helpers.services/gmail.service';
@@ -15,6 +14,7 @@ import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
import { Like, Not, Repository } from 'typeorm';
import * as argon2 from 'argon2';
import * as bcrypt from 'bcrypt';
@Injectable()
export class AuthService {
@@ -23,7 +23,7 @@ export class AuthService {
@InjectRepository(Usuario)
private readonly userRepo: Repository<Usuario>,
) {}
) { }
async jwtVerificar(token: string) {
try {
@@ -48,7 +48,7 @@ export class AuthService {
}
async comparar(password, dbPassword) {
if (await argon2.verify(password, dbPassword)) {
if (await bcrypt.compare(password, dbPassword)) {
return true;
} else {
return false;
@@ -56,7 +56,8 @@ export class AuthService {
}
async encriptar(password) {
return await argon2.hash(password);
const saltRounds = 10;
return await bcrypt.hash(password, saltRounds);
}
async generarPassword() {
@@ -72,16 +73,49 @@ export class AuthService {
return password;
}
async update_password() {
let user = await this.userRepo.findOne({ where: { tipoUsuario: { idTipoUsuario: 1 } } })
if (!user) throw new NotFoundException('No se encontró el usuario administrador.');
const newPassword = await this.generarPassword();
const hashedPassword = await this.encriptar(newPassword);
user.password = hashedPassword;
await this.userRepo.save(user);
return newPassword;
}
async login(usuario: string, password: string) {
const user = await this.userRepo.findOne({ where: { usuario } });
const user = await this.userRepo.findOne({
where: { usuario },
relations: ['tipoUsuario'],
});
if (!user) throw new UnauthorizedException('No existe este usuario.');
const match = await this.comparar(password, user.password);
if (!match) throw new UnauthorizedException('Credenciales inválidas');
if (!user.activo) throw new Error('Este usuario no esta activo.');
const token = this.jwtCreate(
if (!user.activo) throw new BadRequestException('Este usuario no esta activo.');
const token = await this.jwtCreate(
user.idUsuario,
user.tipoUsuario.idTipoUsuario,
);
return token;
return {
token: token.access_token,
Usuario: {
idUsuario: user.idUsuario,
usuario: user.usuario,
nombre: user.nombre || '',
TipoUsuario: {
idTipoUsuario: user.tipoUsuario.idTipoUsuario,
tipoUsuario: user.tipoUsuario.tipoUsuario || '',
},
},
};
}
}
+7 -5
View File
@@ -1,19 +1,21 @@
import { Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { ExtractJwt, Strategy } from 'passport-jwt';
import { ConfigService } from '@nestjs/config';
@Injectable()
export class JwtStrategy extends PassportStrategy(Strategy) {
constructor() {
constructor(private configService: ConfigService) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: process.env.JWT_SECRET,
secretOrKey: configService.get('JWT') || 'clave_temporal_desarrollo',
});
}
async validate(payload: any) {
// payload contiene los datos del usuario que se pusieron al generar el token
// ejemplo: { sub: 123, email: "user@example.com" }
return { userId: payload.sub, email: payload.email };
return {
userId: payload.Usuario,
tipoUsuario: payload.tipoUsuario,
};
}
}
-42
View File
@@ -1,42 +0,0 @@
import {
Controller,
Get,
Post,
Body,
Patch,
Param,
Delete,
} from '@nestjs/common';
import { CarreraService } from './carrera.service';
import { CreateCarreraDto } from './dto/create-carrera.dto';
import { UpdateCarreraDto } from './dto/update-carrera.dto';
@Controller('carrera')
export class CarreraController {
constructor(private readonly carreraService: CarreraService) {}
@Post()
create(@Body() createCarreraDto: CreateCarreraDto) {
return this.carreraService.create(createCarreraDto);
}
@Get()
findAll() {
return this.carreraService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.carreraService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateCarreraDto: UpdateCarreraDto) {
return this.carreraService.update(+id, updateCarreraDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.carreraService.remove(+id);
}
}
+4 -3
View File
@@ -1,8 +1,9 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Carrera } from './entities/carrera.entity';
@Module({
controllers: [],
providers: [],
exports: [],
imports: [TypeOrmModule.forFeature([Carrera])],
exports: [TypeOrmModule],
})
export class CarreraModule {}
+11 -12
View File
@@ -1,19 +1,18 @@
import { CasoEspecial } from "src/caso-especial/entities/caso-especial.entity";
import { Servicio } from "src/servicio/entities/servicio.entity";
import { Column, Entity, ManyToMany, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { CasoEspecial } from 'src/caso-especial/entities/caso-especial.entity';
import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Entity, PrimaryGeneratedColumn, Column, OneToMany } from 'typeorm';
@Entity('carrera')
export class Carrera {
@PrimaryGeneratedColumn()
idCarrera:number;
@PrimaryGeneratedColumn({ name: 'idCarrera' })
idCarrera: number;
@Column({type: 'varchar', length: 50, nullable: false})
carrera:string;
@Column({ type: 'varchar', length: 50 })
carrera: string;
@OneToMany(()=>Servicio, (servicio)=>servicio.carrera)
servicio:Servicio[];
@OneToMany(() => CasoEspecial, (casoEspecial) => casoEspecial.carrera)
casosEspeciales: CasoEspecial[];
@OneToMany(()=>CasoEspecial,(casoEspecial)=>casoEspecial.carrera)
casoEspecial:CasoEspecial[];
@OneToMany(() => Servicio, (servicio) => servicio.carrera)
servicios: Servicio[];
}
+32 -11
View File
@@ -11,6 +11,7 @@ import {
UploadedFile,
BadRequestException,
Put,
Query,
} from '@nestjs/common';
import { CasoEspecialService } from './caso-especial.service';
import { CreateCasoEspecialDto } from './dto/create-caso-especial.dto';
@@ -21,13 +22,18 @@ import { diskStorage } from 'multer';
import { extname } from 'path';
import * as fs from 'fs';
import { FilterCasoEspecialDto } from './dto/filter-caso-especial.dto';
import { DriveService } from 'src/drive/drive.service';
import { plainToInstance } from 'class-transformer';
@Controller('caso-especial')
export class CasoEspecialController {
constructor(private readonly casoEspecialService: CasoEspecialService) {}
constructor(private readonly casoEspecialService: CasoEspecialService,
private drive: DriveService,
) { }
@Post()
@UseGuards(AuthGuard('jwt')) // reemplaza verificaToken
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(
FileInterceptor('archivos', {
storage: diskStorage({
@@ -41,27 +47,42 @@ export class CasoEspecialController {
)
async subirArchivo(
@UploadedFile() file: Express.Multer.File,
@Body('alumno') alumno: CreateCasoEspecialDto,
@Body('alumno') alumno: string, // 👈 STRING
) {
try {
const data = await this.casoEspecialService.create(alumno, file.filename);
const alumnoParsed = plainToInstance(
CreateCasoEspecialDto,
JSON.parse(alumno),
);
return await this.casoEspecialService.create(
alumnoParsed,
file.filename,
);
} catch (err) {
if (file?.filename) {
fs.unlinkSync(`./server/uploads/${file.filename}`);
}
throw new BadRequestException(err.message);
}
}
@Get('carpeta')
async carpeta() {
try {
const data = await this.drive.folder('123456789');
console.log(data);
return {
statusCode: 201,
message: 'Archivo subido con éxito',
statusCode: 200,
data,
};
} catch (err) {
// Si hay error, eliminar archivo subido
if (file && file.filename) {
fs.unlinkSync(`./server/uploads/${file.filename}`);
}
throw new BadRequestException(err.message);
}
}
@Get('servicios_especiales')
@UseGuards(AuthGuard('jwt'))
async getServiciosEspeciales(@Body() dtoServEsp: FilterCasoEspecialDto) {
async getServiciosEspeciales(@Query() dtoServEsp: FilterCasoEspecialDto) {
try {
const data = await this.casoEspecialService.findAllFiltered(dtoServEsp);
return {
+51 -50
View File
@@ -16,6 +16,12 @@ import { DriveService } from 'src/drive/drive.service';
import { Status } from 'src/status/entities/status.entity';
import { FilterCasoEspecialDto } from './dto/filter-caso-especial.dto';
import { arch } from 'os';
import { SendMailDto } from 'src/helpers.services/dto/send-email.dto';
const {
terminoValidado
} = require('./../helpers.services/msjCorreos');
@Injectable()
export class CasoEspecialService {
@@ -31,7 +37,7 @@ export class CasoEspecialService {
private validacion: ValidacionService,
private gmail: gmail,
private drive: DriveService,
) {}
) { }
async create(body: CreateCasoEspecialDto, file: string) {
const {
@@ -51,71 +57,69 @@ export class CasoEspecialService {
motivo = '',
} = body;
const usuario = await this.usuarioRepository.findOneBy({
idUsuario: idUsuario,
const usuario = await this.usuarioRepository.findOne({
where: { idUsuario },
relations: ['tipoUsuario'],
});
if (!usuario)
throw new NotFoundException('Este alumno no existe en la db.');
if (usuario.tipoUsuario.idTipoUsuario !== 3)
throw new BadRequestException('Este usuario no es de tipo Alumno.');
const carrera = await this.carreraRepository.findOneBy({
idCarrera: idCarrera,
});
const carrera = await this.carreraRepository.findOneBy({ idCarrera });
if (!carrera)
throw new NotFoundException('Esta carrera no existe en la db.');
const casoExistente = await this.casoEspecialRepository.findOne({
where: { usuario: usuario },
where: { usuario: { idUsuario } },
});
if (casoExistente)
throw new BadRequestException('Este alumno ya tiene un Caso Especial.');
const path = `./server/uploads/${file}`;
const carpeta = await this.drive.folder(numeroCuenta);
if (!carpeta) {
throw new Error('No se pudo crear la carpeta en Drive.');
}
if (!carpeta) throw new BadRequestException('No se pudo crear la carpeta en Drive.');
const archivoZip = await this.drive.uploadFile(
path,
`archivos.zip`,
'archivos.zip',
'application/zip',
carpeta,
);
if (!archivoZip)
throw new BadRequestException('No se pudo subir el archivo a Drive.');
const status = await this.statusRepository.findOne({
where: { idStatus: idStatus },
});
if (!status) throw new NotFoundException('Este status no existe en la db.');
if (archivoZip === null)
throw new Error('No se pudo subir el archivo a Drive.');
const status = await this.statusRepository.findOneBy({ idStatus });
if (!status)
throw new NotFoundException('Este status no existe en la db.');
const nuevoCaso = this.casoEspecialRepository.create({
correo: correo,
telefono: telefono,
institucion: institucion,
dependencia: dependencia,
motivo: motivo,
direccion: direccion,
fechaInicio: fechaInicio,
fechaFin: fechaFin,
fechaNacimiento: fechaNacimiento,
carpeta: carpeta,
archivoZip: archivoZip,
usuario: usuario,
carrera: carrera,
status: status,
creditos: creditos,
correo,
telefono,
institucion,
dependencia,
motivo,
direccion,
fechaInicio,
fechaFin,
fechaNacimiento,
carpeta,
archivoZip,
usuario,
carrera,
status,
creditos,
});
await this.casoEspecialRepository.save(nuevoCaso);
let caso_especial = await this.casoEspecialRepository.save(nuevoCaso);
console.log('Nuevo Caso Especial creado:', caso_especial);
return {
message: 'Se ha registrado el Caso Especial correctamente.',
};
}
async findAllFiltered(dto: FilterCasoEspecialDto) {
const { pagina = 1, idStatus, nombre = '', numeroCuenta = '' } = dto;
@@ -138,11 +142,11 @@ export class CasoEspecialService {
}
if (nombre) {
query.andWhere('usuario.nombre ILIKE :nombre', { nombre: `%${nombre}%` });
query.andWhere('usuario.nombre LIKE :nombre', { nombre: `%${nombre}%` });
}
if (numeroCuenta) {
query.andWhere('usuario.usuario ILIKE :numeroCuenta', {
query.andWhere('usuario.usuario LIKE :numeroCuenta', {
numeroCuenta: `%${numeroCuenta}%`,
});
}
@@ -164,11 +168,11 @@ export class CasoEspecialService {
const caso = await this.casoEspecialRepository.findOne({
where: { idCasoEspecial },
relations: ['usuario'],
relations: ['usuario', 'status'],
});
if (!caso) {
throw new Error('No existe este Caso Especial.');
throw new BadRequestException('No existe este Caso Especial.');
}
switch (caso.status.idStatus) {
@@ -177,11 +181,10 @@ export class CasoEspecialService {
const correo = terminoValidado(caso.usuario.nombre);
// Actualiza el status según el caso
update = {
idStatus: caso.status.idStatus === 11 ? 13 : 14,
};
caso.status.idStatus = caso.status.idStatus === 11 ? 13 : 14;
await this.casoEspecialRepository.save(caso);
let sendcorreo = {
let sendMessage: SendMailDto = {
to: caso.correo,
subject: correo.subject,
@@ -193,18 +196,16 @@ export class CasoEspecialService {
};
// Envía correo
await this.gmail.enviarCorreo(sendcorreo);
await this.gmail.enviarCorreo(sendMessage);
break;
case 6:
throw new Error('Este Caso Especial ya se encuentra en Liberación.');
throw new BadRequestException('Este Caso Especial ya se encuentra en Liberación.');
default:
throw new Error('Id status no válido.');
throw new BadRequestException('Id status no válido.');
}
// Actualiza en base de datos
await this.casoEspecialRepository.update({ idCasoEspecial }, update);
return {
message:
@@ -223,9 +224,9 @@ export class CasoEspecialService {
relations: ['usuario', 'carrera', 'status'],
});
if (!casoEspecial) {
throw new Error('No existe este Caso Especial.');
throw new BadRequestException('No existe este Caso Especial.');
}
return casoEspecial.createdAt;
return casoEspecial;
}
async updateCasoEspecial(id: number, dto: UpdateCasoEspecialDto) {
@@ -1,3 +1,4 @@
import { Type } from 'class-transformer';
import {
IsEmail,
IsNotEmpty,
@@ -13,19 +14,19 @@ import {
} from 'class-validator';
export class CreateCasoEspecialDto {
@Type(() => Number)
@IsNumber()
idUsuario: number;
@IsNumber()
@Type(() => Number)
@IsInt()
idCarrera: number;
@Type(() => Number)
@IsNumber()
@Length(1, 11)
idStatus: number;
@IsString()
@Length(9, 9)
@Matches(/^\d+$/, { message: 'solo puede contener números' })
numeroCuenta: string;
@@ -14,15 +14,15 @@ export class UpdateCasoEspecialDto {
@IsOptional()
@IsDateString()
fechaInicio?: Date;
fechaInicio?: string;
@IsOptional()
@IsDateString()
fechaFin?: Date;
fechaFin?: string;
@IsOptional()
@IsDateString()
fechaNacimiento?: Date;
fechaNacimiento?: string;
@IsOptional()
@IsString()
@@ -1,63 +1,74 @@
import { Carrera } from "src/carrera/entities/carrera.entity";
import { Status } from "src/status/entities/status.entity";
import { Usuario } from "src/usuario/entities/usuario.entity";
import { Column, Entity, ManyToMany, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import {
Entity,
PrimaryGeneratedColumn,
Column,
ManyToOne,
JoinColumn,
CreateDateColumn,
UpdateDateColumn,
} from 'typeorm';
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Status } from 'src/status/entities/status.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
@Entity('caso_especial')
export class CasoEspecial {
@PrimaryGeneratedColumn()
idCasoEspecial: number;
@PrimaryGeneratedColumn({ name: 'idCasoEspecial' })
idCasoEspecial: number;
@Column({ type: 'varchar', length: 3 })
creditos: string;
@Column({ name: 'creditos', type: 'varchar', length: 3, nullable: false })
creditos: string;
@Column({ type: 'varchar', length: 60 })
correo: string;
@Column({ name: 'correo', type: 'varchar', length: 60, nullable: false })
correo: string;
@Column({ type: 'varchar', length: 15 })
telefono: string;
@Column({ name: 'telefono', type: 'varchar', length: 15, nullable: false })
telefono: string;
@Column({ type: 'varchar', length: 200, nullable: true, default: null })
institucion: string | null;
@Column({ name: 'institucion', type: 'varchar', length: 200, nullable: true })
institucion?: string;
@Column({ type: 'varchar', length: 200, nullable: true, default: null })
dependencia: string | null;
@Column({ name: 'dependencia', type: 'varchar', length: 200, nullable: true })
dependencia?: string;
@Column({ type: 'varchar', length: 1, nullable: true, default: null })
motivo: string | null;
@Column({ name: 'motivo', type: 'varchar', length: 1, nullable: true })
motivo?: string;
@Column({ type: 'varchar', length: 200 })
direccion: string;
@Column({ name: 'direccion', type: 'varchar', length: 200, nullable: false })
direccion: string;
@Column({ type: 'timestamp' })
fechaInicio: Date;
@Column({ name: 'fechaInicio', type: 'date', nullable: false })
fechaInicio: string;
@Column({ type: 'timestamp' })
fechaFin: Date;
@Column({ name: 'fechaFin', type: 'date', nullable: false })
fechaFin: string;
@Column({ type: 'timestamp' })
fechaNacimiento: Date;
@Column({ name: 'fechaNacimiento', type: 'date', nullable: false })
fechaNacimiento: string;
@Column({ type: 'varchar', length: 60 })
carpeta: string;
@Column({ name: 'carpeta', type: 'varchar', length: 60, nullable: false })
carpeta: string;
@Column({ type: 'varchar', length: 60 })
archivoZip: string;
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' })
createdAt: Date;
@Column({ name: 'archivoZip', type: 'varchar', length: 60, nullable: false })
archivoZip: string;
@Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' })
updatedAt: Date;
@CreateDateColumn({ name: 'createdAt', type: 'datetime' })
createdAt: Date;
@ManyToOne(()=>Usuario, (usuario)=>usuario.casoEspecial)
usuario:Usuario;
@UpdateDateColumn({ name: 'updatedAt', type: 'datetime' })
updatedAt: Date;
@ManyToOne(()=>Carrera, (carrera)=>carrera.casoEspecial)
carrera:Carrera;
@ManyToOne(()=>Status, (status)=>status.casoEspecial)
status:Status;
// ===== Relaciones =====
@ManyToOne(() => Usuario, (usuario) => usuario.casoEspecial) //error en el nombre al llamar caso especial (se cambio)
@JoinColumn({ name: 'idUsuario' })
usuario: Usuario;
@ManyToOne(() => Carrera, (carrera) => carrera.casosEspeciales)
@JoinColumn({ name: 'idCarrera' })
carrera: Carrera;
@ManyToOne(() => Status, (status) => status.casoEspecial) //error en el nombre al llamar caso especial (se cambio)
@JoinColumn({ name: 'idStatus' })
status: Status;
}
@@ -1,111 +1,124 @@
import { Servicio } from "src/servicio/entities/servicio.entity";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Servicio } from 'src/servicio/entities/servicio.entity';
import {
Entity,
PrimaryGeneratedColumn,
Column,
CreateDateColumn,
OneToMany,
} from 'typeorm';
@Entity('cuestionario_alumno')
export class CuestionarioAlumno {
@PrimaryGeneratedColumn()
idCuestionarioAlumno: number;
@PrimaryGeneratedColumn({ name: 'idCuestionarioAlumno' })
idCuestionarioAlumno: number;
@Column({ type: 'varchar', length: 1 })
sexo: string;
@Column({ name: 'sexo', type: 'varchar', length: 1, nullable: false })
sexo: string;
@Column({ type: 'varchar', length: 2 })
edad: string;
@Column({ name: 'edad', type: 'varchar', length: 2, nullable: false })
edad: string;
@Column({ type: 'varchar', length: 100 })
servicioMedico: string;
@Column({
name: 'servicioMedico',
type: 'varchar',
length: 100,
nullable: false,
})
servicioMedico: string;
@Column()
p1: boolean;
@Column({ name: 'p1', type: 'tinyint', width: 1, nullable: false })
p1: boolean;
@Column()
p2: boolean;
@Column({ name: 'p2', type: 'tinyint', width: 1, nullable: false })
p2: boolean;
@Column({ type: 'varchar', length: 150 })
p3: string;
@Column({ name: 'p3', type: 'varchar', length: 150, nullable: false })
p3: string;
@Column()
p4: boolean;
@Column({ name: 'p4', type: 'tinyint', width: 1, nullable: false })
p4: boolean;
@Column({ type: 'varchar', length: 7 })
p5: string;
@Column({ name: 'p5', type: 'varchar', length: 7, nullable: false })
p5: string;
@Column()
p6: boolean;
@Column({ name: 'p6', type: 'tinyint', width: 1, nullable: false })
p6: boolean;
@Column()
p7: boolean;
@Column({ name: 'p7', type: 'tinyint', width: 1, nullable: false })
p7: boolean;
@Column({ type: 'varchar', length: 5 })
p8: string;
@Column({ name: 'p8', type: 'varchar', length: 5, nullable: false })
p8: string;
@Column()
p9: boolean;
@Column({ name: 'p9', type: 'tinyint', width: 1, nullable: false })
p9: boolean;
@Column()
p10: boolean;
@Column({ name: 'p10', type: 'tinyint', width: 1, nullable: false })
p10: boolean;
@Column()
p11: boolean;
@Column({ name: 'p11', type: 'tinyint', width: 1, nullable: false })
p11: boolean;
@Column()
p12: boolean;
@Column({ name: 'p12', type: 'tinyint', width: 1, nullable: false })
p12: boolean;
@Column()
p13: boolean;
@Column({ name: 'p13', type: 'tinyint', width: 1, nullable: false })
p13: boolean;
@Column({ type: 'varchar', length: 7 })
p14: string;
@Column({ name: 'p14', type: 'varchar', length: 7, nullable: false })
p14: string;
@Column({ type: 'varchar', length: 7 })
p15: string;
@Column({ name: 'p15', type: 'varchar', length: 7, nullable: false })
p15: string;
@Column({ type: 'varchar', length: 2 })
p16: string;
@Column({ name: 'p16', type: 'varchar', length: 2, nullable: false })
p16: string;
@Column({ type: 'varchar', length: 5 })
p17: string;
@Column({ name: 'p17', type: 'varchar', length: 5, nullable: false })
p17: string;
@Column()
p18: boolean;
@Column({ name: 'p18', type: 'tinyint', width: 1, nullable: false })
p18: boolean;
@Column()
p19: boolean;
@Column({ name: 'p19', type: 'tinyint', width: 1, nullable: false })
p19: boolean;
@Column()
p20: boolean;
@Column({ name: 'p20', type: 'tinyint', width: 1, nullable: false })
p20: boolean;
@Column({ type: 'varchar', length: 9 })
p21: string;
@Column({ name: 'p21', type: 'varchar', length: 9, nullable: false })
p21: string;
@Column({ type: 'varchar', length: 23 })
p22: string;
@Column({ name: 'p22', type: 'varchar', length: 23, nullable: false })
p22: string;
@Column({ type: 'varchar', length: 8 })
p23: string;
@Column({ name: 'p23', type: 'varchar', length: 8, nullable: false })
p23: string;
@Column({ type: 'varchar', length: 8 })
p24: string;
@Column({ name: 'p24', type: 'varchar', length: 8, nullable: false })
p24: string;
@Column()
p25: boolean;
@Column({ name: 'p25', type: 'tinyint', width: 1, nullable: false })
p25: boolean;
@Column()
p26: boolean;
@Column({ name: 'p26', type: 'tinyint', width: 1, nullable: false })
p26: boolean;
@Column({ type: 'varchar', length: 400 })
p27: string;
@Column({ name: 'p27', type: 'varchar', length: 400, nullable: false })
p27: string;
@Column({ type: 'varchar', length: 1 })
p28: string;
@Column({ name: 'p28', type: 'varchar', length: 1, nullable: false })
p28: string;
@Column({ type: 'varchar', length: 1 })
p29: string;
@Column({ name: 'p29', type: 'varchar', length: 1, nullable: false })
p29: string;
@Column({ type: 'varchar', length: 400 })
p30: string;
@Column({ name: 'p30', type: 'varchar', length: 400, nullable: false })
p30: string;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioAlumno2)
servicio:Servicio[];
@CreateDateColumn({ name: 'createdAt', type: 'datetime' })
createdAt: Date;
@OneToMany(() => Servicio, (servicio) => servicio.cuestionarioAlumno2)
servicio: Servicio[];
}
@@ -7,12 +7,15 @@ import {
Param,
Delete,
UseGuards,
Query,
Res,
} from '@nestjs/common';
import { CuestionarioAlumno2Service } from './cuestionario-alumno2.service';
import { CreateCuestionarioAlumno2Dto } from './dto/create-cuestionario-alumno2.dto';
import { AuthGuard } from '@nestjs/passport';
import { findCuestionarioAlumno2Dto } from './dto/find.dto';
import { Response } from 'express';
@Controller('cuestionario-alumno2')
export class CuestionarioAlumno2Controller {
@@ -23,13 +26,18 @@ export class CuestionarioAlumno2Controller {
@Post()
@UseGuards(AuthGuard('jwt'))
create(@Body() createCuestionarioAlumno2Dto: CreateCuestionarioAlumno2Dto) {
console.log('CreateCuestionarioAlumno2Dto recibido:', createCuestionarioAlumno2Dto);
return this.cuestionarioAlumno2Service.create(createCuestionarioAlumno2Dto);
}
@Get()
@UseGuards(AuthGuard('jwt'))
findAll(@Body() findDto: findCuestionarioAlumno2Dto) {
return this.cuestionarioAlumno2Service.get(findDto.version, findDto.anio);
async findAll(@Query() findDto: findCuestionarioAlumno2Dto, @Res() res: Response) {
const path = await this.cuestionarioAlumno2Service.get(findDto.version, findDto.anio);
return res.download(path);
}
// @Get(':id')
@@ -1,4 +1,4 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { CreateCuestionarioAlumno2Dto } from './dto/create-cuestionario-alumno2.dto';
import { InjectRepository } from '@nestjs/typeorm';
import { Servicio } from 'src/servicio/entities/servicio.entity';
@@ -21,7 +21,7 @@ export class CuestionarioAlumno2Service {
private cuestionarioAlumnoRepository: Repository<CuestionarioAlumno>,
private validacionService: ValidacionService,
private archivoService: ArchivoService,
) {}
) { }
async create(createCuestionarioAlumno2Dto: CreateCuestionarioAlumno2Dto) {
let idServicio = createCuestionarioAlumno2Dto.idServicio;
@@ -32,13 +32,14 @@ export class CuestionarioAlumno2Service {
let servicio = await this.servicioRepository.findOne({
where: { idServicio },
relations: ['cuestionarioAlumno2', 'status'],
});
if (!servicio) {
throw new Error('No existe este Servicio Social.');
throw new BadRequestException('No existe este Servicio Social.');
}
if (servicio.cuestionarioAlumno2.idCuestionarioAlumno2) {
throw new Error(
if (servicio.cuestionarioAlumno2) {
throw new BadRequestException(
'Este Servicio Social ya cuenta con cuestionario de alumno',
);
}
@@ -48,7 +49,7 @@ export class CuestionarioAlumno2Service {
console.log('antes de create ques2');
if (messageByStatus.hasOwnProperty(servicio.status.idStatus)) {
throw new Error(messageByStatus[servicio.status.idStatus]);
throw new BadRequestException(messageByStatus[servicio.status.idStatus]);
}
console.log('datos a registrar en la tabla cuestionarioAlumno2', {
@@ -66,44 +67,87 @@ export class CuestionarioAlumno2Service {
{ cuestionarioAlumno2: respuestasRegistradas },
);
await this.validacionService.validarPreTermino(idServicio)
return resX;
}
async get(version: string, anio: string) {
const year = anio;
const path = `server/uploads/${year}_cuestionario_alumno.csv`;
/*
async get(version: string, anio: string) {
const year = anio;
const path = `server/uploads/${year}_cuestionario_alumno.csv`;
let data = [];
console.log(year);
let temp;
if (version == 'v1') {
temp = await this.cuestionarioAlumnoRepository.find({
where: {
idCuestionarioAlumno: Not(IsNull()),
},
});
}
if (version == 'v2') {
temp = await this.cuestionarioAlumno2Repository.find({
where: {
idCuestionarioAlumno2: Not(IsNull()),
},
});
}
if (!temp) {
throw new BadRequestException();
}
data = temp.map((item) => ({ ...item }));
await this.archivoService.eliminarArchivo(path).catch((err) => {
console.log(err);
});
return this.archivoService.crearArchivo(path, convertArrayToCSV(data));
}
*/
async get(version: string, anio: string): Promise<string> {
const path = `server/uploads/${anio}_cuestionario_alumno.csv`;
let data = [];
console.log(year);
let temp;
if (version == 'v1') {
temp = this.cuestionarioAlumnoRepository.find({
if (version === 'v1') {
temp = await this.cuestionarioAlumnoRepository.find({
where: {
idCuestionarioAlumno: Not(IsNull()),
},
});
}
if (version == 'v2') {
temp = this.cuestionarioAlumno2Repository.find({
if (version === 'v2') {
temp = await this.cuestionarioAlumno2Repository.find({
where: {
idCuestionarioAlumno2: Not(IsNull()),
},
});
}
if (!temp) {
throw new Error();
if (!temp || temp.length === 0) {
throw new BadRequestException('No hay datos para exportar');
}
data = temp.map((item) => ({ ...item }));
const data = temp.map((item) => ({ ...item }));
await this.archivoService.eliminarArchivo(path).catch((err) => {
console.log(err);
});
await this.archivoService.eliminarArchivo(path).catch(() => null);
return this.archivoService.crearArchivo(path, convertArrayToCSV(data));
// ✅ Creamos el archivo
await this.archivoService.crearArchivo(
path,
convertArrayToCSV(data),
);
// ✅ SOLO regresamos el path
return path;
}
}
@@ -7,7 +7,7 @@ export class CreateCuestionarioAlumno2Dto {
@IsNumber()
@IsNotEmpty()
idServicio:number
idServicio: number
@IsString()
@@ -134,6 +134,10 @@ export class CreateCuestionarioAlumno2Dto {
@IsNotEmpty()
p12_C_4: string;
@IsString()
@IsNotEmpty()
p12_C_5: string;
@IsString()
@IsNotEmpty()
p12_D_1: string;
@@ -1,145 +1,143 @@
import { Servicio } from "src/servicio/entities/servicio.entity";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('cuestionario_alumno_2')
export class CuestionarioAlumno2 {
@PrimaryGeneratedColumn()
idCuestionarioAlumno2: number;
@Column({ type: 'varchar', length: 100, nullable: false })
@Column({ type: 'text', nullable: true })
p1: string;
@Column({ type: 'varchar', length: 50, nullable: false })
@Column({ type: 'varchar', length: 200, nullable: true })
p2: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 200, nullable: true })
p4: string;
@Column({ type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 500, nullable: true })
p5: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 200, nullable: true })
p6: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 200, nullable: true })
p7: string;
@Column({ type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 500, nullable: true })
p8: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 200, nullable: true })
p9: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p3_1: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p3_2: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p3_3: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p3_4: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p10_1: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p10_2: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p10_3: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p11_1: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p11_2: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p11_3: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p11_4: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_A_1: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_A_2: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_A_3: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_A_4: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_B_1: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_B_2: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_B_3: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_B_4: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_C_1: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_C_2: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_C_3: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p12_C_4: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p12_D_1: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p12_D_2: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p12_D_3: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p12_D_4: string;
@Column({ type: 'varchar', length: 20, nullable: false })
@Column({ type: 'varchar', length: 50, nullable: true })
p12_D_5: string;
@Column({ type: 'varchar', length: 100, nullable: false })
@Column({ type: 'varchar', length: 200, nullable: true })
p13: string;
@Column({ type: 'varchar', length: 100, nullable: false })
@Column({ type: 'varchar', length: 200, nullable: true })
p14: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 200, nullable: true })
p15: string;
@Column({ type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 500, nullable: true })
p16: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p17_1: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p17_2: string;
@Column({ type: 'varchar', length: 5, nullable: false })
@Column({ type: 'varchar', length: 20, nullable: true })
p17_3: string;
@Column({ type: 'varchar', length: 50, nullable: true })
@Column({ type: 'varchar', length: 500, nullable: true })
p18: string;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioAlumno2)
servicio:Servicio[];
@OneToMany(() => Servicio, (servicio) => servicio.cuestionarioAlumno2)
servicio: Servicio[];
}
@@ -1,5 +1,11 @@
import { Servicio } from "src/servicio/entities/servicio.entity";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Servicio } from 'src/servicio/entities/servicio.entity';
import {
Column,
CreateDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('cuestionario_programa')
export class CuestionarioPrograma {
@@ -30,6 +36,11 @@ export class CuestionarioPrograma {
@Column({ type: 'varchar', length: 800, nullable: false })
p7: string;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioPrograma)
servicio:Servicio[];
@CreateDateColumn({ name: 'createdAt', type: 'datetime' })
createdAt: Date;
// faltaba createdAT
@OneToMany(() => Servicio, (servicio) => servicio.cuestionarioPrograma)
servicio: Servicio[];
}
@@ -7,6 +7,7 @@ import { CuestionarioPrograma2 } from './entities/cuestionario-programa2.entity'
import { Servicio } from 'src/servicio/entities/servicio.entity';
import { ValidacionService } from 'src/helpers.services/validacion.service';
import { ArchivoService } from 'src/helpers.services/archivo.service';
import { ServicioService } from 'src/servicio/servicio.service';
@Module({
imports: [
@@ -9,6 +9,7 @@ import { ValidacionService } from 'src/helpers.services/validacion.service';
import { Servicio } from 'src/servicio/entities/servicio.entity';
import { messageByStatus } from 'src/messageByStatus';
import { convertArrayToCSV } from 'convert-array-to-csv';
import { ServicioService } from 'src/servicio/servicio.service';
@Injectable()
export class CuestionarioPrograma2Service {
@@ -21,7 +22,8 @@ export class CuestionarioPrograma2Service {
private servicioRepository: Repository<Servicio>,
private archivoService: ArchivoService,
private validacionService: ValidacionService,
) {}
) { }
async create(cuestionarioPrograma2: CreateCuestionarioPrograma2Dto) {
let idServicio = cuestionarioPrograma2.idServicio;
@@ -31,21 +33,30 @@ export class CuestionarioPrograma2Service {
let servicio = await this.servicioRepository.findOne({
where: { idServicio },
relations: ['cuestionarioPrograma2', 'status'],
});
if (!servicio) {
throw new Error('No existe este Servicio Social.');
throw new BadRequestException('No existe este Servicio Social.');
}
if (servicio.cuestionarioPrograma2.idCuestionarioPrograma2) {
throw new Error(
'Este Servicio Social ya cuenta con cuestionario de alumno',
if (servicio.cuestionarioPrograma2) {
throw new BadRequestException(
'Este Servicio Social ya cuenta con cuestionario de programa',
);
}
// Esta validacion esta incorrecta la cambie por la linea de arriva
// if (servicio.cuestionarioPrograma2.idCuestionarioPrograma2) {
// throw new BadRequestException(
// 'Este Servicio Social ya cuenta con cuestionario de alumno',
// );
// }
console.log('el servicio social si existe', servicio);
console.log('antes de create ques2');
if (messageByStatus.hasOwnProperty(servicio.status.idStatus))
throw new Error(messageByStatus[servicio.status.idStatus]);
throw new BadRequestException(messageByStatus[servicio.status.idStatus]);
console.log('despues de registrar cuestionario', cuestionario);
@@ -61,7 +72,7 @@ export class CuestionarioPrograma2Service {
);
console.log(x);
await this.validacionService.validarPreTermino(idServicio)
return respuestasRegistradas;
}
@@ -1,142 +1,142 @@
import {
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
Length,
} from 'class-validator';
export class CreateCuestionarioPrograma2Dto {
IsNotEmpty,
IsNumber,
IsOptional,
IsString,
Length,
} from 'class-validator';
@IsNumber()
@IsNotEmpty()
idServicio:number
export class CreateCuestionarioPrograma2Dto {
@IsNumber()
@IsNotEmpty()
idServicio: number
// p1, p2, p4, p5, p6
@IsString()
@IsNotEmpty()
@Length(1, 100)
p1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p2: string;
@IsString()
@IsNotEmpty()
@Length(1, 100)
p4: string;
@IsString()
@IsNotEmpty()
@Length(1, 5)
p5: string;
@IsOptional()
@IsString()
@Length(1, 100)
p6?: string;
// Sección p3_A
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_A_1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_A_2: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_A_3: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_A_4: string;
// Sección p3_B
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_B_1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_B_2: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_B_3: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_B_4: string;
// Sección p3_C
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_2: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_3: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_4: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_5: string;
// Sección p3_D
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_2: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_3: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_4: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_5: string;
// Sección p3_E
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_E_1: string;
}
// p1, p2, p4, p5, p6
@IsString()
@IsNotEmpty()
@Length(1, 500)
p1: string;
@IsString()
@IsNotEmpty()
@Length(1, 200)
p2: string;
@IsString()
@IsNotEmpty()
@Length(1, 500)
p4: string;
@IsString()
@IsNotEmpty()
@Length(1, 200)
p5: string;
@IsOptional()
@IsString()
@Length(1, 500)
p6?: string;
// Sección p3_A
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_A_1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_A_2: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_A_3: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_A_4: string;
// Sección p3_B
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_B_1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_B_2: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_B_3: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_B_4: string;
// Sección p3_C
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_2: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_3: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_4: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_C_5: string;
// Sección p3_D
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_1: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_2: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_3: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_4: string;
@IsString()
@IsNotEmpty()
@Length(1, 50)
p3_D_5: string;
// Elimine esta tabla por que no existe en el cuestionario => Verificar si se ocupa en otro cuestionario ?
// Sección p3_E
// @IsString()
// @IsNotEmpty()
// @Length(1, 50)
// p3_E_1: string;
}
@@ -1,90 +1,95 @@
import { Servicio } from "src/servicio/entities/servicio.entity";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Servicio } from 'src/servicio/entities/servicio.entity';
import {
Column,
CreateDateColumn,
Entity,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('cuestionario_programa_2')
export class CuestionarioPrograma2 {
@PrimaryGeneratedColumn()
@PrimaryGeneratedColumn({ name: 'idCuestionarioPrograma2' })
idCuestionarioPrograma2: number;
// Sección p3_A_*
@Column({ type: 'varchar', length: 50, nullable: false })
p3_A_1: string;
@CreateDateColumn({ name: 'createdAt', type: 'datetime', nullable: true })
createdAt?: Date;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_A_2: string;
@Column({ name: 'p3_A_1', type: 'varchar', length: 50, nullable: true })
p3_A_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_A_3: string;
@Column({ name: 'p3_A_2', type: 'varchar', length: 50, nullable: true })
p3_A_2?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_A_4: string;
@Column({ name: 'p3_A_3', type: 'varchar', length: 50, nullable: true })
p3_A_3?: string;
// Sección p3_B_*
@Column({ type: 'varchar', length: 50, nullable: false })
p3_B_1: string;
@Column({ name: 'p3_A_4', type: 'varchar', length: 50, nullable: true })
p3_A_4?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_B_2: string;
@Column({ name: 'p3_B_1', type: 'varchar', length: 50, nullable: true })
p3_B_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_B_3: string;
@Column({ name: 'p3_B_2', type: 'varchar', length: 50, nullable: true })
p3_B_2?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_B_4: string;
@Column({ name: 'p3_B_3', type: 'varchar', length: 50, nullable: true })
p3_B_3?: string;
// Sección p3_C_*
@Column({ type: 'varchar', length: 50, nullable: false })
p3_C_1: string;
@Column({ name: 'p3_B_4', type: 'varchar', length: 50, nullable: true })
p3_B_4?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_C_2: string;
@Column({ name: 'p3_C_1', type: 'varchar', length: 50, nullable: true })
p3_C_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_C_3: string;
@Column({ name: 'p3_C_2', type: 'varchar', length: 50, nullable: true })
p3_C_2?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_C_4: string;
@Column({ name: 'p3_C_3', type: 'varchar', length: 50, nullable: true })
p3_C_3?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_C_5: string;
@Column({ name: 'p3_C_4', type: 'varchar', length: 50, nullable: true })
p3_C_4?: string;
// Sección p3_D_*
@Column({ type: 'varchar', length: 50, nullable: false })
p3_D_1: string;
@Column({ name: 'p3_C_5', type: 'varchar', length: 50, nullable: true })
p3_C_5?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_D_2: string;
@Column({ name: 'p3_D_1', type: 'varchar', length: 50, nullable: true })
p3_D_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_D_3: string;
@Column({ name: 'p3_D_2', type: 'varchar', length: 50, nullable: true })
p3_D_2?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_D_4: string;
@Column({ name: 'p3_D_3', type: 'varchar', length: 50, nullable: true })
p3_D_3?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p3_D_5: string;
@Column({ name: 'p3_D_4', type: 'varchar', length: 50, nullable: true })
p3_D_4?: string;
// Sección p3_E_*
@Column({ type: 'varchar', length: 50, nullable: false })
p3_E_1: string;
@Column({ name: 'p3_D_5', type: 'varchar', length: 50, nullable: true })
p3_D_5?: string;
// p1, p2, p4, p5, p6
@Column({ type: 'varchar', length: 100, nullable: false })
p1: string;
@Column({ name: 'p3_E_1', type: 'varchar', length: 50, nullable: true })
p3_E_1?: string;
@Column({ type: 'varchar', length: 50, nullable: false })
p2: string;
@Column({ name: 'p1', type: 'varchar', length: 500, nullable: true })
p1?: string;
@Column({ type: 'varchar', length: 100, nullable: false })
p4: string;
@Column({ name: 'p2', type: 'varchar', length: 200, nullable: true })
p2?: string;
@Column({ type: 'varchar', length: 5, nullable: false })
p5: string;
@Column({ name: 'p4', type: 'varchar', length: 500, nullable: true })
p4?: string;
@Column({ type: 'varchar', length: 100, nullable: true })
@Column({ name: 'p5', type: 'varchar', length: 200, nullable: true })
p5?: string;
@Column({ name: 'p6', type: 'varchar', length: 500, nullable: true })
p6?: string;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioAlumno2)
servicio:Servicio[];
@Column({ name: 'p7', type: 'varchar', length: 200, nullable: true })
p7?: string;
@OneToMany(() => Servicio, (servicio) => servicio.cuestionarioAlumno2)
servicio: Servicio[];
}
+87
View File
@@ -0,0 +1,87 @@
// src/drive/drive.controller.ts
import {
BadRequestException,
Body,
Controller,
Delete,
Get,
Param,
Post,
Query,
} from '@nestjs/common';
import { DriveService } from './drive.service';
import * as fs from 'fs';
@Controller('drive-test')
export class DriveController {
constructor(private readonly driveService: DriveService) { }
// ✅ Listar archivos/carpetas
@Get('list')
async list(@Query('pageToken') pageToken?: string) {
return this.driveService.list(pageToken || '');
}
// ✅ Crear carpeta dentro de CARPETA
@Post('mkdir')
async createFolder(@Body('name') name: string) {
if (!name) {
throw new BadRequestException('El nombre es obligatorio');
}
const folderId = await this.driveService.mkDir(name);
return { folderId };
}
// ✅ Obtener carpeta (buscar o crear)
@Get('folder/:numeroCuenta')
async getOrCreateFolder(
@Param('numeroCuenta') numeroCuenta: string,
) {
const folderId = await this.driveService.folder(numeroCuenta);
return { folderId };
}
// ✅ Subir archivo (archivo dummy para pruebas)
@Post('upload')
async uploadFile(
@Body('parent') parent: string,
) {
if (!parent) {
throw new BadRequestException('Parent es obligatorio');
}
// Archivo temporal para prueba
const tempPath = './temp-test.txt';
fs.writeFileSync(tempPath, 'Archivo de prueba DriveService');
const fileId = await this.driveService.uploadFile(
tempPath,
'test-drive.txt',
'text/plain',
parent,
);
return { fileId };
}
// ✅ Mover archivo (update)
@Post('update')
async updateFile(
@Body('fileId') fileId: string,
@Body('addParents') addParents: string,
@Body('removeParents') removeParents: string,
) {
if (!fileId || !addParents || !removeParents) {
throw new BadRequestException('Datos incompletos');
}
return this.driveService.update(fileId, addParents, removeParents);
}
// ✅ Eliminar archivo
@Delete('delete/:fileId')
async deleteFile(@Param('fileId') fileId: string) {
await this.driveService.deleteFile(fileId);
return { message: 'Archivo eliminado correctamente' };
}
}
+3 -2
View File
@@ -1,9 +1,10 @@
// src/drive/drive.module.ts
import { Module } from '@nestjs/common';
import { DriveService } from './drive.service';
import { DriveController } from './drive.controller';
@Module({
controllers: [DriveController],
providers: [DriveService],
exports: [DriveService],
})
export class DriveModule {}
export class DriveModule { }
+76 -55
View File
@@ -1,9 +1,11 @@
// src/drive/drive.service.ts
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import {
BadRequestException,
Injectable,
InternalServerErrorException,
} from '@nestjs/common';
import { google } from 'googleapis';
import * as fs from 'fs';
import * as path from 'path';
import { error } from 'console';
@Injectable()
export class DriveService {
@@ -11,30 +13,32 @@ export class DriveService {
private loadCredentials() {
try {
const credenciales = JSON.parse(
fs.readFileSync(path.resolve('./cred.json'), 'utf8'),
const oAuth2Client = new google.auth.OAuth2(
process.env.CLIENT_ID,
process.env.CLIENT_SECRET,
);
credenciales.private_key = credenciales.private_key.replace(/\\n/g, '\n');
return new google.auth.JWT({
email: credenciales.client_email,
key: credenciales.private_key,
scopes: ['https://www.googleapis.com/auth/drive'],
});
oAuth2Client.setCredentials({
refresh_token: process.env.REFRESH_TOKEN,
});
return oAuth2Client;
} catch (err) {
console.error('❌ Error cargando credenciales:', err);
throw new InternalServerErrorException('Error al cargar credenciales');
throw new InternalServerErrorException(
'Error al cargar credenciales de Google Drive',
);
}
}
async list(pageToken = '') {
const authClient = await this.loadCredentials();
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient });
try {
const res = await drive.files.list({
corpora: 'user',
supportsTeamDrives: true,
includeTeamDriveItems: true,
supportsAllDrives: true,
includeItemsFromAllDrives: true,
spaces: 'drive',
pageSize: 1000,
pageToken,
@@ -50,28 +54,36 @@ export class DriveService {
async deleteFile(fileId: string) {
if (fileId === process.env.CARPETA) {
throw new Error('No se puede eliminar este archivo/carpeta.');
throw new BadRequestException(
'No se puede eliminar este archivo/carpeta.',
);
}
const authClient = await this.loadCredentials();
console.log('Eliminando archivo de Drive, ID:', fileId);
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient });
try {
return await drive.files.delete({ fileId });
} catch (err) {
throw new InternalServerErrorException(
'Error al eliminar archivo: ' + err.message,
);
console.error('Error al eliminar archivo de Drive:', err.message);
return 0;
}
}
async uploadFile(filePath: string, name: string, mimeType: string, parent: string) {
const authClient = await this.loadCredentials();
async uploadFile(
filePath: string,
name: string,
mimeType: string,
parent: string,
) {
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient });
const requestBody = {
name,
parents: [parent],
};
const media = {
mimeType,
body: fs.createReadStream(filePath),
@@ -82,9 +94,12 @@ export class DriveService {
requestBody,
media,
fields: 'id',
supportsAllDrives: true,
});
fs.unlinkSync(filePath);
return res.data.id;
console.log('Archivo subido a Drive, ID:', res.data.id);
return res.data.id!;
} catch (err) {
throw new InternalServerErrorException(
'Error al subir archivo: ' + err.message,
@@ -93,36 +108,35 @@ export class DriveService {
}
async mkDir(name: string): Promise<string> {
const authClient = await this.loadCredentials();
if (!name) {
throw new BadRequestException('Nombre de carpeta inválido');
}
if (!process.env.CARPETA) {
throw new InternalServerErrorException('CARPETA no configurada');
}
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient });
if(!name){
throw new Error()
}
if(!process.env.CARPETA){
throw new Error()
}
const requestBody = {
name,
mimeType: 'application/vnd.google-apps.folder',
parents: [process.env.CARPETA],
};
try {
const res = await drive.files.create({
requestBody,
fields: 'id',
supportsAllDrives: true,
});
if(!res.data.id){
throw new Error()
}
if (!res.data.id) {
throw new BadRequestException('No se pudo crear la carpeta');
}
return res.data.id;
} catch (err) {
throw new InternalServerErrorException(
'Error al crear carpeta: ' + err.message,
@@ -131,7 +145,7 @@ export class DriveService {
}
async update(fileId: string, addParents: string, removeParents: string) {
const authClient = await this.loadCredentials();
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient });
try {
@@ -139,6 +153,7 @@ export class DriveService {
fileId,
addParents,
removeParents,
supportsAllDrives: true,
});
return res.data;
} catch (err) {
@@ -148,24 +163,30 @@ export class DriveService {
}
}
async folder(numeroCuenta: string) {
let pageToken = '';
do {
const res = await this.list(pageToken);
if(!res.files){
throw new Error()
async folder(numeroCuenta: string): Promise<string> {
if (!process.env.CARPETA) {
throw new InternalServerErrorException('CARPETA no configurada');
}
const authClient = this.loadCredentials();
const drive = google.drive({ version: 'v3', auth: authClient });
const res = await drive.files.list({
q: `
mimeType='application/vnd.google-apps.folder'
and name='${numeroCuenta}'
and '${process.env.CARPETA}' in parents
and trashed=false
`,
fields: 'files(id, name)',
supportsAllDrives: true,
includeItemsFromAllDrives: true,
});
if (res.data.files?.length) {
return res.data.files[0].id!;
}
for (const file of res.files) {
if (
file.name === numeroCuenta &&
file.mimeType === 'application/vnd.google-apps.folder'
) {
return file.id;
}
}
pageToken = res.nextPageToken || '';
} while (pageToken);
return this.mkDir(numeroCuenta);
}
}
+14 -1
View File
@@ -11,7 +11,19 @@ const preRegistro = (password, nombre) => {
subject: 'Notificación de Pre-Registro.',
msj: `Estimado(a) ${nombre}:
Te informamos que la dependencia en donde decidiste realizar tu servicio social ya te registró en nuestro sistema de servicio social, por lo que para completar el proceso debes ingresar en un periodo máximo de 10 días hábiles a partir de la recepción de este mensaje, a la página https://iris.acatlan.unam.mx/ con tu número de cuenta y contraseña: ${password}, para llenar un formulario con tus datos.
Te informamos que la dependencia en donde decidiste realizar tu servicio social
ya te registró en nuestro sistema de servicio social, por lo que para completar
el proceso deberás:
-Ingresar en un periodo máximo de 10 días hábiles a partir de la recepción de este mensaje,
a la página https://iris.acatlan.unam.mx/
-Tu usuario será tu número de cuenta
-Tu contraseña es: ${password}
-Deberás llenar un formulario con tus datos
En caso de no realizar dicho registro, tu trámite queda inconcluso y no nos hacemos responsables por las horas de servicio social prestadas en cualquier dependencia.
@@ -272,6 +284,7 @@ ${atentamente}
};
};
// Quitamos el module para que podamos exportar normal anteriormente module.export
module.exports = {
preRegistro,
preRegistroRechazadoAlumno,
+58 -37
View File
@@ -1,4 +1,4 @@
import { Injectable } from "@nestjs/common";
import { BadRequestException, Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import moment from "moment";
import { Servicio } from "src/servicio/entities/servicio.entity";
@@ -8,49 +8,49 @@ import validator from "validator";
@Injectable()
export class ValidacionService {
constructor(
@InjectRepository(Servicio)
private servicioRepository:Repository<Servicio>,
){}
constructor(
@InjectRepository(Servicio)
private servicioRepository: Repository<Servicio>,
) { }
caracterEspecial(char) {
caracterEspecial(char) {
const charset = [' ', '.', ',', ':', ';', '?', '¿', '!', '¡', '(', ')', '"', "'", '-', '_', '/', '#', '%', '\n'];
return charset.includes(char);
}
yaExiste(campo, m) {
throw new Error(`Ya se encuentra en uso ${m ? 'el' : 'la'} ${campo}.`);
yaExiste(campo, m) {
throw new BadRequestException(`Ya se encuentra en uso ${m ? 'el' : 'la'} ${campo}.`);
}
noValido(campo, m, razon) {
throw new Error(`${m ? 'El' : 'La'} ${campo} no es valid${m ? 'o' : 'a'}, ${razon}.`);
noValido(campo, m, razon) {
throw new BadRequestException(`${m ? 'El' : 'La'} ${campo} no es valid${m ? 'o' : 'a'}, ${razon}.`);
}
noHay(variable, campo, m) {
if (!variable) throw new Error(`No se mando ${m ? 'el' : 'la'} ${campo}.`);
noHay(variable, campo, m) {
if (!variable) throw new BadRequestException(`No se mando ${m ? 'el' : 'la'} ${campo}.`);
}
validacionBasicaStr(texto, campo, m, length) {
validacionBasicaStr(texto, campo, m, length) {
this.noHay(texto, campo, m);
if (typeof texto !== 'string') this.noValido(campo, m, 'no se mando una string');
if (length && texto.length > length) this.noValido(campo, m, 'tiene más caracteres de lo permitido');
return texto;
}
validarNumeroEntero(numero, campo, m: boolean = true) {
validarNumeroEntero(numero, campo, m: boolean = true) {
this.noHay(numero, campo, m);
if (typeof numero === 'number') numero = numero.toString();
if (!validator.isNumeric(numero, { no_symbols: true })) this.noValido(campo, m, 'no es un número entero válido');
return Number(numero);
}
validarCorreo(correo: any, length?: any, campo: string = 'correo', m: boolean = true) {
validarCorreo(correo: any, length?: any, campo: string = 'correo', m: boolean = true) {
this.validacionBasicaStr(correo, campo, m, length);
if (!validator.isEmail(correo)) this.noValido(campo, m, 'no es un correo válido');
return correo;
}
validarTexto(texto, campo, m, length) {
validarTexto(texto, campo, m, length) {
this.validacionBasicaStr(texto, campo, m, length);
for (const char of texto) {
if (!validator.isAlpha(char, 'es-ES') && char !== ' ' && char !== '.') {
@@ -60,7 +60,7 @@ export class ValidacionService {
return texto;
}
validarAlfanumerico(texto, campo, m, length) {
validarAlfanumerico(texto, campo, m, length) {
this.validacionBasicaStr(texto, campo, m, length);
for (const char of texto) {
if (!this.caracterEspecial(char) && !validator.isAlphanumeric(char, 'es-ES')) {
@@ -70,7 +70,7 @@ export class ValidacionService {
return texto;
}
validarNumeroCuenta(numeroCuenta, campo = 'numero de cuenta', m = true) {
validarNumeroCuenta(numeroCuenta, campo = 'numero de cuenta', m = true) {
this.validacionBasicaStr(numeroCuenta, campo, m, 9);
if (!validator.isNumeric(numeroCuenta, { no_symbols: true })) {
this.noValido(campo, m, 'tiene caracteres que no son números');
@@ -78,14 +78,14 @@ export class ValidacionService {
return numeroCuenta;
}
validarFecha(fecha, campo, m) {
validarFecha(fecha, campo, m) {
const fechaMoment = moment(fecha);
this.noHay(fecha, campo, m);
if (!fechaMoment.isValid()) this.noValido(campo, m, 'no es una fecha válida');
return fechaMoment;
}
validarNumero(numero, campo, m, length, no_symbols = true, makeNumber = false) {
validarNumero(numero, campo, m, length, no_symbols = true, makeNumber = false) {
this.validacionBasicaStr(numero, campo, m, length);
if (!validator.isNumeric(numero, { no_symbols })) {
this.noValido(campo, m, 'tiene caracteres que no son números');
@@ -93,32 +93,53 @@ export class ValidacionService {
return makeNumber ? Number(numero) : numero;
}
validarObjetoVacio(obj) {
validarObjetoVacio(obj) {
return Object.keys(obj).length === 0;
}
async validarPreTermino(idServicio) {
const res = await this.servicioRepository.findOne({ where: { idServicio } });
async validarPreTermino(idServicio: number) {
const res = await this.servicioRepository.findOne({
where: { idServicio },
relations: [
'cuestionarioPrograma',
'cuestionarioPrograma2',
'cuestionarioAlumno',
'cuestionarioAlumno2',
],
});
if (!res) return '';
const tieneCuestionarioPrograma =
!!res.cuestionarioPrograma?.idCuestionarioPrograma ||
!!res.cuestionarioPrograma2?.idCuestionarioPrograma2;
const tieneCuestionarioAlumno =
!!res.cuestionarioAlumno?.idCuestionarioAlumno ||
!!res.cuestionarioAlumno2?.idCuestionarioAlumno2;
const tieneDocumentos =
!!res.cartaTermino && !!res.informeGlobal;
if (tieneCuestionarioPrograma && tieneCuestionarioAlumno && tieneDocumentos) {
await this.servicioRepository.update(idServicio, {
status: { idStatus: 5 },
});
if (
(res?.cuestionarioPrograma.idCuestionarioPrograma || res?.cuestionarioPrograma2.idCuestionarioPrograma2) &&
(res.cuestionarioAlumno.idCuestionarioAlumno || res.cuestionarioAlumno2.idCuestionarioAlumno2) &&
res.cartaTermino &&
res.informeGlobal
) {
await this.servicioRepository.update({ idServicio }, { status: {idStatus:5} });
return 'Este Servicio Social pasó a Término, espera a que COESI autorice tu liberación.';
}
return '';
}
validarCuestionarioAlumno2(body) {
validarCuestionarioAlumno2(body) {
const optionalKeys = ['p5', 'p8', 'p16', 'p18'];
if (Array.isArray(body.p1)) body.p1 = body.p1.join(', ');
for (const [key, value] of Object.entries(body)) {
if (!optionalKeys.includes(key) && (value === null || value === undefined || value === '')) {
throw new Error(`El campo ${key} no puede estar vacío.`);
throw new BadRequestException(`El campo ${key} no puede estar vacío.`);
}
}
@@ -136,20 +157,20 @@ export class ValidacionService {
for (const key of expectedKeys) {
if (!(key in body)) {
throw new Error(`Falta la respuesta para la pregunta ${key}.`);
throw new BadRequestException(`Falta la respuesta para la pregunta ${key}.`);
}
}
return body;
}
validarCuestionarioPrograma2(body) {
validarCuestionarioPrograma2(body) {
const camposOpcionales = ['p6'];
if (Array.isArray(body.p1)) body.p1 = body.p1.join(', ');
for (const [key, value] of Object.entries(body)) {
if (!camposOpcionales.includes(key) && (value === null || value === undefined || value === '')) {
throw new Error(`El campo ${key} no puede estar vacío.`);
throw new BadRequestException(`El campo ${key} no puede estar vacío.`);
}
}
@@ -163,10 +184,10 @@ export class ValidacionService {
for (const key of expectedKeys) {
if (!(key in body)) {
throw new Error(`Falta la respuesta para la pregunta ${key}.`);
throw new BadRequestException(`Falta la respuesta para la pregunta ${key}.`);
}
}
return body;
}
}
+13 -2
View File
@@ -1,12 +1,23 @@
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ValidationPipe } from '@nestjs/common';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useGlobalPipes(
new ValidationPipe({
whitelist: true, // elimina propiedades que no existan en el DTO
transform: true, // permite que @Type() funcione
transformOptions: { enableImplicitConversion: true }, // convierte strings → number/date
}),
);
app.enableCors();
await app.listen(process.env.PORT ?? 3000);
console.log(`Aplicación corriendo en: http://localhost:${process.env.PORT ?? 3000}`);
await app.listen(process.env.APP_PORT ?? 3000);
console.log(
`Aplicación corriendo en: http://localhost:${process.env.APP_PORT ?? 3000}`,
);
}
bootstrap();
+15 -7
View File
@@ -1,6 +1,13 @@
import { Servicio } from "src/servicio/entities/servicio.entity";
import { Usuario } from "src/usuario/entities/usuario.entity";
import { Column, Entity, ManyToOne, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Servicio } from 'src/servicio/entities/servicio.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
} from 'typeorm';
@Entity('programa')
export class Programa {
@@ -25,9 +32,10 @@ export class Programa {
@Column({ type: 'boolean', default: true })
activo: boolean;
@ManyToOne(()=>Usuario,(usuario)=>usuario.programa)
usuario:Usuario;
@ManyToOne(() => Usuario, (usuario) => usuario.programa)
@JoinColumn({ name: 'idUsuario' })
usuario: Usuario;
@OneToMany(()=>Servicio,(servicio)=>servicio.cuestionarioAlumno2)
servicio:Servicio[];
@OneToMany(() => Servicio, (servicio) => servicio.cuestionarioAlumno2)
servicio: Servicio[];
}
+7 -1
View File
@@ -10,6 +10,7 @@ import {
UseInterceptors,
BadRequestException,
Param,
ParseIntPipe,
} from '@nestjs/common';
import { FileInterceptor } from '@nestjs/platform-express';
import { diskStorage } from 'multer';
@@ -70,13 +71,18 @@ export class ProgramaController {
@UseGuards(AuthGuard('jwt'))
async reasignarProgramas(
@Body() email: Email,
@Param('idUsuario') idUsuario: number,
@Param('idUsuario', ParseIntPipe) idUsuario: number,
) {
console.log('BODY:', email);
console.log('PARAM:', idUsuario);
try {
const data = await this.programaService.reasignarProgramas(
idUsuario,
email.correo,
);
return {
statusCode: 200,
message: 'Programas reasignados con éxito',
+19 -1
View File
@@ -1,9 +1,27 @@
import { Module } from '@nestjs/common';
import { ProgramaService } from './programa.service';
import { ProgramaController } from './programa.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Programa } from './entities/programa.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
import { UsuarioModule } from 'src/usuario/usuario.module';
import { ValidacionService } from 'src/helpers.services/validacion.service';
import { AuthService } from 'src/auth/auth.service';
import { gmail } from 'src/helpers.services/gmail.service';
import { AuthModule } from 'src/auth/auth.module';
import { JwtService } from '@nestjs/jwt';
import { Servicio } from 'src/servicio/entities/servicio.entity';
@Module({
imports: [TypeOrmModule.forFeature([Programa, Servicio]), UsuarioModule],
controllers: [ProgramaController],
providers: [ProgramaService],
providers: [
ProgramaService,
AuthService,
ValidacionService,
gmail,
JwtService,
],
exports: [TypeOrmModule],
})
export class ProgramaModule {}
+49 -24
View File
@@ -1,6 +1,6 @@
import { Injectable } from '@nestjs/common';
import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Like, Repository } from 'typeorm';
import * as csv from 'csvtojson';
import * as fs from 'fs';
import * as path from 'path';
@@ -13,6 +13,8 @@ import { ValidacionService } from 'src/helpers.services/validacion.service';
import { AuthService } from 'src/auth/auth.service';
import { gmail } from 'src/helpers.services/gmail.service';
import { SendMailDto } from 'src/helpers.services/dto/send-email.dto';
const { enviarSec } = require('../helpers.services/msjCorreos');
@Injectable()
export class ProgramaService {
@@ -24,7 +26,7 @@ export class ProgramaService {
private validacionService: ValidacionService,
private authService: AuthService,
private gmail: gmail,
) {}
) { }
async cargaMasiva(nombreArchivo: string): Promise<{ message: string }> {
const filePath = path.join('server/uploads', nombreArchivo);
@@ -50,12 +52,14 @@ export class ProgramaService {
try {
this.validacionService.validarCorreo(prog.correo);
} catch {
} catch (err) {
console.log(err)
mensaje += 'Correo inválido. Línea omitida.\n\n';
continue;
}
const acatlan = prog.clave.substr(4, 7) === '-12/20-';
const acatlan = prog.clave.substr(4, 8) === '-12/145-';
console.log("clave:", prog.clave.substr(4, 8))
const activo = Number(prog.clave.substr(0, 4)) === today.year();
let responsable = await this.usuarioRepo.findOne({
@@ -67,11 +71,18 @@ export class ProgramaService {
},
});
//Agregamos esta linea para verificar los logs
console.log("prog:", prog);
if (!responsable) {
const password = await this.authService.encriptar(
this.authService.generarPassword(),
);
const correoInfo = enviarSec(password, prog.correo, prog.nombre);
let contraseña = await this.authService.generarPassword();
const password = await this.authService.encriptar(contraseña);
const correoInfo = enviarSec(contraseña, prog.correo, prog.nombre);
responsable = await this.usuarioRepo.create({
usuario: prog.correo,
@@ -92,10 +103,6 @@ export class ProgramaService {
fecha_recibido: new Date(),
};
if (!process.env.TOKEN_GMAIL) {
throw new Error('No existe el token');
}
try {
responsable = await this.usuarioRepo.save(responsable);
@@ -167,6 +174,15 @@ export class ProgramaService {
mensaje += `Error creando programa: ${err.message}\n`;
}
} else {
await this.programaRepo.update(
{ idPrograma: existente.idPrograma },
{ institucion: prog.institucion, programa: prog.programa }
)
await this.usuarioRepo.update(
{ idUsuario: responsable.idUsuario },
{ nombre: prog.nombre }
)
mensaje += `Programa ACATLÁN ya pertenece a ${responsable.usuario}\n`;
}
}
@@ -189,17 +205,22 @@ export class ProgramaService {
const usuario = await this.usuarioRepo.findOne({
where: { idUsuario },
relations: ['tipoUsuario'],
});
if (!usuario) {
throw new Error('No existe este usuario.');
throw new BadRequestException('No existe este usuario.');
}
if (usuario.tipoUsuario.idTipoUsuario !== 2) {
throw new Error('No es un usuario de tipo responsable');
throw new BadRequestException('No es un usuario de tipo responsable');
}
return this.programaRepo.find({
where: { usuario: { idUsuario } },
const year = new Date().getFullYear().toString();
return await this.programaRepo.find({
where: {
usuario: { idUsuario }, clavePrograma: Like(`${year}%`),
},
});
}
@@ -211,20 +232,23 @@ export class ProgramaService {
const usuario = await this.usuarioRepo.findOne({
where: { idUsuario },
relations: ['tipoUsuario'], // Se agrego la relacion con tipo de usuario
});
if (!usuario) {
throw new Error('No existe este usuario.');
throw new BadRequestException('No existe este usuario.');
}
if (usuario.tipoUsuario.idTipoUsuario !== 2) {
throw new Error('No es un usuario de tipo responsable');
throw new BadRequestException('No es un usuario de tipo responsable');
}
const year = new Date().getFullYear().toString();
return this.programaRepo.find({
return await this.programaRepo.find({
where: {
usuario: { idUsuario },
activo: true,
clavePrograma: Like(`${year}%`)
},
});
}
@@ -240,19 +264,20 @@ export class ProgramaService {
let usuarioNuevo = await this.usuarioRepo.findOne({
where: { idUsuario: idusuario },
relations: ["tipoUsuario"],
});
if (!usuarioNuevo) throw new Error('No existe este usuario.');
if (!usuarioNuevo) throw new BadRequestException('No existe este usuario.');
if (usuarioNuevo.tipoUsuario.idTipoUsuario != 2)
throw new Error('No es un usuario de tipo responsable');
throw new BadRequestException('No es un usuario de tipo responsable');
if (usuarioNuevo.usuario === correoOtroResponsable)
throw new Error('Son el mismo usuario.');
throw new BadRequestException('Son el mismo usuario.');
let viejoResponsable = await this.usuarioRepo.findOne({
where: { usuario: otroResponsable },
});
if (!viejoResponsable) throw new Error('No existe este usuario.');
if (!viejoResponsable) throw new BadRequestException('No existe este usuario.');
await this.programaRepo.update(
{ usuario: viejoResponsable },
+4 -4
View File
@@ -26,11 +26,11 @@ export class CrearServicioDto {
@IsEmail()
correo: string;
@IsDate()
fechaInicio: Date;
@IsDateString()
fechaInicio: string; // Cambiado a string antes tenia Date
@IsDate()
fechaFin: Date;
@IsDateString()
fechaFin: string; // Cambiado a string antes tenia Date
@IsOptional()
@IsString()
+1 -1
View File
@@ -12,7 +12,7 @@ export class RegistroValidadoDto {
idServicio: number;
@IsOptional()
fechaNacimiento?: Date;
fechaNacimiento?: string; // Lo pasamos a string antes date
@IsOptional()
@IsString()
+6 -3
View File
@@ -19,15 +19,18 @@ export class UpdateServicioDto {
@IsOptional()
@IsDateString({}, { message: 'fechaInicio debe ser una fecha válida' })
fechaInicio?: Date;
//@Type(() => Date)
fechaInicio?: string; // Lo pasamos a string antes date
@IsOptional()
@IsDateString({}, { message: 'fechaFin debe ser una fecha válida' })
fechaFin?: Date;
//@Type(() => Date)
fechaFin?: string; // Lo pasamos a string antes date
@IsOptional()
@IsDateString({}, { message: 'fechaNacimiento debe ser una fecha válida' })
fechaNacimiento?: Date;
//@Type(() => Date)
fechaNacimiento?: string; // Lo pasamos a string antes date
@IsOptional()
@IsString({ message: 'direccion debe ser texto' })
+106 -74
View File
@@ -1,88 +1,120 @@
import { Carrera } from "src/carrera/entities/carrera.entity";
import { CuestionarioAlumno } from "src/cuestionario-alumno/entities/cuestionario-alumno.entity";
import { CuestionarioAlumno2 } from "src/cuestionario-alumno2/entities/cuestionario-alumno2.entity";
import { CuestionarioPrograma } from "src/cuestionario-programa/entities/cuestionario-programa.entity";
import { CuestionarioPrograma2 } from "src/cuestionario-programa2/entities/cuestionario-programa2.entity";
import { Programa } from "src/programa/entities/programa.entity";
import { Status } from "src/status/entities/status.entity";
import { Usuario } from "src/usuario/entities/usuario.entity";
import { Column, Entity, ManyToOne, PrimaryGeneratedColumn } from "typeorm";
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { CuestionarioAlumno } from 'src/cuestionario-alumno/entities/cuestionario-alumno.entity';
import { CuestionarioAlumno2 } from 'src/cuestionario-alumno2/entities/cuestionario-alumno2.entity';
import { CuestionarioPrograma } from 'src/cuestionario-programa/entities/cuestionario-programa.entity';
import { CuestionarioPrograma2 } from 'src/cuestionario-programa2/entities/cuestionario-programa2.entity';
import { Programa } from 'src/programa/entities/programa.entity';
import { Status } from 'src/status/entities/status.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
import {
Column,
CreateDateColumn,
Entity,
JoinColumn,
ManyToOne,
PrimaryGeneratedColumn,
UpdateDateColumn,
} from 'typeorm';
@Entity('servicio')
export class Servicio {
@PrimaryGeneratedColumn()
idServicio: number;
@PrimaryGeneratedColumn()
idServicio: number;
@Column({ type: 'varchar', length: 3 })
creditos: string;
@Column({ type: 'varchar', length: 60 })
correo: string;
@Column({ type: 'varchar', length: 15, nullable: true, default: null })
telefono?: string;
@Column({ type: 'varchar', length: 200, nullable: true, default: null })
direccion?: string;
@Column({ type: 'date' })
fechaInicio: Date;
@Column({ type: 'date' })
fechaFin: Date;
@Column({ type: 'date', nullable: true, default: null })
fechaLiberacion?: Date;
@Column({ type: 'date', nullable: true, default: null })
fechaNacimiento?: Date;
@Column({ type: 'varchar', length: 60 })
carpeta: string;
@Column({ type: 'varchar', length: 60 })
cartaAceptacion: string;
@Column({ type: 'varchar', length: 60, nullable: true, default: null })
cartaTermino?: string;
@Column({ type: 'varchar', length: 60, nullable: true, default: null })
informeGlobal?: string;
@Column({ type: 'varchar', length: 250, nullable: true, default: null })
programaInterno?: string;
@Column({ type: 'varchar', length: 50, nullable: true, default: null })
profesor?: string;
@Column({ type: 'boolean', nullable: true, default: null })
vistoBuenoAcatlan?: boolean;
@Column({ type: 'varchar', length: 3 })
creditos: string;
@ManyToOne(()=>Usuario,(usuario)=>usuario.servicio)
usuario:Usuario
@Column({ type: 'varchar', length: 60 })
correo: string;
@ManyToOne(()=>CuestionarioAlumno2,(cuestionarioAlumno2)=>cuestionarioAlumno2.servicio)
cuestionarioAlumno2:CuestionarioAlumno2;
@Column({ type: 'varchar', length: 15, nullable: true, default: null })
telefono?: string;
@ManyToOne(()=>CuestionarioPrograma2,(cuestionarioPrograma2)=>cuestionarioPrograma2.servicio)
cuestionarioPrograma2:CuestionarioPrograma2;
@Column({ type: 'varchar', length: 200, nullable: true, default: null })
direccion?: string;
@ManyToOne(()=>CuestionarioPrograma,(cuestionarioPrograma)=>cuestionarioPrograma.servicio)
cuestionarioPrograma:CuestionarioPrograma;
@ManyToOne(()=>CuestionarioAlumno,(cuestionarioAlumno)=>cuestionarioAlumno.servicio)
cuestionarioAlumno:CuestionarioAlumno;
@Column({ type: 'date' })
fechaInicio: string; // Cambios a tipo string antes tenia date
@ManyToOne(()=>Status,(status)=>status.servicio)
status:Status;
@Column({ type: 'date' })
fechaFin: string; // Cambios a tipo string antes tenia date
@ManyToOne(()=>Programa,(programa)=>programa.servicio)
programa:Programa;
@Column({ type: 'date', nullable: true, default: null })
fechaLiberacion?: string; // Cambios a tipo string antes tenia date
@ManyToOne(()=>Carrera,(carrera)=>carrera.servicio)
carrera:Carrera;
@Column({ type: 'date', nullable: true, default: null })
fechaNacimiento?: string; // Cambios a tipo string antes tenia date
@Column({ type: 'varchar', length: 60 })
carpeta: string;
@Column({ type: 'varchar', length: 60 })
cartaAceptacion: string;
@Column({ type: 'varchar', length: 60, nullable: true, default: null })
cartaTermino?: string;
@Column({ type: 'varchar', length: 60, nullable: true, default: null })
informeGlobal?: string;
@Column({ type: 'varchar', length: 250, nullable: true, default: null })
programaInterno?: string;
@Column({ type: 'varchar', length: 50, nullable: true, default: null })
profesor?: string;
@Column({ type: 'boolean', nullable: true, default: null })
vistoBuenoAcatlan?: boolean;
@CreateDateColumn({ name: 'createdAt', type: 'datetime' })
createdAt: Date;
@UpdateDateColumn({ name: 'updatedAt', type: 'datetime' })
updatedAt: Date;
// faltaba createdAT y updatedAT
@ManyToOne(() => Usuario, (usuario) => usuario.servicio)
@JoinColumn({ name: 'idUsuario' })
usuario: Usuario;
@ManyToOne(
() => CuestionarioAlumno2,
(cuestionarioAlumno2) => cuestionarioAlumno2.servicio,
)
@JoinColumn({ name: 'idCuestionarioAlumno2' })
cuestionarioAlumno2: CuestionarioAlumno2;
@ManyToOne(
() => CuestionarioPrograma2,
(cuestionarioPrograma2) => cuestionarioPrograma2.servicio,
)
@JoinColumn({ name: 'idCuestionarioPrograma2' })
cuestionarioPrograma2: CuestionarioPrograma2;
@ManyToOne(
() => CuestionarioPrograma,
(cuestionarioPrograma) => cuestionarioPrograma.servicio,
)
@JoinColumn({ name: 'idCuestionarioPrograma' })
cuestionarioPrograma: CuestionarioPrograma;
@ManyToOne(
() => CuestionarioAlumno,
(cuestionarioAlumno) => cuestionarioAlumno.servicio,
)
@JoinColumn({ name: 'idCuestionarioAlumno' })
cuestionarioAlumno: CuestionarioAlumno;
@ManyToOne(() => Status, (status) => status.servicio)
@JoinColumn({ name: 'idStatus' })
status: Status;
@ManyToOne(() => Programa, (programa) => programa.servicio)
@JoinColumn({ name: 'idPrograma' })
programa: Programa;
@ManyToOne(() => Carrera, (carrera) => carrera.servicios)
@JoinColumn({ name: 'idCarrera' })
carrera: Carrera;
}
+13
View File
@@ -0,0 +1,13 @@
import { diskStorage } from 'multer';
import { extname } from 'path';
export const multerConfig = {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
const uniqueName =
Date.now() + '-' + Math.round(Math.random() * 1e9);
cb(null, uniqueName + extname(file.originalname));
},
}),
};
+115 -12
View File
@@ -11,6 +11,8 @@ import {
UseInterceptors,
Res,
ParseIntPipe,
Param,
Req,
} from '@nestjs/common';
import {
FileInterceptor,
@@ -20,30 +22,85 @@ import { ServicioService } from './servicio.service';
import { AuthGuard } from '@nestjs/passport';
import { CrearServicioDto } from './dto/create-servicio.dto';
import { Response } from 'express';
import { ServiciosAdminDto } from './dto/servicios-admin.dto';
import { ServiciosResponsableDto } from './dto/servicios-responsable.dto';
import { RegistroValidadoDto } from './dto/registro-validado.dto';
import { multerConfig } from './multer';
import { diskStorage } from 'multer';
@Controller('servicio')
export class ServicioController {
constructor(private readonly servicioService: ServicioService) {}
constructor(private readonly servicioService: ServicioService) { }
// ------------------ POST ------------------
// Garegue un nuevo endpoint para hacer pruebas
@Post('test-upload')
@UseInterceptors(FileInterceptor('file'))
uploadTest(
@UploadedFile() file: Express.Multer.File,
@Req() req: any,
) {
console.log('FILE:', file);
console.log('FILES REQ:', req.file);
console.log('FILES BODY:', req.body);
return { ok: true };
}
@Post('registro-validado')
async registroValidado(
@Body() dto: RegistroValidadoDto,
): Promise<{ message: string }> {
console.log('RegistroValidadoDto recibido:', dto);
return this.servicioService.registroValidado(dto);
}
@Post('nuevo')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaAceptacion'))
@UseInterceptors(FileInterceptor('cartaAceptacion', {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
}),
}),
)
async crearServicio(
@UploadedFile() file: Express.Multer.File,
@Body('alumno') alumnoJson: string,
) {
const dto: CrearServicioDto = JSON.parse(alumnoJson);
console.log('Archivo recibido:', file);
return await this.servicioService.registrarNuevoServicio(dto, file);
}
// ------------------ GET ------------------
@Get('todos')
//@UseGuards(AuthGuard('jwt'))
async obtenerTodosLosServicios() {
return this.servicioService.obtenerTodosLosServicios();
}
@Get('admin')
@UseGuards(AuthGuard('jwt'))
async obtenerAdmin(@Query() query: any) {
return this.servicioService.obtenerDetalleServicio(query.idServicio);
async obtenerServiciosAdmin(@Query() dto: ServiciosAdminDto) {
return await this.servicioService.obtenerServiciosAdmin(dto);
}
@Get('servicios_responsable')
@UseGuards(AuthGuard('jwt'))
async obtenerServiciosResponsable(@Query() dto: ServiciosResponsableDto) {
return await this.servicioService.obtenerServiciosResponsable(dto);
}
@Get('admin/:idServicio')
@UseGuards(AuthGuard('jwt'))
async obtenerAdmin(@Param('idServicio') idServicio: number) {
return this.servicioService.obtenerDetalleServicio(idServicio);
}
@Get('alumno')
@@ -62,10 +119,14 @@ export class ServicioController {
return res.download(path);
}
// Corregi el endpoint tenia llamando a otrea funcion
@Get('reporte')
@UseGuards(AuthGuard('jwt'))
async generarReporte(@Query() query: any, @Res() res: Response) {
const path = await this.servicioService.generarGustavoBazPrada(query.year);
async generarReporte(
@Query('inicio') inicio: string,
@Query('fin') fin: string,
@Res() res: Response) {
const path = await this.servicioService.generarReporte(inicio, fin);
return res.download(path);
}
@@ -78,30 +139,59 @@ export class ServicioController {
}
@Put('carta_aceptacion')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaAceptacion'))
//@UseGuards(AuthGuard('jwt'))
@UseInterceptors(
FileInterceptor('cartaAceptacion', {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
}),
}),
)
async subirCartaAceptacion(
@UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string,
) {
const body = JSON.parse(dataJson);
return this.servicioService.cartaTermino(body.idServicio, file);
console.log('file:', file)
return this.servicioService.cartaTermino(body.idServicio, file.filename);
}
@Put('carta_termino')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('cartaTermino'))
@UseInterceptors(
FileInterceptor('cartaTermino', {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
}),
}),
)
async subirCartaTermino(
@UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string,
) {
const body = JSON.parse(dataJson);
console.log("file", file);
return this.servicioService.subirCartaTermino(body.idServicio, file);
}
@Put('informe_global')
@UseGuards(AuthGuard('jwt'))
@UseInterceptors(FileInterceptor('informeGlobal'))
@UseInterceptors(
FileInterceptor('informeGlobal', {
storage: diskStorage({
destination: './server/uploads',
filename: (req, file, cb) => {
cb(null, Date.now() + '-' + file.originalname);
},
}),
}),
)
async subirInformeGlobal(
@UploadedFile() file: Express.Multer.File,
@Body('data') dataJson: string,
@@ -146,13 +236,26 @@ export class ServicioController {
{ name: 'cartaAceptacion', maxCount: 1 },
{ name: 'cartaTermino', maxCount: 1 },
{ name: 'informeGlobal', maxCount: 1 },
]),
], multerConfig,),
)
async actualizarServicio(
@UploadedFiles() files: Record<string, Express.Multer.File[]>,
@Body('data') dataJson: string,
) {
const data = JSON.parse(dataJson);
return this.servicioService.update(data, files);
}
@Put('registro')
@UseGuards(AuthGuard('jwt'))
async registro(@Body() body: { idServicio: number }) {
try {
return this.servicioService.registro(body.idServicio);
} catch (error) {
throw error;
}
}
}
+18 -2
View File
@@ -8,9 +8,24 @@ import { TypeOrmModule } from '@nestjs/typeorm';
import { Servicio } from './entities/servicio.entity';
import { Usuario } from 'src/usuario/entities/usuario.entity';
import { ArchivoService } from 'src/helpers.services/archivo.service';
import { UsuarioModule } from 'src/usuario/usuario.module';
import { ProgramaModule } from 'src/programa/programa.module';
import { Programa } from 'src/programa/entities/programa.entity';
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Auth } from 'googleapis';
import { AuthService } from 'src/auth/auth.service';
import { AuthModule } from 'src/auth/auth.module';
import { Status } from 'src/status/entities/status.entity';
import { MulterModule } from '@nestjs/platform-express';
@Module({
imports: [TypeOrmModule.forFeature([Servicio, Usuario])],
imports: [
TypeOrmModule.forFeature([Servicio, Usuario, Programa, Carrera, Status]),
AuthModule,
MulterModule.register({
dest: './uploads',
}),
],
controllers: [ServicioController],
providers: [
ServicioService,
@@ -19,5 +34,6 @@ import { ArchivoService } from 'src/helpers.services/archivo.service';
gmail,
ArchivoService,
],
exports: [TypeOrmModule],
})
export class ServicioModule {}
export class ServicioModule { }
File diff suppressed because it is too large Load Diff
+3
View File
@@ -1,8 +1,11 @@
import { Module } from '@nestjs/common';
import { StatusService } from './status.service';
import { StatusController } from './status.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { Status } from './entities/status.entity';
@Module({
imports: [TypeOrmModule.forFeature([Status])],
controllers: [StatusController],
providers: [StatusService],
})
@@ -1,16 +1,14 @@
import { Usuario } from "src/usuario/entities/usuario.entity";
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from "typeorm";
import { Usuario } from 'src/usuario/entities/usuario.entity';
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
@Entity('tipo_usuario')
export class TipoUsuario {
@PrimaryGeneratedColumn()
idTipoUsuario: number;
@PrimaryGeneratedColumn()
idTipoUsuario: number;
@Column({ type: 'varchar', length: 15 })
tipoUsuario: string;
@OneToMany(()=>Usuario, (usuario)=>usuario.tipoUsuario)
usuario:Usuario;
@Column({ type: 'varchar', length: 15 })
tipoUsuario: string;
@OneToMany(() => Usuario, (usuario) => usuario.tipoUsuario)
usuario: Usuario;
}
@@ -1,34 +0,0 @@
import { Controller, Get, Post, Body, Patch, Param, Delete } from '@nestjs/common';
import { TipoUsuarioService } from './tipo-usuario.service';
import { CreateTipoUsuarioDto } from './dto/create-tipo-usuario.dto';
import { UpdateTipoUsuarioDto } from './dto/update-tipo-usuario.dto';
@Controller('tipo-usuario')
export class TipoUsuarioController {
constructor(private readonly tipoUsuarioService: TipoUsuarioService) {}
@Post()
create(@Body() createTipoUsuarioDto: CreateTipoUsuarioDto) {
return this.tipoUsuarioService.create(createTipoUsuarioDto);
}
@Get()
findAll() {
return this.tipoUsuarioService.findAll();
}
@Get(':id')
findOne(@Param('id') id: string) {
return this.tipoUsuarioService.findOne(+id);
}
@Patch(':id')
update(@Param('id') id: string, @Body() updateTipoUsuarioDto: UpdateTipoUsuarioDto) {
return this.tipoUsuarioService.update(+id, updateTipoUsuarioDto);
}
@Delete(':id')
remove(@Param('id') id: string) {
return this.tipoUsuarioService.remove(+id);
}
}
+3
View File
@@ -1,6 +1,9 @@
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { TipoUsuario } from './entities/tipo-usuario.entity';
@Module({
imports: [TypeOrmModule.forFeature([TipoUsuario])],
controllers: [],
providers: [],
})
-1
View File
@@ -9,7 +9,6 @@ import {
} from 'class-validator';
export class CreateUsuarioDto {
@IsEmail()
@IsString()
@MaxLength(100)
usuario: string;
+22 -15
View File
@@ -1,13 +1,20 @@
import { CasoEspecial } from "src/caso-especial/entities/caso-especial.entity";
import { Programa } from "src/programa/entities/programa.entity";
import { Servicio } from "src/servicio/entities/servicio.entity";
import { TipoUsuario } from "src/tipo-usuario/entities/tipo-usuario.entity";
import { Column, Entity, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn, Unique } from "typeorm";
import { CasoEspecial } from 'src/caso-especial/entities/caso-especial.entity';
import { Programa } from 'src/programa/entities/programa.entity';
import { Servicio } from 'src/servicio/entities/servicio.entity';
import { TipoUsuario } from 'src/tipo-usuario/entities/tipo-usuario.entity';
import {
Column,
Entity,
JoinColumn,
ManyToOne,
OneToMany,
PrimaryGeneratedColumn,
Unique,
} from 'typeorm';
@Entity('usuario')
@Unique(['usuario'])
export class Usuario {
@PrimaryGeneratedColumn()
idUsuario: number;
@@ -23,16 +30,16 @@ export class Usuario {
@Column({ type: 'boolean', default: true })
activo: boolean;
@ManyToOne(()=>TipoUsuario,(tipoUsuario)=>tipoUsuario.usuario)
tipoUsuario:TipoUsuario;
@ManyToOne(() => TipoUsuario, (tipoUsuario) => tipoUsuario.usuario)
@JoinColumn({ name: 'idTipoUsuario' }) // Talvez faltaba que se llame idTipoUsuario
tipoUsuario: TipoUsuario;
@OneToMany(()=>CasoEspecial,(casoEspecial)=>casoEspecial.usuario)
casoEspecial:CasoEspecial[];
@OneToMany(() => CasoEspecial, (casoEspecial) => casoEspecial.usuario)
casoEspecial: CasoEspecial[];
@OneToMany(()=>Programa,(programa)=>programa.usuario)
programa:Programa[];
@OneToMany(()=>Servicio,(servicio)=>servicio.usuario)
servicio:Servicio[];
@OneToMany(() => Programa, (programa) => programa.usuario)
programa: Programa[];
@OneToMany(() => Servicio, (servicio) => servicio.usuario)
servicio: Servicio[];
}
+34 -3
View File
@@ -8,6 +8,10 @@ import {
Query,
ParseIntPipe,
UseGuards,
UsePipes,
ValidationPipe,
HttpException,
HttpStatus,
} from '@nestjs/common';
import { UsuarioService } from './usuario.service';
import { CreateUsuarioDto } from './dto/create-usuario.dto';
@@ -16,7 +20,26 @@ import { UpdateUsuarioDto } from './dto/update-usuario.dto';
@Controller('usuario')
export class UsuarioController {
constructor(private readonly usuarioService: UsuarioService) {}
constructor(private readonly usuarioService: UsuarioService) { }
/*Get('todos')
//@UseGuards(AuthGuard('jwt'))
async obtenerTodosLosServicios() {
return this.usuarioService.obtenerTodosLosUsuarios();
}*/ //No subir en produccion
@Post('crearUsuario')
@UsePipes(new ValidationPipe({ transform: true }))
async create(@Body() createUsuarioDto: CreateUsuarioDto) {
try {
return await this.usuarioService.create(createUsuarioDto);
} catch (error) {
throw new HttpException(
error.message || 'Error al crear usuario',
HttpStatus.BAD_REQUEST,
);
}
}
@Post('escolares/:numeroDeCuenta')
async escolares(@Param('numeroDeCuenta') numeroDeCuenta: string) {
@@ -24,7 +47,7 @@ export class UsuarioController {
}
@Post('new-password-alumno/:idServicio')
@UseGuards(AuthGuard('jwt'))
//@UseGuards(AuthGuard('jwt'))
async newPasswordAlumno(
@Param('idServicio', ParseIntPipe) idServicio: number,
) {
@@ -56,7 +79,7 @@ export class UsuarioController {
}
@Patch('responsable/:idUsuario')
@UseGuards(AuthGuard('jwt'))
//seGuards(AuthGuard('jwt'))
async actualizarResponsable(
@Param('idUsuario', ParseIntPipe) idUsuario: number,
@Body() updateUsuarioDto: UpdateUsuarioDto,
@@ -67,4 +90,12 @@ export class UsuarioController {
updateUsuarioDto.nombre,
);
}
@Get('tipos-usuario')
async findTiposUsuario() {
return await this.usuarioService.findAll();
}
}
+18 -1
View File
@@ -1,9 +1,26 @@
import { Module } from '@nestjs/common';
import { UsuarioService } from './usuario.service';
import { UsuarioController } from './usuario.controller';
import { Usuario } from './entities/usuario.entity';
import { TypeOrmModule } from '@nestjs/typeorm';
import { CarreraModule } from 'src/carrera/carrera.module';
import { ServicioModule } from 'src/servicio/servicio.module';
import { gmail } from 'src/helpers.services/gmail.service';
import { Auth } from 'googleapis';
import { AuthModule } from 'src/auth/auth.module';
import { TipoUsuario } from 'src/tipo-usuario/entities/tipo-usuario.entity';
import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Servicio } from 'src/servicio/entities/servicio.entity';
@Module({
imports: [
TypeOrmModule.forFeature([Usuario, TipoUsuario, Carrera, Servicio]),
CarreraModule,
ServicioModule,
AuthModule,
],
controllers: [UsuarioController],
providers: [UsuarioService],
providers: [UsuarioService, gmail],
exports: [TypeOrmModule],
})
export class UsuarioModule {}
+114 -18
View File
@@ -1,5 +1,7 @@
import {
BadRequestException,
HttpException,
HttpStatus,
Injectable,
NotFoundException,
UnauthorizedException,
@@ -14,6 +16,8 @@ import { Carrera } from 'src/carrera/entities/carrera.entity';
import { Servicio } from 'src/servicio/entities/servicio.entity';
import { gmail } from 'src/helpers.services/gmail.service';
import { AuthService } from 'src/auth/auth.service';
import { TipoUsuario } from 'src/tipo-usuario/entities/tipo-usuario.entity';
import * as bcrypt from 'bcrypt';
@Injectable()
export class UsuarioService {
@@ -52,24 +56,48 @@ export class UsuarioService {
private readonly carreraRepo: Repository<Carrera>,
@InjectRepository(Servicio)
private readonly servicioRepo: Repository<Servicio>,
@InjectRepository(TipoUsuario)
private readonly tipoUsurioRepo: Repository<TipoUsuario>,
private readonly gmail: gmail,
private readonly authService: AuthService,
) {}
) { }
async escolares(numeroDeCuenta: string) {
let response;
try {
response = await axios.post(
`${process.env.ESCOLARES}${numeroDeCuenta}`,
{ password: process.env.ESCOLARES_PASS },
if (process.env.MODE == 'pruebas') {
/*
Quitamos la promesa ya que muestra un error
response = new Promise((res) => {
res({
data: {
nombre: 'nombre',
carrconst: 'Carrera',
avance: '80',
},
});
});
*/
{
headers: {
'Content-Type': 'application/json', // Tipo de contenido
Authorization: `Bearer ${process.env.API_TOKEN}`, // Header de auth
return response = {
data: {
nombre: 'nombre',
carrconst: 'Carrera',
avance: '80',
},
},
);
};
} else {
response = await axios.post(
`${process.env.ESCOLARES}${numeroDeCuenta}`,
process.env.ESCOLARES_PASS,
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
},
);
}
} catch (error) {
throw new UnauthorizedException(
'No se pudo conectar con el servicio de escolares',
@@ -81,7 +109,7 @@ export class UsuarioService {
!response.data.carrconst ||
!response.data.avance
) {
throw new Error(
throw new BadRequestException(
'El alumno no cumple con los requisitos para realizar el Servicio Social. Si cree que esto es erróneo comunícate al Departamento de Servicio Social y Bolsa de Trabajo.',
);
}
@@ -99,7 +127,7 @@ export class UsuarioService {
};
if (alumno.creditos < this.carreras[alumno.carrera]) {
throw new Error('Este alumno no cuenta con los créditos necesarios.');
throw new BadRequestException('Este alumno no cuenta con los créditos necesarios.');
}
while (alumno.nombre.search('‘') != -1 && alumno.nombre.search('Ã') != -1) {
alumno.nombre = alumno.nombre.replace('Ã', 'Ñ');
@@ -129,17 +157,17 @@ export class UsuarioService {
});
alum = await this.userRepo.save(nuevoUsuario);
}
console.log("el alumno es:", alumno);
return { ...alumno, idUsuario: alum.idUsuario };
}
async newPasswordAlumno(idServicio: number) {
const password = this.authService.generarPassword();
const password = await this.authService.generarPassword();
// Buscar el servicio junto con el usuario
const servicio = await this.servicioRepo.findOne({
where: { idServicio },
relations: ['usuario'], // asegura que traiga la relación
relations: ['usuario', 'usuario.tipoUsuario'], // asegura que traiga la relación Le agregue la relacion para traer tipoUsuario
});
if (!servicio) {
@@ -153,6 +181,7 @@ export class UsuarioService {
}
// Preparar correo
const { preRegistro } = require('../helpers.services/msjCorreos');
let correo = preRegistro(password, usuario.nombre);
// Enviar correo
@@ -173,10 +202,10 @@ export class UsuarioService {
}
async newPasswordResponsable(idUsuario: number) {
const password = this.authService.generarPassword();
const password = await this.authService.generarPassword();
// Buscar usuario
const usuario = await this.userRepo.findOne({ where: { idUsuario } });
const usuario = await this.userRepo.findOne({ where: { idUsuario }, relations: ['tipoUsuario'] });
if (!usuario) {
throw new NotFoundException('No existe este Usuario.');
@@ -186,7 +215,10 @@ export class UsuarioService {
throw new BadRequestException('Este usuario no es de tipo responsable.');
}
// Preparar correo
const { enviarSec } = require('../helpers.services/msjCorreos');
const correo = enviarSec(password, usuario.usuario, usuario.nombre);
// Enviar correo
@@ -233,7 +265,10 @@ export class UsuarioService {
const dataUpdate: Partial<Usuario> = {};
// Buscar responsable
const responsable = await this.userRepo.findOne({ where: { idUsuario } });
const responsable = await this.userRepo.findOne({
where: { idUsuario },
relations: ['tipoUsuario'],
});
if (!responsable) {
throw new NotFoundException(
'No existe este responsable en la base de datos.',
@@ -249,6 +284,7 @@ export class UsuarioService {
if (correo) {
const yaUsado = await this.userRepo.findOne({
where: { usuario: correo, idUsuario: Not(idUsuario) },
relations: ['tipoUsuario'],
});
if (yaUsado) {
@@ -278,4 +314,64 @@ export class UsuarioService {
'Se actualizó la información de este responsable de programas correctamente.',
};
}
async create(createUsuarioDto: CreateUsuarioDto) {
// Verificar si el usuario ya existe
const usuarioExistente = await this.userRepo.findOne({
where: { usuario: createUsuarioDto.usuario },
});
if (usuarioExistente) {
throw new HttpException('El usuario ya existe', HttpStatus.BAD_REQUEST);
}
// Verificar tipo de usuario
const tipoUsuario = await this.tipoUsurioRepo.findOne({
where: { idTipoUsuario: createUsuarioDto.tipoUsuario },
});
if (!tipoUsuario) {
throw new HttpException(
'Tipo de usuario no válido',
HttpStatus.BAD_REQUEST,
);
}
// Hash password
let passwordHash = null;
if (createUsuarioDto.password) {
const saltRounds = 10;
passwordHash = await bcrypt.hash(createUsuarioDto.password, saltRounds);
}
// Crear usuario
const usuario = this.userRepo.create({
...createUsuarioDto,
password: passwordHash,
tipoUsuario: tipoUsuario,
});
const usuarioGuardado = await this.userRepo.save(usuario);
// Eliminar password del response
const { password, ...result } = usuarioGuardado;
return {
message: 'Usuario creado exitosamente',
usuario: result,
};
}
async findAll() {
return await this.tipoUsurioRepo.find()
}
/*c obtenerTodosLosUsuarios() {
return await this.userRepo.find({
relations: ['tipoUsuario'],
take: 10,
order: {
idUsuario: 'DESC', // Ordenar por el más reciente primero
},
});
}*/ // no subir a produccion
}