AACsearch
Arquitectura

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 }
    end

Las seis operaciones

FaseHTTPCap. bodyPropósitoPersiste
handshakePOST /api/connectors/handshakepequeñoConfirma el token, intercambia capacidades + modosActualiza lastUsedAt
heartbeatPOST /api/connectors/:connectorId/heartbeatningunoPing de vida del módulo, intercambio de horaActualiza lastUsedAt
full-syncPOST /api/projects/:projectId/sync/full1000Carga inicial o reset; bulk-upsert de todo el catálogoSearchConnectorSyncJob + filas outbox
delta-syncPOST /api/projects/:projectId/sync/delta100Webhook por cambio desde el CMSSearchConnectorSyncJob + filas outbox
deleteDELETE /api/projects/:projectId/products/:externalId
DELETE /api/connector/documents
1 / 500Eliminar un producto / loteFila(s) outbox doc_delete
diagnosticsPOST /api/projects/:projectId/diagnostics4 KBAuto-reporte de versión PHP, último sync, log de erroresStore de diagnósticos en memoria + log estructurado

Qué controla cada llamada

gateConnectorRequest se ejecuta antes de cualquier lógica de handler:

  1. La cabecera Authorization debe llevar Bearer ss_connector_*.
  2. verifySearchApiKey(rawKey, "connector_write") — el scope admin es un superset.
  3. Devuelve { keyId, organizationId, indexId, indexSlug } al handler.
  4. Los handlers de sync además verifican que :projectId coincide con organizationId — 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

On this page