HEX
Server: Apache/2.4.52 (Ubuntu)
System: Linux ip-172-31-4-197 6.8.0-1036-aws #38~22.04.1-Ubuntu SMP Fri Aug 22 15:44:33 UTC 2025 x86_64
User: ubuntu (1000)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
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');
  }
}