Módulo Widget
src/app/modules/widget/
API HTTP pública que consume el widget de chat web incrustado en sitios de terceros. No usa JWT: el cliente se identifica con el token de una conexión web (?token=) y el servidor valida además el dominio de origen. No define modelos propios; reutiliza Connection, Tenant, Contact, Ticket, Message y File mediante los módulos que importa (TenantModule, ContactModule, TicketModule, MessageModule, FileModule, ConnectionModule).
El tiempo real del widget (envío y recepción de mensajes) no está en este módulo: lo gestiona el gateway de chat (ver Chat). El módulo cubre validación, configuración, historial y subida de archivos.
Validación de acceso
Todos los endpoints pasan por WidgetService.resolveAccessOrThrow, que exige simultáneamente:
- Que
tokencorresponda a una Connection constatus = connectedychannelType = web; si no,404(widget.invalidToken). No se aceptan tokens de otros canales ni el antiguo token de tenant. - Que el tenant de la conexión exista (
404en caso contrario). - Que el header
Origin(o, si falta,Referer) coincida con alguno dewebConfig.allowedDomainsde la conexión, comparando esquema y host; si no coincide o no hay ninguno de los dos headers,403(widget.originNotAllowed). Por eso una llamada servidor a servidor (por ejemplo concurl) sinOriginse rechaza.
Endpoints
Base: /api/v1/widget. Sin @Auth() ni permisos: son públicos y la seguridad depende del token y del origen.
| Método | Ruta | Descripción |
|---|---|---|
GET | /widget/validate | Verifica que el token y el origen sean válidos; responde data.results con { status: 'active' } |
GET | /widget/config | Configuración de arranque; responde { requiresAuth } |
GET | /widget/messages | Historial del ticket abierto del contacto identificado por externalId |
POST | /widget/files/upload | Sube un archivo en nombre del contacto (multipart) |
Respuestas
- config:
requiresAuthtomawebConfig.requiresAuthde la conexión y valetruesi el campo no existe (conexiones antiguas). Indica si el widget exige el usuario autenticado del sitio anfitrión o admite visitantes anónimos. - messages:
data.resultses un arreglo deWidgetMessage. Devuelve un arreglo vacío si faltaexternalId, si no hay contacto con eseexternalIden el tenant, o si el último ticket del contacto no está abierto. Trae hasta 50 mensajes y excluye notas e internos. - files/upload: misma forma que
POST /files/upload:{ message, data }con los datos del archivo (ver File).
DTOs y parámetros
El módulo no define DTOs de class-validator: los parámetros se leen con @Query y @Headers, sin validación de formato (solo se comprueba su efecto en el servicio).
| Parámetro | Ubicación | Tipo | Obligatorio | Validación | Usado en |
|---|---|---|---|---|---|
token | query | string | Sí | Debe resolver a una conexión web conectada; un valor vacío produce 404 | Los cuatro endpoints |
externalId | query | string | Sí en messages y files/upload | En messages un valor vacío devuelve []; en files/upload no se valida | GET /widget/messages, POST /widget/files/upload |
Origin / Referer | header | string | Sí en la práctica | Debe estar en allowedDomains; se usa Origin y, si falta, Referer | Los cuatro endpoints |
file | body multipart | archivo | Sí en files/upload | Campo de formulario file; mimetype, tamaño y duración los valida FileService.upload | POST /widget/files/upload |
Los límites de tamaño por tipo de archivo (imagen 5 MB, documento 10 MB, audio 15 MB, video 20 MB) provienen de FILE_SIZE en file.constants.ts.
WidgetMessage (elemento de messages)
Interfaz TypeScript (interfaces/widget.interface.ts), no DTO validado.
| Campo | Tipo | Descripción |
|---|---|---|
_id | string | Id del mensaje |
ticketId | string | Id del ticket (el widget lo necesita para acusar la entrega) |
senderId | string | null | Id del remitente, si existe |
sendType | string | Tipo de remitente |
senderName | string | Nombre del remitente; Sistema para saludos o despedidas sin autor; agentes y operadores se rotulan con nombre y apellido; los contactos con su username |
body | string | Texto |
timestamp | Date | null | Fecha de creación |
parent | objeto | null | Mensaje citado (_id, body, messageType, sendType) |
file | objeto | null | Adjunto (id, url firmada, filename, mimetype, type, size) |
ack | string | Estado de entrega; sent si el mensaje es anterior al campo |
Gotchas
- Si falta
OriginyReferer, la petición falla con403aunque el token sea correcto. - El módulo mantiene dos archivos de interfaz:
interfaces/widget.interface.ts(el vigente, usado por el servicio) einterface/widget.interface.ts(una versión antigua y reducida deWidgetMessage, sin_id,ticketId,parent,fileniack). messagessolo muestra el ticket más reciente del contacto y únicamente si está abierto; un ticket resuelto no se muestra.- La subida de archivos valida el token antes de tocar
FileService, por lo que un token inválido nunca llega al procesamiento del archivo.