2025-10-10 10:06:00 -06:00
import {
BadRequestException ,
Injectable ,
NotFoundException ,
UnauthorizedException ,
} from '@nestjs/common' ;
2025-05-20 15:53:57 -06:00
import { JwtService } from '@nestjs/jwt' ;
2025-10-10 10:06:00 -06:00
import { InjectRepository } from '@nestjs/typeorm' ;
import axios from 'axios' ;
2025-05-26 15:29:47 -06:00
import { bcrypt } from 'bcrypt' ;
2025-10-10 10:06:00 -06:00
import { Carrera } from 'src/carrera/entities/carrera.entity' ;
import { gmail } from 'src/helpers.services/gmail.service' ;
import { Servicio } from 'src/servicio/entities/servicio.entity' ;
import { Usuario } from 'src/usuario/entities/usuario.entity' ;
import { Like , Not , Repository } from 'typeorm' ;
2025-05-20 15:53:57 -06:00
@Injectable ()
export class AuthService {
2025-10-10 10:06:00 -06:00
private readonly carreras : Record < string , number > = {
'LIC. EN ACTUARIA' : 64 ,
'LIC. EN ARQUITECTURA' : 70 ,
'LIC. EN CIENCIAS POLITICAS Y ADMON PUB' : 67 ,
'LIC. EN CIENCIAS POLITICAS Y ADMON.PUBL.' : 67 ,
'LIC. EN COMUNICACION' : 67 ,
'LIC. EN DERECHO' : 68 ,
'LIC. EN DERECHO (SUA)' : 67 ,
'LIC. EN DISEÑO GRAFICO' : 70 ,
'LIC. EN ECONOMIA' : 66 ,
'LIC. EN ENSEÑANZA DE INGLES' : 69 ,
'LIC. EN FILOSOFIA' : 70 ,
'LIC. EN HISTORIA' : 70 ,
'LIC. EN INGENIERIA CIVIL' : 70 ,
'LIC. EN LENGUA Y LITERATURA HISPANICAS' : 70 ,
'LIC. EN MAT. APLICADAS Y COMPUTACION' : 66 ,
'LIC. EN MATEMATICAS APLICADAS Y COMP.' : 66 ,
'LIC. EN PEDAGOGÍA' : 70 ,
'LIC. EN PERIODISMO Y COMUNICACION COL.' : 70 ,
'LIC. EN RELACIONES INTERNACIONALES' : 70 ,
'LIC. EN RELACIONES INTERNACIONALES (SUA)' : 70 ,
'LIC. EN SOCIOLOGIA' : 68 ,
'LIC. ENSEÑANZA DE ALEMÁN (LENG. EXTRANJS' : 70 ,
'LIC. ENSEÑANZA DE ESPAÑOL(LENG. EXTRANJ)' : 70 ,
'LIC. ENSEÑANZA DE INGLÉS(LENG. EXTRANJE)' : 70 ,
'LIC. ENSEÑANZA DE ITALIANO(LENG. EXTRANJ' : 70 ,
};
2025-05-20 15:53:57 -06:00
2025-10-10 10:06:00 -06:00
constructor (
private readonly jwtService : JwtService ,
@InjectRepository ( Usuario )
private readonly userRepo : Repository < Usuario >,
@InjectRepository ( Carrera )
private readonly carreraRepo : Repository < Carrera >,
@InjectRepository ( Servicio )
private readonly servicioRepo : Repository < Servicio >,
private readonly gmail : gmail ,
) {}
async jwtVerificar ( token : string ) {
try {
const payload = this . jwtService . verify ( token );
2025-05-26 15:29:47 -06:00
return payload ;
2025-10-10 10:06:00 -06:00
} catch ( err ) {
if ( err . name === 'TokenExpiredError' ) {
throw new UnauthorizedException ( 'El token ha expirado' );
} else if ( err . name === 'JsonWebTokenError' ) {
throw new UnauthorizedException ( 'Token inválido' );
} else {
throw new UnauthorizedException ( 'Error al verificar el token' );
2025-05-26 15:29:47 -06:00
}
2025-10-10 10:06:00 -06:00
}
2025-05-26 15:29:47 -06:00
}
2025-10-10 10:06:00 -06:00
async jwtCreate ( idUsuario : number , idTipoUsuario : number ) {
const payload = { Usuario : idUsuario , tipoUsuario : idTipoUsuario };
return {
2025-05-26 15:29:47 -06:00
access_token : this.jwtService.sign ( payload ),
2025-10-10 10:06:00 -06:00
};
2025-05-26 15:29:47 -06:00
}
2025-10-10 10:06:00 -06:00
async comparar ( password , dbPassword ) {
if ( ! bcrypt . compareSync ( password , dbPassword )) {
return false ;
} else {
2025-05-26 15:29:47 -06:00
return true ;
2025-05-20 15:53:57 -06:00
}
2025-05-26 15:29:47 -06:00
}
2025-05-20 15:53:57 -06:00
2025-10-10 10:06:00 -06:00
async encriptar ( password ) {
return bcrypt . hashSync ( password , Number ( process . env . SALT_ROUNDS ));
2025-05-26 15:29:47 -06:00
}
2025-10-10 10:06:00 -06:00
async generarPassword() {
2025-05-26 15:29:47 -06:00
const length = 8 ;
const charset =
2025-10-10 10:06:00 -06:00
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789' ;
2025-05-26 15:29:47 -06:00
let password = '' ;
2025-10-10 10:06:00 -06:00
for ( let i = 0 , n = charset . length ; i < length ; ++ i ) {
password += charset . charAt ( Math . floor ( Math . random () * n ));
2025-05-20 15:53:57 -06:00
}
2025-05-26 15:29:47 -06:00
return password ;
}
2025-10-10 10:06:00 -06:00
async login ( usuario : string , password : string ) {
const user = await this . userRepo . findOne ({ where : { usuario } });
if ( ! user ) throw new UnauthorizedException ( 'No existe este usuario.' );
const match = await bcrypt . compare ( 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 (
user . idUsuario ,
user . tipoUsuario . idTipoUsuario ,
);
return token ;
}
async escolares ( numeroDeCuenta : string ) {
let response ;
try {
response = await axios . post (
` ${ process . env . ESCOLARES }${ numeroDeCuenta } ` ,
{ password : process.env.ESCOLARES_PASS },
{
headers : {
'Content-Type' : 'application/json' , // Tipo de contenido
Authorization : `Bearer ${ process . env . API_TOKEN } ` , // Header de auth
},
},
);
} catch ( error ) {
throw new UnauthorizedException (
'No se pudo conectar con el servicio de escolares' ,
);
}
if (
! response . data . nombre ||
! response . data . carrconst ||
! response . data . avance
) {
throw new Error (
'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.' ,
);
}
interface AlumnoDTO {
nombre : string ;
creditos : number ;
carrera : string ;
idCarrera? : number ;
}
let alumno : AlumnoDTO = {
nombre : response.data.nombre.trim (),
creditos : response.data.avance ,
carrera : response.data.carrconst.trim (),
};
if ( alumno . creditos < this . carreras [ alumno . carrera ]) {
throw new Error ( 'Este alumno no cuenta con los créditos necesarios.' );
}
while ( alumno . nombre . search ( '' ) != - 1 && alumno . nombre . search ( 'Ã' ) != - 1 ) {
alumno . nombre = alumno . nombre . replace ( 'Ã' , 'Ñ' );
alumno . nombre = alumno . nombre . replace ( '' , '' );
}
let carrera = await this . carreraRepo . findOne ({
where : { carrera : alumno.carrera },
});
if ( ! carrera ) {
let carr = this . carreraRepo . create ({ carrera : alumno.carrera });
carrera = await this . carreraRepo . save ( carr );
}
alumno . idCarrera = carrera . idCarrera ;
let alum = await this . userRepo . findOne ({
where : { usuario : numeroDeCuenta },
});
if ( ! alum ) {
let nuevoUsuario = this . userRepo . create ({
usuario : numeroDeCuenta ,
nombre : alumno.nombre ,
activo : true ,
tipoUsuario : { idTipoUsuario : 3 },
});
alum = await this . userRepo . save ( nuevoUsuario );
}
return { ... alumno , idUsuario : alum.idUsuario };
}
async newPasswordAlumno ( idServicio : number ) {
const password = this . generarPassword ();
// Buscar el servicio junto con el usuario
const servicio = await this . servicioRepo . findOne ({
where : { idServicio },
relations : [ 'usuario' ], // asegura que traiga la relación
});
2025-05-26 15:29:47 -06:00
2025-10-10 10:06:00 -06:00
if ( ! servicio ) {
throw new NotFoundException ( 'No existe este Servicio Social.' );
}
let usuario = servicio . usuario ;
if ( usuario . tipoUsuario . idTipoUsuario !== 3 ) {
throw new BadRequestException ( 'Este usuario no es de tipo alumno.' );
}
2025-05-26 15:29:47 -06:00
2025-10-10 10:06:00 -06:00
// Preparar correo
let correo = preRegistro ( password , usuario . nombre );
// Enviar correo
await this . gmail . sendMail ({
subject : correo.subject ,
to : servicio.correo ,
text : correo.msj ,
});
// Actualizar contraseña en la DB
usuario . password = await this . encriptar ( password );
await this . userRepo . save ( usuario );
return {
message : 'Se envió un correo con una contraseña nueva al alumno.' ,
};
}
2025-05-26 15:29:47 -06:00
2025-10-10 10:06:00 -06:00
async newPasswordResponsable ( idUsuario : number ) {
const password = this . generarPassword ();
2025-05-26 15:29:47 -06:00
2025-10-10 10:06:00 -06:00
// Buscar usuario
const usuario = await this . userRepo . findOne ({ where : { idUsuario } });
if ( ! usuario ) {
throw new NotFoundException ( 'No existe este Usuario.' );
}
if ( usuario . tipoUsuario . idTipoUsuario !== 2 ) {
throw new BadRequestException ( 'Este usuario no es de tipo responsable.' );
}
// Preparar correo
const correo = enviarSec ( password , usuario . usuario , usuario . nombre );
// Enviar correo
await this . gmail . sendMail ({
subject : correo.subject ,
to : usuario.usuario ,
text : correo.msj ,
});
// Actualizar contraseña en la DB
usuario . password = await this . encriptar ( password );
await this . userRepo . save ( usuario );
return {
message : 'Se envió un correo con una contraseña nueva al responsable.' ,
};
}
async findResponsable ( idUsuario : number ) {
return await this . userRepo . findOne ({ where : { idUsuario } });
}
async findResponsables ( pagina : number , nombre : string , correo : string ) {
const [ responsables , count ] = await this . userRepo . findAndCount ({
where : {
usuario : Like ( `% ${ correo } %` ),
nombre : Like ( `% ${ nombre } %` ),
tipoUsuario : { idTipoUsuario : 2 },
},
select : [ 'idUsuario' , 'usuario' , 'nombre' , 'activo' ],
take : 25 ,
skip : 25 * ( pagina - 1 ),
});
return { count : count , responsables : responsables };
}
async actualizarResponsable (
idUsuario : number ,
correo? : string ,
nombre? : string ,
) {
const dataUpdate : Partial < Usuario > = {};
// Buscar responsable
const responsable = await this . userRepo . findOne ({ where : { idUsuario } });
if ( ! responsable ) {
throw new NotFoundException (
'No existe este responsable en la base de datos.' ,
);
}
if ( responsable . tipoUsuario . idTipoUsuario !== 2 ) {
throw new BadRequestException (
'Este usuario no es un responsable de programa.' ,
);
}
if ( correo ) {
const yaUsado = await this . userRepo . findOne ({
where : { usuario : correo , idUsuario : Not ( idUsuario ) },
});
if ( yaUsado ) {
throw new BadRequestException (
'No se puede asignar este correo a esta cuenta porque está siendo usado por otro responsable.' ,
);
}
dataUpdate . usuario = correo ;
}
// Validar nombre si se envía
if ( nombre ) {
dataUpdate . nombre = nombre ;
}
// Verificar que haya algo para actualizar
if ( Object . keys ( dataUpdate ). length === 0 ) {
throw new BadRequestException ( 'No se ha enviado nada para actualizar.' );
}
// Actualizar en la base
await this . userRepo . update ( idUsuario , dataUpdate );
return {
message :
'Se actualizó la información de este responsable de programas correctamente.' ,
};
}
2025-05-20 15:53:57 -06:00
}