Módulo User
src/app/modules/user/
Gestión de usuarios internos del CRM (agentes y administradores) y de su perfil (firma y avatar).
Modelo — User
Colección users. El toJSON elimina siempre el campo password.
| Campo | Tipo | Descripción |
|---|---|---|
_id | ObjectId | ID de MongoDB |
identifier | string (UUID) | ID externo único (se genera con randomUUID) |
tenantId | ObjectId → Tenant | Tenant al que pertenece |
username | string | Nombre de usuario (sin índice único) |
email | string | Email único, en minúsculas |
password | string | Hash bcrypt (10 rondas) |
roleId | ObjectId → Role | Rol asignado |
isActive | boolean | Si puede iniciar sesión (por defecto true) |
firstname | string | Primer nombre |
secondFirstname | string | Segundo nombre (por defecto vacío) |
lastName | string | Apellido paterno |
secondLastName | string | Apellido materno (por defecto vacío) |
userType | string | admin o agent (USER_TYPE) |
externalId | string | null | ID en un sistema externo (indexado) |
signature | string | Firma del agente (por defecto vacía) |
avatarUrl | string | URL pública del avatar |
isPlatformAdmin | boolean | Administrador de plataforma (por defecto false); lo usa PlatformAdminGuard |
createdAt / updatedAt | Date | Timestamps automáticos |
Endpoints
Base: /api/v1/users. El controlador aplica @UseGuards(PermissionsGuard) y @Auth(), por lo que todas las rutas requieren sesión; las que declaran @RequirePermission exigen además el permiso indicado (ver Guards). Las rutas siguen el patrón CRUD_ROUTES (no REST puro).
| Método | Ruta | Permiso | Descripción |
|---|---|---|---|
GET | /users/get-me | Solo sesión | Usuario actual, permisos, feature flags y preferencias del tenant |
PATCH | /users/me/signature | Solo sesión | Actualiza la firma del usuario actual |
PATCH | /users/me/avatar | Solo sesión | Sube el avatar del usuario actual (multipart, campo file) |
POST | /users/create | USER_CREATE | Crea un usuario en el tenant del solicitante |
GET | /users/get-all | USER_READ | Lista usuarios; paginada si se envían page y limit |
GET | /users/get-one/:id | USER_READ | Obtiene un usuario por identifier (UUID) |
PATCH | /users/update/:id | USER_UPDATE | Actualiza un usuario por _id |
PATCH | /users/toggle-active/:id | USER_UPDATE | Invierte isActive |
DELETE | /users/remove/:id | USER_DELETE | Elimina físicamente el usuario |
Detalles por endpoint
get-me: devuelve el usuario sinpassword, máspermissions(acrónimos resueltos porPermissionResolverService),featureFlags(scheduledMessagesEnabled) ytenantPreferences(language,timezone). Solo se exponen esos valores deTenantSettings, nunca el documento completo, porque este endpoint lo consume cualquier rol (ver Settings).me/avatar: solo se aceptaimage/jpegy hasta 2 MB (AVATAR_UPLOAD). La imagen se procesa conMediaProcessorService, se guarda como objeto público enavatars/{tenantId}/{userId}y la URL se guarda con un sufijo?v=timestamppara invalidar caché.get-all: sinpageylimitdevuelve todos los usuarios del tenant endata.results; con ambos devuelvedata.resultsydata.meta { total, page, limit }.get-one/:id: busca poridentifier(UUID) y tenant, no por_id.update/:id,toggle-active/:id,remove/:id: usan el_idde MongoDB filtrado por tenant; un id con formato inválido responde404.
DTOs
Validaciones de class-validator (ValidationPipe). "Opcional" indica @IsOptional().
Parámetros de ruta
| Parámetro | Tipo | Obligatorio | Validación | Usado en |
|---|---|---|---|---|
id | string | Sí | Ninguna en el controlador. En update, toggle-active y remove el servicio exige un ObjectId válido (si no, 404) | get-one/:id (UUID identifier), update/:id, toggle-active/:id, remove/:id (_id de MongoDB) |
CreateUserDto (body de POST /users/create)
| Campo | Tipo | Obligatorio | Validaciones | Descripción |
|---|---|---|---|---|
username | string | Sí | @IsString, @MinLength(2), @MaxLength(50) | Nombre de usuario |
email | string | Sí | @IsEmail | Email (único globalmente) |
password | string | Sí | @IsString, @MinLength(8) | Contraseña en texto plano; se guarda con bcrypt |
roleId | string | Sí | @IsMongoId | _id del rol |
firstname | string | Sí | @IsString, @MinLength(1), @MaxLength(80) | Primer nombre |
secondFirstname | string | No | @IsString, @MaxLength(80) | Segundo nombre |
lastName | string | Sí | @IsString, @MinLength(1), @MaxLength(80) | Apellido paterno |
secondLastName | string | No | @IsString, @MaxLength(80) | Apellido materno |
userType | string | Sí | @IsIn: admin, agent | Tipo de usuario |
isActive | boolean | No | @IsBoolean | Por defecto true (modelo) |
tenantId no viaja en el body: el controlador lo toma del usuario autenticado.
UpdateUserDto (body de PATCH /users/update/:id)
PartialType(CreateUserDto): mismos campos y validaciones, todos opcionales. Si se envía password, se vuelve a hashear.
QueryUserDto (query de GET /users/get-all)
Combina PaginationDto y FilterUserDto con IntersectionType. Sin page y limit no hay paginación.
| Campo | Tipo | Obligatorio | Validaciones | Descripción |
|---|---|---|---|---|
page | number | No | @IsInt, @Min(1), @Type(() => Number) | Página |
limit | number | No | @IsInt, @Min(1), @Type(() => Number) | Tamaño de página |
username | string | No | @IsString | Regex insensible a mayúsculas |
email | string | No | @IsString | Regex insensible a mayúsculas |
firstname | string | No | @IsString | Regex insensible a mayúsculas |
lastName | string | No | @IsString | Regex insensible a mayúsculas |
userType | string | No | @IsString | Igualdad exacta |
isActive | boolean | No | @IsBoolean, @Transform: 'true' o true produce true; cualquier otro valor produce false | Filtro por estado |
UpdateSignatureDto (body de PATCH /users/me/signature)
| Campo | Tipo | Obligatorio | Validaciones | Descripción |
|---|---|---|---|---|
signature | string | Sí | @IsString, @MaxLength(1000) | Firma del agente |
Avatar (PATCH /users/me/avatar)
No usa DTO: recibe multipart/form-data con el campo de archivo file (FileInterceptor, almacenamiento en memoria). El servicio valida image/jpeg y un tamaño máximo de 2 MB; si no se cumple responde 400.
Servicio
UserService crea usuarios (rechaza con 409 si el email ya existe), hashea contraseñas con bcrypt, filtra siempre por tenant en actualización, borrado y toggle, y expone findFilterableAgents (campos _id, firstname, lastName, username) para filtros de otros módulos. hasOperationalTenant lo usa AuthGuard para rechazar sesiones de tenants inactivos.
Gotchas
- La unicidad de email es global (índice único sobre
email), no por tenant. get-onebusca poridentifier, mientras queupdate,toggle-activeyremoveusan_id.removees un borrado físico (findOneAndDelete).passwordnunca se devuelve:toJSONlo elimina yget-mey las rutas de perfil lo quitan explícitamente.
Exports
UserService y UserRepository se exportan para su uso en AuthGuard y ChatGateway.