Files
Iris_back/src/auth/auth.service.ts
T

341 lines
9.8 KiB
TypeScript
Raw Normal View History

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
}