Skip to main content

Conexión y flujo con el backend

El widget usa tres constantes derivadas de CRM_ORIGIN:

ConstanteValor
API_BASECRM_ORIGIN + '/api/v1'
SOCKET_NSCRM_ORIGIN + '/chat'
SOCKET_CLIENT_SRCCRM_ORIGIN + '/socket.io/socket.io.js'

El módulo widget del backend expone los endpoints HTTP (API_PATHS.WIDGET = 'widget', versión v1) y el gateway chat (namespace chat) atiende el socket.

Endpoints HTTP​

Método y rutaUso en el widget
GET /widget/config?token=Al arrancar (boot). Del resultado solo se usa requiresAuth; devuelve data.results[0]
GET /widget/validate?token=Pre-check no bloqueante tras montar; si el estado no es active solo emite un console.warn
GET /widget/messages?token=&externalId=Historial del ticket abierto, tras cada authenticated
POST /widget/files/upload?token=&externalId=Subida de archivos (multipart/form-data, campo file); responde data.id

Todos validan el token y el Origin/Referer (ver Seguridad y orígenes).

Carga de socket.io-client​

ensureSocketIo inyecta un script con SOCKET_CLIENT_SRC en head. Mantiene una cola de callbacks, un token por intento y un timeout de 15 s (SOCKET_IO_LOAD_TIMEOUT_MS); si falla, muestra el mensaje de sistema "No se pudo cargar el chat. Intenta más tarde." y permite reintentar.

Conexión​

socket = window.io(SOCKET_NS, {
transports: ['websocket'],
autoConnect: true,
reconnection: true,
reconnectionAttempts: Infinity,
reconnectionDelay: 1000,
reconnectionDelayMax: 30000,
});

Una bandera connecting evita abrir dos sockets si init() y open() se llaman seguidos.

Autenticación​

En connect el widget emite authenticate:

CampoOrigen
sendTypesiempre 'contact'
tokenconfig.token
externalId, usernameidentidad del visitante
langVISITOR.lang, luego config.lang, luego idioma del navegador
email, phone, fullNamesolo si están presentes (el backend los persiste en el Contact; los ausentes no borran)

Si no llega authenticated en 10 s (AUTHENTICATE_TIMEOUT_MS), el widget hace disconnect() y connect() por su cuenta (el servidor corta a los 30 s).

Al recibir authenticated: se marca como autenticado, se limpia la lista de mensajes, se muestra el skeleton de carga, se llama loadHistory() (recupera lo recibido durante una caída) y se habilita el composer. El authenticated de un contacto no trae ticketId; el widget lo toma de los eventos new_message o del historial.

La presencia online la deriva el backend del propio authenticate; el widget no emite un evento extra.

Eventos​

Emitidos por el widget​

EventoPayloadCuándo
authenticatever arribaEn cada connect
send-message{ content, parentId? }Envío de texto (con parentId si responde a un mensaje)
send-file{ fileId, parentId? }Tras subir el archivo por HTTP
message-delivered{ ticketId, messageIds }Al recibir mensajes de agente o al cargar historial con ack === 'sent'
mark-read{ ticketId }Al enfocar el campo de escritura
contact-leavingsin payloadCierre intencional (ver abajo)

Recibidos​

EventoEfecto
authenticatedVer arriba
new_messagePinta el mensaje; sendType === 'contact' es el lado propio
new-file-messagePinta imagen, audio o enlace de archivo
message_ack_changedActualiza ✓ / ✓✓ solo en las filas propias indicadas en messageIds (si coincide el ticket activo)
ticket_closedMensaje de sistema "Esta conversación fue cerrada por el agente.", deshabilita el composer y estado "Conversación cerrada"
exceptionMuestra err.message como mensaje de sistema
disconnectVer reconexión

Si el widget no está visible (panel cerrado, pestaña oculta o sin foco), cada mensaje de agente incrementa el badge, reproduce el sonido (si está habilitado) y lanza una notificación del navegador.

Subida de archivos​

  1. Validación local (attachments.js): documentos hasta 10 MB, imágenes hasta 5 MB, audio de 1 a 120 s.
  2. POST /widget/files/upload por XMLHttpRequest con barra de progreso.
  3. Con la respuesta (data.id) emite send-file por el socket.
  4. Si destroy() ocurre durante la subida, la request se aborta para no filtrar el archivo a otra sesión.

Reconexión​

SituaciónComportamiento
Caída de red u otro motivo distinto de io server disconnectEstado "Conexión perdida. Reconectando…" y reintentos automáticos de socket.io (infinitos, backoff de 1 a 30 s)
reconnect_failedEstado "Sin conexión"
io server disconnectHasta 3 reintentos propios, con espera de n x 3000 ms; agotados, "Sesión finalizada. Tocá para reintentar." y el estado pasa a ser pulsable para reintentar

El contador de reintentos solo se reinicia si la conexión se mantiene autenticada 5 s (STABLE_CONNECTION_MS), para evitar bucles infinitos silenciosos.

Aviso de salida (contact-leaving)​

presence.js decide si se emite: solo con origen pagehide o destroy, y únicamente si el widget está autenticado y el socket conectado. Una caída de red u origen desconocido nunca lo emite, de modo que el backend lo clasifica como network-loss. Se envía por el mismo socket (no sendBeacon) y es de mejor esfuerzo. El evento coincide con CHAT_EVENTS.CONTACT_LEAVING del backend.