File: /var/www/api-management/src/app/user/services/user.service.ts
import {
ConflictException,
Injectable,
InternalServerErrorException,
NotFoundException,
} from '@nestjs/common';
import { Model, Types } from 'mongoose';
import { User, UserDocument } from '../schemas/user.schema';
import { CreateUserDto } from '../dto/create-user.dto';
import { MailService } from '../../mail/service/mail.service';
import { UpdateUserDto } from '../dto/update-user.dto';
import { handleValidationError } from '../../../shared/utils/error-handler';
import {
handleMessageError,
handleMessageSucces,
} from '../../../shared/utils/message-handler';
import { InjectModel } from '@nestjs/mongoose';
import * as bcrypt from 'bcryptjs';
import { Role, RoleDocument } from '../../roles/schemas/role.schema';
import {
Permiso,
PermisoDocument,
} from '../../permisos/schemas/permiso.schema';
@Injectable()
export class UserService {
resetPasswordTokenModel: any;
constructor(
@InjectModel(User.name)
private userModel: Model<UserDocument>,
@InjectModel(Role.name) private readonly roleModel: Model<RoleDocument>,
@InjectModel(Permiso.name)
private readonly permisoModel: Model<PermisoDocument>,
private readonly mailService: MailService,
) {}
async create(createUserDto: CreateUserDto): Promise<any> {
try {
const { email, identificacion, ...rest } = createUserDto;
const existingUser = await this.userModel.findOne({ email }).exec();
if (existingUser) {
throw new ConflictException('El correo ya está registrado.');
}
const hashedPassword = await bcrypt.hash(identificacion, 10);
const createdUser = new this.userModel({
...rest,
password: hashedPassword,
email,
identificacion,
});
const savedUser = await createdUser.save();
const subject = 'Registro exitoso';
const text = `
Hola ${createdUser.nombres}\n
Tu cuenta ha sido creada exitosamente. Aquí están tus credenciales de inicio de sesión:\n
Correo: ${createdUser.email}\n
Contraseña: Es tu número de Cédula\n
Ingresa a tu cuenta en https://web.enelar.com.co/auth/login \n\n
Gracias por registrarte con nosotros.`;
await this.mailService.sendMail(createdUser.email, subject, text);
return handleMessageSucces(savedUser, 'create');
} catch (error) {
if (error instanceof ConflictException) {
throw error;
} else if (error.name === 'ValidationError') {
throw handleValidationError(error);
} else {
throw new InternalServerErrorException();
}
}
}
async findAll(page: number, limit: number): Promise<any> {
try {
const skip = (page - 1) * limit;
const totalRecordsPromise = await this.userModel.countDocuments({
deletedAt: { $in: null },
});
const usersPromise = this.userModel
.find(
{ deletedAt: { $in: null } },
{ password: 0, deletedAt: 0, createdAt: 0, updatedAt: 0, __v: 0 },
)
.skip(skip)
.limit(limit)
.exec();
const [users, totalRecords] = await Promise.all([
usersPromise,
totalRecordsPromise,
]);
return handleMessageSucces(users, 'findAll', totalRecords);
} catch (error) {
throw handleValidationError(error);
}
}
async findOne(id: string): Promise<any> {
try {
const user = await this.userModel
.findById(id, {
password: 0,
deletedAt: 0,
createdAt: 0,
updatedAt: 0,
__v: 0,
})
.exec();
if (!user) {
throw new NotFoundException(handleMessageError('notFound'));
}
return handleMessageSucces(user, 'findOne');
} catch (error) {
throw handleValidationError(error);
}
}
async update(id: string, updateUserDto: UpdateUserDto): Promise<any> {
try {
const updatedUser = await this.userModel.findByIdAndUpdate(
id,
updateUserDto,
{ new: true },
);
if (!updatedUser) {
throw new NotFoundException(handleMessageError('notFound'));
}
return handleMessageSucces(updatedUser, 'update');
} catch (error) {
throw handleValidationError(error);
}
}
async delete(id: string): Promise<any> {
try {
const deletedUser = await this.userModel.findByIdAndUpdate(id, {
deletedAt: new Date(),
});
if (!deletedUser) {
throw new NotFoundException(handleMessageError('notFound'));
}
return handleMessageSucces(deletedUser, 'delete');
} catch (error) {
throw handleValidationError(error);
}
}
async deleteMulti(idUsers: { _id: string }[]): Promise<any> {
try {
const objectIdArray = idUsers.map((user) => new Types.ObjectId(user._id));
const result = await this.userModel.deleteMany({
_id: { $in: objectIdArray },
});
return handleMessageSucces(result, 'deleteMultipleUsers');
} catch (error) {
throw handleValidationError(error);
}
}
async validarPermiso(
userId: string,
permissionSlug: string,
): Promise<boolean> {
// 1. Obtener el usuario por ID y hacer populate de los roles
const user = await this.userModel.findById(userId).populate('roles').exec();
if (!user) {
throw new NotFoundException(handleMessageError('notFound'));
}
// 2. Obtener los IDs de los permisos desde los roles del usuario
const roleIds = user.roles.map((role) => role._id);
const roleDocs = await this.roleModel
.find({ _id: { $in: roleIds } })
.exec();
// 3. Obtener todos los IDs de permisos de los roles
const permissionIds = roleDocs.flatMap((role) => role.permisos);
// 4. Obtener todos los permisos correspondientes a los IDs
const permisos = await this.permisoModel
.find({ _id: { $in: permissionIds } })
.exec();
// 5. Verificar si alguno de los permisos tiene el slug proporcionado
const hasPermission = permisos.some(
(permission) => permission.slug === permissionSlug,
);
return hasPermission;
}
async menu(userId: string): Promise<any> {
const user = await this.userModel.findById(userId).populate('roles').exec();
if (!user) {
throw new NotFoundException(handleMessageError('notFound'));
}
const roleIds = user.roles.map((role) => role._id);
const roleDocs = await this.roleModel
.find({ _id: { $in: roleIds } }, 'permisos')
.exec();
// Aplanar el array de arrays
const flattenedPermisos = roleDocs.flatMap((role) => role.permisos);
// Convertir ObjectId a string para deduplicar
const flattenedPermisosString = flattenedPermisos.map((id) =>
id.toString(),
);
// Eliminar duplicados usando un Set
const uniquePermisosString = [...new Set(flattenedPermisosString)];
const permisos = await this.permisoModel
.find(
{
_id: { $in: uniquePermisosString },
$and: [{ 'modulo.showMenu': true }, { 'area.showMenu': true }],
},
'-_id modulo.nombre modulo.codigo modulo.icon.codigo modulo.posicion area.nombre area.codigo area.icon.codigo area.posicion',
)
.sort({
'modulo.posicion': 1,
'area.posicion': 1,
})
.exec();
const grouped = permisos.reduce((acc, curr) => {
const moduloName = curr.modulo.nombre;
const areaName = curr.area.nombre;
const areaIcon = curr.area.icon.codigo;
// Encontrar o crear el grupo para el modulo
if (!acc[moduloName]) {
acc[moduloName] = { label: moduloName, items: [] };
}
// Agregar area al modulo correspondiente
const moduloGroup = acc[moduloName];
const existingArea = moduloGroup.items.find(
(item) => item.label === areaName,
);
if (!existingArea) {
moduloGroup.items.push({
label: areaName,
icon: `pi pi-fw ${areaIcon}`,
routerLink: [`/${curr.modulo.codigo}/${curr.area.codigo}`],
});
}
return acc;
}, {});
// Convertir el objeto agrupado a un array
const menu = Object.values(grouped);
return handleMessageSucces(menu, 'findOne');
}
}