Skip to main content

Módulo Connection

src/app/modules/connection/

Una conexión representa un canal de comunicación de un tenant: chat web (widget), WhatsApp por QR, o cuentas de Meta (Instagram / Messenger). Cada ticket nace asociado a una conexión (ticket.connectionId), y esa conexión decide por qué dispatcher saliente se entrega la respuesta (ver Message).

Tipos de canal y estado​

src/app/modules/connection/connection.constants.ts

channelTypeValorCreable por API (POST /connections/create)Configuración
WEBwebSíwebConfig
WHATSAPP_QRwhatsapp-qrSíwhatsappConfig (ver WhatsApp)
WHATSAPP_CLOUDwhatsapp-cloudNo (definido, sin implementación)—
FACEBOOKfacebookNometaConfig
INSTAGRAMinstagramNo: se crea por OAuth (ver Meta)metaConfig

ConnectionService.create rechaza (400 connection.channel_not_available) cualquier tipo fuera de web y whatsapp-qr (CREATABLE_CHANNEL_TYPES).

status: connected (por defecto), disconnected, pending. Una conexión de WhatsApp QR nace pending; una de Instagram nace pending y pasa a connected al guardar metaConfig; un error de autenticación de Meta la deja en disconnected.

Modelo — Connection​

Colección connections.

CampoTipoDescripción
tenantIdObjectId → TenantTenant propietario (indexado)
namestringNombre. Por defecto Web Chat Connection / WhatsApp Connection si no se envía
channelTypestringVer tabla anterior. Inmutable tras crear
statusstringconnected | disconnected | pending
tokenstring (UUID, único)Token público de embebido del widget. Se regenera con PATCH regenerate-token/:id
greetingMessage / farewellMessagestringMensajes automáticos de saludo/despedida (ver System Message)
departmentIdsObjectId[] → DepartmentDepartamentos que reciben los tickets nuevos
reopenToleranceMinutesnumberPor defecto 15
returnToLastAgentbooleanPor defecto true
webConfigsubdoc | nullallowedDomains: string[] (mín. 1, whitelist de orígenes del widget) y requiresAuth: boolean (por defecto true)
whatsappConfigsubdoc | nullsessionState, phoneJid, lastQrAt, lastConnectedAt (lo escribe solo WhatsappSessionManager)
metaConfigsubdoc | nullaccountId, accountName, accessToken (cifrado, select: false), expiresAt, authErrorAt

Índice único parcial { channelType, 'metaConfig.accountId' } (solo si accountId es string): una cuenta de Meta pertenece a una única conexión.

metaConfig.accessToken​

Se guarda como EncryptedEnvelope { iv, tag, ciphertext } (AES-256-GCM con META_TOKEN_ENCRYPTION_KEY, ver Crypto). El campo tiene select: false: las lecturas normales no lo devuelven. Los métodos del repositorio que lo necesitan lo piden con .select('+metaConfig.accessToken') (findByIdWithMetaToken, findByMetaAccount, findMetaConnectionsExpiringBefore). readMetaAccessToken(connection) (helpers/meta-token.helper.ts) lo descifra y falla si el token no fue cargado.

Endpoints​

Base: /api/v1/connections. Todos con @Auth() + PermissionsGuard y el permiso indicado.

MétodoRutaPermisoDescripción
POST/connections/createCONNECTION_CREATECrea una conexión web o whatsapp-qr
GET/connections/get-allCONNECTION_READLista (paginada si se envían page y limit); filtros name, channelType
GET/connections/get-one/:idCONNECTION_READObtiene una conexión
PATCH/connections/update/:idCONNECTION_UPDATEActualiza campos editables
PATCH/connections/regenerate-token/:idCONNECTION_UPDATEEmite un token UUID nuevo (el anterior deja de resolver)
DELETE/connections/remove/:idCONNECTION_DELETEElimina la conexión

Los endpoints de sesión de WhatsApp cuelgan de /connections/whatsapp/... (ver WhatsApp). Para alta de Instagram ver Meta.

DTOs​

// CreateConnectionDto
{
name?: string // opcional, no vacío
channelType: ChannelType // IsIn(CHANNEL_TYPE_VALUES)
webConfig?: ConnectionWebConfigDto // OBLIGATORIO si channelType === 'web'
}

// ConnectionWebConfigDto
{
allowedDomains: string[] // ≥1, URL con protocolo (require_tld: false)
requiresAuth?: boolean // omitido → true
}

// UpdateConnectionDto (todo opcional)
{
name, greetingMessage, farewellMessage,
departmentIds: string[] // MongoIds; [] limpia los departamentos
reopenToleranceMinutes: number // entero ≥ 1
returnToLastAgent: boolean
webConfig: ConnectionWebConfigDto
}

channelType, token, whatsappConfig y metaConfig no son editables vía PATCH update.

Gotchas​

  • ConnectionService extiende EventEmitter y emite created solo para whatsapp-qr; WhatsappModule se suscribe e inicia la sesión. Así ConnectionModule no importa WhatsappModule (evita ciclo).
  • Para el canal web no hay dispatcher saliente registrado: la entrega es el propio emit por socket.
  • requiresAuth puede faltar en documentos antiguos; léalo como ?? true.