Skip to main content

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:

  1. Que token corresponda a una Connection con status = connected y channelType = web; si no, 404 (widget.invalidToken). No se aceptan tokens de otros canales ni el antiguo token de tenant.
  2. Que el tenant de la conexión exista (404 en caso contrario).
  3. Que el header Origin (o, si falta, Referer) coincida con alguno de webConfig.allowedDomains de 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 con curl) sin Origin se rechaza.

Endpoints​

Base: /api/v1/widget. Sin @Auth() ni permisos: son públicos y la seguridad depende del token y del origen.

MétodoRutaDescripción
GET/widget/validateVerifica que el token y el origen sean válidos; responde data.results con { status: 'active' }
GET/widget/configConfiguración de arranque; responde { requiresAuth }
GET/widget/messagesHistorial del ticket abierto del contacto identificado por externalId
POST/widget/files/uploadSube un archivo en nombre del contacto (multipart)

Respuestas​

  • config: requiresAuth toma webConfig.requiresAuth de la conexión y vale true si 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.results es un arreglo de WidgetMessage. Devuelve un arreglo vacío si falta externalId, si no hay contacto con ese externalId en 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ámetroUbicaciónTipoObligatorioValidaciónUsado en
tokenquerystringSíDebe resolver a una conexión web conectada; un valor vacío produce 404Los cuatro endpoints
externalIdquerystringSí en messages y files/uploadEn messages un valor vacío devuelve []; en files/upload no se validaGET /widget/messages, POST /widget/files/upload
Origin / RefererheaderstringSí en la prácticaDebe estar en allowedDomains; se usa Origin y, si falta, RefererLos cuatro endpoints
filebody multipartarchivoSí en files/uploadCampo de formulario file; mimetype, tamaño y duración los valida FileService.uploadPOST /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.

CampoTipoDescripción
_idstringId del mensaje
ticketIdstringId del ticket (el widget lo necesita para acusar la entrega)
senderIdstring | nullId del remitente, si existe
sendTypestringTipo de remitente
senderNamestringNombre del remitente; Sistema para saludos o despedidas sin autor; agentes y operadores se rotulan con nombre y apellido; los contactos con su username
bodystringTexto
timestampDate | nullFecha de creación
parentobjeto | nullMensaje citado (_id, body, messageType, sendType)
fileobjeto | nullAdjunto (id, url firmada, filename, mimetype, type, size)
ackstringEstado de entrega; sent si el mensaje es anterior al campo

Gotchas​

  • Si falta Origin y Referer, la petición falla con 403 aunque el token sea correcto.
  • El módulo mantiene dos archivos de interfaz: interfaces/widget.interface.ts (el vigente, usado por el servicio) e interface/widget.interface.ts (una versión antigua y reducida de WidgetMessage, sin _id, ticketId, parent, file ni ack).
  • messages solo 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.