Console d'exploitation
Le kit embarque une petite console d'exploitation autonome, pour vous, l'intégrateur. Définissez un adminToken, ouvrez http://<host>:<port>/admin/ui, et vous obtenez une vue en direct de chaque installation ainsi que quelques actions sûres.
Elle fonctionne entièrement de votre côté : la console ne lit que le store SQLite local de votre intégration et déclenche le moteur de synchro que vous exécutez déjà. Aucun compte, service ou base ShopiMind n'est impliqué — rien ici ne dialogue avec ShopiMind hormis les appels de synchro que votre intégration effectue déjà.
Rien à développer
La console fait partie du kit. Aucun paquet supplémentaire, aucun frontend à déployer — c'est une page unique et autonome servie par le serveur HTTP du kit.
L'activer
La console et toute la surface /admin/* sont inactives tant que vous n'avez pas défini d'adminToken. Sans lui, chaque route admin répond 401.
const app = createIntegrationApp(myIntegration, {
databasePath: env.DATABASE_PATH ?? './data/store.sqlite',
webhookSecret: required('WEBHOOK_SECRET'),
credentialsKey: env.CREDENTIALS_KEY,
adminToken: env.ADMIN_TOKEN, // active /admin/* et la console (32+ caractères haute entropie)
adminPort: 9090, // recommandé : servir /admin sur un listener PRIVÉ…
adminHost: '127.0.0.1', // …bouclé sur loopback (le défaut quand adminPort est défini)
adminSecureCookie: true, // marque le cookie de session Secure (servez la console en HTTPS)
});Rendez-vous ensuite sur http://127.0.0.1:9090/admin/ui, collez votre adminToken, et vous y êtes.
Ce que vous pouvez voir
| Zone | Contenu |
|---|---|
| Tableau de bord | nombre d'installations par statut, webhooks reçus sur 24 h (et combien refusés), totaux de rejets par entité, version du kit en cours. |
| Définition | ce que déclare cette intégration — métadonnées, schéma de configuration, étapes de synchro, routes entrantes, hooks et capacités. |
| Installations | une liste recherchable et paginée ; ouvrez-en une pour voir sa boutique, le compte interne rattaché et le détail par installation. |
| Curseurs & runs | le curseur de synchro par entité/source (dernier statut, dernière synchro, nombre d'items, échecs consécutifs) et les runs de synchro récents avec leur résumé. |
| Webhooks & entrants | le journal des webhooks de cycle de vie et le journal des appels entrants, paginés et filtrables. |
| État | le stockage clé/valeur par installation en métadonnées uniquement — clé, type, longueur, dernière mise à jour. La valeur d'un secret n'est jamais affichée. |
| Dead-letter | les éléments refusés par l'API ShopiMind lors d'un push en masse (entité, raison, charge utile), globalement ou par installation. |
| Audit | chaque action d'administration (connexion, synchro, reprovisioning, reveal, purge) en métadonnées. |
Ce que vous pouvez faire
Depuis une installation, ou la vue dead-letter :
- Synchroniser — déclencher une synchro incrémentale, ou un backfill complet.
- Reprovisionner — ré-exécuter le
provisioning()de votre intégration (find-or-create idempotent) ; pratique après un changement de source de données ou de plan custom-data. - Purger un élément dead-letter (scopé à son installation).
- Révéler la charge utile brute d'un webhook ou d'un rejet — une action délibérée et auditée (voir plus bas).
Modèle de sécurité
La console est sûre par défaut. Les garanties ci-dessous sont imposées par le kit, pas par l'UI.
- Deux voies d'accès, toutes deux protégées par jeton. Un navigateur échange le jeton admin une fois (
POST /admin/session) contre un cookie de session HttpOnly,SameSite=Strictplus un jeton CSRF ; le jeton brut n'est jamais stocké dans le navigateur. Les clients automatisés (curl, scripts) peuvent à la place envoyer le jeton directement viax-admin-token(ouAuthorization: Bearer). - CSRF double-submit. Un appel modifiant l'état réalisé via une session navigateur doit renvoyer le jeton CSRF dans l'en-tête
x-csrf-token. Les clients authentifiés par jeton ne reposent pas sur un cookie : ils ne sont pas exposés au CSRF et n'en ont pas besoin. - PII masquée par défaut. Les e-mails, numéros de téléphone, noms et adresses présents dans les charges de webhooks et de rejets sont masqués avant affichage.
- Les secrets ne sont jamais renvoyés. L'état est lu en métadonnées uniquement ; une valeur chiffrée (une clé API stockée via
setSecret) n'est jamais matérialisée — c'est imposé au niveau SQL, pas seulement dans l'UI. - Le reveal est séparé et audité. Voir une charge non masquée est une action explicite consignée dans le journal d'audit.
- Borné & throttlé. Le jeton admin est comparé en temps constant, les routes admin sont limitées par IP, et les sessions expirent (glissant 12 h) et sont plafonnées en nombre.
Gardez la surface admin privée
Le jeton admin protège tout, mais traitez /admin/* comme une surface interne. En production, placez-la sur son propre listener avec adminPort (bouclé sur loopback ou une interface privée par défaut), servez-la en HTTPS avec adminSecureCookie: true, et utilisez un adminToken long et à haute entropie (32+ caractères). Le kit émet un avertissement au démarrage si la surface admin est exposée sur un listener public 0.0.0.0, si le jeton est court, ou si le cookie n'est pas marqué Secure.
Isoler la surface admin (recommandé)
Par défaut, /admin/* partage le serveur public (correct en développement local). Définissez adminPort pour déplacer toute la surface admin — la console et son API — sur un listener séparé, afin que le port public n'expose que les webhooks, les appels entrants et /health :
adminPort: 9090, // surface admin sur son propre port…
adminHost: '127.0.0.1', // …bouclée sur loopback par défaut ; exposez-la via votre ingress au besoin
adminSecureCookie: true, // et servez-la en HTTPSRétention
Les données d'administration ont leur propre rétention, indépendante des tables de journal :
| Option | Défaut | Purge |
|---|---|---|
rejectedRetentionDays | = retentionDays (90) | le dead-letter (rejected_item). Augmentez-la pour conserver les éléments refusés plus longtemps à des fins d'analyse. |
auditRetentionDays | 365 | le journal d'audit. ≤ 0 désactive la purge d'audit. |
Accès API sans la console
Chaque lecture et chaque action est un simple endpoint HTTP — utilisez-le depuis un script ou un tableau de bord de supervision avec l'en-tête x-admin-token (pas de cookie, pas de CSRF) :
curl -s http://127.0.0.1:9090/admin/meta -H "x-admin-token: $ADMIN_TOKEN"
curl -s -X POST "http://127.0.0.1:9090/admin/sync/$ID?full=true" -H "x-admin-token: $ADMIN_TOKEN"Référence des endpoints
L'authentification de chaque route ci-dessous est le jeton admin ou une session valide ; les routes modifiant l'état exigent en plus le jeton CSRF lorsqu'elles sont appelées depuis une session.
| Méthode | Chemin | Rôle |
|---|---|---|
GET | /admin/ui | La console d'exploitation (HTML). |
POST | /admin/session | Échange le jeton admin contre un cookie de session + jeton CSRF. |
DELETE | /admin/session | Termine la session courante. |
GET | /admin/meta | Synthèse du tableau de bord (inclut l'identité de l'intégration). |
GET | /admin/definition | Ce que déclare l'intégration (métadonnées, schéma de config, étapes de synchro, capacités…). |
GET | /admin/installations | Liste des installations (?status=, ?q=, pagination). |
GET | /admin/installations/{id} | Une installation, agrégée. |
GET | /admin/installations/{id}/cursors | Ses curseurs de synchro. |
GET | /admin/installations/{id}/runs | Ses runs de synchro (paginés). |
GET | /admin/installations/{id}/webhooks | Son journal de webhooks (PII masquée, paginé). |
GET | /admin/installations/{id}/inbound | Ses événements entrants (paginés). |
GET | /admin/installations/{id}/state | Son état en métadonnées (secrets jamais renvoyés). |
GET | /admin/rejected | Dead-letter global (?installationId=, ?entity=, PII masquée, paginé). |
GET | /admin/audit | Journal d'audit (paginé). |
POST | /admin/sync/{id}?full=true | Déclenche une synchro (audité). |
POST | /admin/installations/{id}/reprovision | Ré-exécute le provisioning (audité). |
POST | /admin/rejected/purge | Supprime des éléments dead-letter — corps { installationId, ids }, scopé à l'installation (audité). |
POST | /admin/rejected/{id}/reveal | Révèle la charge brute d'un rejet (audité). |
POST | /admin/installations/{id}/webhook-log/{logId}/reveal | Révèle la charge brute d'un webhook (audité). |
Pour aller plus loin
- Le kit d'intégration — les options de
createIntegrationAppet les routes exposées par le kit. - Synchroniser les données — ce qui alimente les curseurs, runs et dead-letter que vous voyez ici.