Ciclo de vida del conector
Las seis operaciones del conector — handshake, heartbeat, full-sync, delta-sync, delete, diagnóstico — y cómo se mapean a la superficie de la Connector API.
Un conector de CMS (PrestaShop, Bitrix, WordPress, Shopify) habla con AACsearch solo a
través de la Connector API montada en /api/connectors/** y
/api/projects/:projectId/**. Nunca toca Typesense directamente. Cada llamada está
controlada por un token ss_connector_* con el scope connector_write.
Ciclo de vida
Descripción
El diagrama recorre las seis fases del conector CMS — handshake, heartbeat, full-sync, delta-sync, delete, diagnóstico — cada una controlada por un token ss_connector_*. Las llamadas de sync crean una fila SearchConnectorSyncJob más N filas SearchSyncOutbox; el worker drena el outbox hacia el alias de Typesense mientras el módulo CMS hace polling sobre la jobId para detectar la finalización.
sequenceDiagram
autonumber
participant Mod as CMS module (PrestaShop / Bitrix / WP / Shopify)
participant API as packages/api/modules/search/connector-public.ts
participant Auth as gateConnectorRequest (scope=connector_write)
participant DB as PostgreSQL (SearchConnectorSyncJob + SearchSyncOutbox)
participant W as Sync worker
participant TS as Typesense alias
rect rgb(220, 252, 231)
note over Mod,API: Phase 1 — handshake (once per install)
Mod->>API: POST /api/connectors/handshake { moduleVersion, platform }
API->>Auth: verify ss_connector_* + connector_write
API-->>Mod: { projectId, indexSlug, capabilities, syncModes }
end
rect rgb(219, 234, 254)
note over Mod,API: Phase 2 — heartbeat (periodic, every ~5 min)
Mod->>API: POST /api/connectors/:connectorId/heartbeat
API-->>Mod: { status: "ok", timestamp }
end
rect rgb(254, 243, 199)
note over Mod,DB: Phase 3 — full sync (initial / on-demand)
Mod->>API: POST /api/projects/:projectId/sync/full { products[1..1000] }
API->>DB: createSyncJob(type=full)
API->>DB: executeSyncJob -> enqueue doc_upsert per product (outbox)
API-->>Mod: { jobId, total, queued }
W->>DB: drain outbox doc_upsert rows
W->>TS: bulkUpsert(batch, action=upsert)
W->>DB: completeSyncJob(jobId)
end
rect rgb(245, 243, 255)
note over Mod,DB: Phase 4 — delta sync (per change webhook)
Mod->>API: POST /api/projects/:projectId/sync/delta { products[1..100] }
API->>DB: createSyncJob(type=delta)
API->>DB: executeSyncJob -> outbox rows
API-->>Mod: { jobId, total, queued }
W->>TS: bulkUpsert(batch)
end
rect rgb(254, 226, 226)
note over Mod,TS: Phase 5 — delete (single or batch)
Mod->>API: DELETE /api/projects/:projectId/products/:externalId
API->>DB: enqueue doc_delete
W->>TS: delete_by_query(externalId)
Mod->>API: DELETE /api/connector/documents { externalIds[1..500] }
API->>DB: enqueue doc_delete (batch)
W->>TS: batch delete
end
rect rgb(241, 245, 249)
note over Mod,DB: Phase 6 — diagnostics (on demand / on error)
Mod->>API: POST /api/projects/:projectId/diagnostics { moduleVersion, lastFullSync, errors[] }
API->>DB: recordDiagnostics() (in-memory store + log)
API-->>Mod: { received: true }
endLas seis operaciones
| Fase | HTTP | Cap. body | Propósito | Persiste |
|---|---|---|---|---|
| handshake | POST /api/connectors/handshake | pequeño | Confirma el token, intercambia capacidades + modos | Actualiza lastUsedAt |
| heartbeat | POST /api/connectors/:connectorId/heartbeat | ninguno | Ping de vida del módulo, intercambio de hora | Actualiza lastUsedAt |
| full-sync | POST /api/projects/:projectId/sync/full | 1000 | Carga inicial o reset; bulk-upsert de todo el catálogo | SearchConnectorSyncJob + filas outbox |
| delta-sync | POST /api/projects/:projectId/sync/delta | 100 | Webhook por cambio desde el CMS | SearchConnectorSyncJob + filas outbox |
| delete | DELETE /api/projects/:projectId/products/:externalIdDELETE /api/connector/documents | 1 / 500 | Eliminar un producto / lote | Fila(s) outbox doc_delete |
| diagnostics | POST /api/projects/:projectId/diagnostics | 4 KB | Auto-reporte de versión PHP, último sync, log de errores | Store de diagnósticos en memoria + log estructurado |
Qué controla cada llamada
gateConnectorRequest se ejecuta antes de cualquier lógica de handler:
- La cabecera Authorization debe llevar
Bearer ss_connector_*. verifySearchApiKey(rawKey, "connector_write")— el scope admin es un superset.- Devuelve
{ keyId, organizationId, indexId, indexSlug }al handler. - Los handlers de sync además verifican que
:projectIdcoincide conorganizationId— se devuelve 404 si un token se usa contra un proyecto distinto.
Por qué un job + una outbox
La Connector API nunca bloquea por Typesense. executeSyncJob escribe una fila
SearchConnectorSyncJob para visibilidad end-to-end, más N filas en
SearchSyncOutbox para que el worker drene. El módulo de CMS obtiene un jobId que
puede pollear para ver la finalización; el worker reconcilia cada fila de manera
independiente para que una sola SKU mala no rompa el lote.
Relacionado
- Ruta de escritura — el lado worker de la outbox.
- Tipos de claves y modelo de seguridad
— forma y reglas de scope del token
ss_connector_*.
Tipos de claves y modelo de seguridad
Las cuatro categorías de claves de AACsearch — search, connector, scoped y admin — sus prefijos ss_*, almacenamiento solo-hash, HMAC + TTL + filtro del scoped-token y cómo se verifica cada una en tiempo de petición.
Bucle de feedback analítico
Cómo una consulta de búsqueda se convierte en un SearchUsageEvent, cómo se registran clics y conversiones, y cómo la agregación alimenta el dashboard para el ajuste de relevancia.