AACsearch
Architecture

Cycle de vie du connecteur

Les six opérations du connecteur — handshake, heartbeat, full-sync, delta-sync, delete, diagnostics — et leur mapping sur la surface de la Connector API.

Un connecteur CMS (PrestaShop, Bitrix, WordPress, Shopify) ne parle à AACsearch qu'à travers la Connector API montée sur /api/connectors/** et /api/projects/:projectId/**. Il ne touche jamais Typesense directement. Chaque appel est gated par un token ss_connector_* ayant le scope connector_write.

Cycle de vie

Description

Le diagramme parcourt les six phases du connecteur CMS — handshake, heartbeat, full-sync, delta-sync, delete, diagnostics — chacune gated par un token ss_connector_*. Les appels de sync créent une ligne SearchConnectorSyncJob plus N lignes SearchSyncOutbox ; le worker draine l'outbox vers l'alias Typesense pendant que le module CMS poll la jobId jusqu'à la complétion.

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

Les six opérations

PhaseHTTPCap. bodyRôlePersiste
handshakePOST /api/connectors/handshakepetitConfirmer le token, échanger capabilities + modesMet à jour lastUsedAt
heartbeatPOST /api/connectors/:connectorId/heartbeataucunPing de vie du module, échange d'heure serveurMet à jour lastUsedAt
full-syncPOST /api/projects/:projectId/sync/full1000Premier chargement / reset ; bulk-upsert du catalogue entierSearchConnectorSyncJob + lignes outbox
delta-syncPOST /api/projects/:projectId/sync/delta100Webhook par changement venant du CMSSearchConnectorSyncJob + lignes outbox
deleteDELETE /api/projects/:projectId/products/:externalId
DELETE /api/connector/documents
1 / 500Supprimer un produit / lotLigne(s) outbox doc_delete
diagnosticsPOST /api/projects/:projectId/diagnostics4 KoAuto-rapport version PHP, dernier sync, log d'erreursStore de diagnostics en mémoire + log structuré

Ce qui gate chaque appel

gateConnectorRequest s'exécute avant toute logique de handler :

  1. L'en-tête Authorization doit porter Bearer ss_connector_*.
  2. verifySearchApiKey(rawKey, "connector_write") — le scope admin est un superset.
  3. Renvoie { keyId, organizationId, indexId, indexSlug } au handler.
  4. Les handlers de sync vérifient en plus que :projectId correspond à organizationId — 404 est renvoyé si un token est utilisé contre un autre projet.

Pourquoi un job + une outbox

La Connector API ne bloque jamais sur Typesense. executeSyncJob écrit une ligne SearchConnectorSyncJob pour la visibilité de bout en bout, plus N lignes dans SearchSyncOutbox que le worker draine. Le module CMS reçoit un jobId qu'il peut poller pour suivre la complétion ; le worker réconcilie chaque ligne indépendamment, donc une SKU mauvaise ne casse pas le lot entier.

Liens associés

On this page