AI Decision System
Replayable, offline-verifiable records for AI-assisted go/no-go decisions: experiment promotions, typed model answers and human-approved actions.
Extensions OneStock du site, settings et journal des appels API
Translation pending
Developer toolsTypeScript · JavaScriptGitHub @mbousquet-onestockOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
App Next.js (déployée sur Vercel) chargée en iframe dans OneStock comme UI extension.Read the full README ↗
App Next.js (déployée sur Vercel) chargée en iframe dans OneStock comme UI extension.
Contexte : récupère les paramètres d'URL envoyés par OneStock (site_id, extension_id, user_id, lang, host_app, parent_url, …), fait le handshake extension_ready → onestock_data par postMessage (la signature extension_signature n'est pas contrôlée). Le contexte est affiché en haut de chaque page et conservé dans les liens. Hors OneStock, on peut saisir un site_id à la main.
Environnement OneStock = parent_url du contexte (ex. https://admin.eu1.qualif.onestock-retail.com). Ce n'est pas l'URL de l'API : le back-office répond 405 Not Allowed aux appels. Il sert de clé au token et à l'URL d'API de cet environnement ; seule une URL https d'un hôte OneStock est acceptée (*.onestock-retail.com, réglable avec ONESTOCK_ALLOWED_HOSTS).
Token OneStock : un par parent_url, saisi dans Settings généraux (encadré « Token OneStock », ouvert depuis OneStock) et stocké chiffré dans settings : key = onestock_token, site_id vide, extension_id = *, environment = origine du parent_url. Depuis OneStock, seul le token de ce parent_url est utilisé (aucun repli) : tant qu'il n'est pas saisi, aucun appel n'est fait et la page Extensions le signale. Hors OneStock (sans parent_url), le token de l'environnement (qualif, prod…).
URL de l'API OneStock ({onestock_api_root} ci-dessous, ex. https://p0039.api.qualif.onestock-retail.com) : l'api_url envoyée par OneStock dans onestock_data (ajoutée à l'URL de la page après le handshake, paramètre api_url, hôte OneStock https uniquement) . Elle est mémorisée pour le parent_url (ligne onestock_api_root dont environment = origine du parent_url, modifiable dans l'encadré « Token OneStock ») : les chargements suivants appellent directement la bonne API, avant même le handshake. Depuis OneStock, aucun repli sur une autre URL : tant que l'api_url n'est pas connue, aucun appel n'est fait (message d'attente). Hors OneStock (sans parent_url), le setting onestock_api_root de l'environnement (qualif, ONESTOCK_ENVIRONMENT, le seul disponible ou prod). Le token reste celui du parent_url. En cas de 401, la page Extensions indique quel token (celui du parent_url) a été envoyé à quelle API.
Extensions (/) : les extensions installées sur le site_id du contexte viennent de l'API OneStock — POST {onestock_api_root}/extensions/query puis GET {onestock_api_root}/extensions/{id} (corps { site_id, token }, token = setting global onestock_token) : icône, points d'injection, URL de production / test, dernière mise à jour. Elles sont rapprochées du catalogue commun en base par le nom (l'id OneStock est généré à la création sur chaque environnement) ; un badge signale les champs qui diffèrent (url, test_url, icon, injection_points). La modale de modification affiche toutes les données de l'API (id, dates, URL, icônes, points d'injection, JSON complet) à côté du formulaire du catalogue ; une extension installée hors catalogue peut y être ajoutée en un clic, formulaire prérempli. La création reprend les champs de l'API : nom, icône (base64, 100 Ko max), url, test_url et points d'injection (anchor, name, slug, path, icône). Chaque appel est enregistré dans api_logs (token masqué, icônes base64 remplacées par un libellé).
Actions sur chaque ligne (avec confirmation pour l'environnement) :
POST {onestock_api_root}/extensions avec { site_id, token, extension: { name, description, icon, url, test_url, injection_points: [{ name, anchor, path, icon, slug, rank }] } }, conforme à la spec OneStock (POST_v1_extensions) : le rang est porté par chaque point d'injection (1 par défaut), name, url, icon et au moins un point d'injection sont obligatoires (vérifiés avant l'appel), le name et le path (/) d'un point sont complétés s'ils manquent, les champs vides ne sont pas envoyés ;DELETE {onestock_api_root}/extensions/{id} avec { site_id, token } ;Ces appels sont regroupés dans lib/onestock.ts (extensionBody, createExtension, deleteRemoteExtension). Ancres acceptées : bo.page, bo.order.action, bo.orders.action, bo.customer.information, bo.customer.tab.
Settings d'une extension (/extensions/<id>/settings) : lignes de la table settings du site du contexte dont extension_id est l'id OneStock de l'extension, son nom ou le slug d'un de ses points d'injection. Les variables globales de l'extension (site_id vide) n'y sont pas affichées : elles servent de modèle et sont copiées sur le site lors de l'installation de l'extension sur l'environnement (même environnement, valeurs déjà présentes sur le site conservées).
Settings généraux (/settings) : lignes de settings avec scope = 'global' (ou extension_id = '*').
Les settings peuvent être ajoutés et modifiés depuis ces deux pages (en modification, seuls la valeur et le scope changent : key, site_id, extension_id et environment forment la clé). Ils sont filtrés sur le site_id du contexte ; un site_id à * ou vide vaut pour tous les sites et est marqué « remplacé » quand une valeur existe pour le site. Filtre par environment.
Valeurs sensibles (onestock_token et toute clé contenant token, secret, password, api_key, credential) : chiffrées en base (AES-256-GCM, format enc:v1:…, clé SETTINGS_ENCRYPTION_KEY) et jamais affichées — leur valeur n'est pas transmise au navigateur (ni dans le tableau, ni dans le formulaire : champ masqué, vide = valeur conservée). Elles ne sont déchiffrées que côté serveur, au moment de l'appel à l'API OneStock. Les valeurs encore en clair sont signalées « Non chiffré » ; npm run secrets:encrypt les chiffre toutes. Dans les logs, l'export et le contexte, elles sont remplacées par ••••••••.
lang du contexte (français ou anglais ; une autre langue bascule en anglais, lang absent → français). Les dates utilisent locale et timezone du contexte. Les traductions sont dans lib/i18n.ts./logs) : historique de la table api_logs (filtré sur le site du contexte si la table a une colonne site_id) — filtre par API, « erreurs uniquement », recherche, export CSV et vidage. Un clic sur une ligne affiche la requête et la réponse / l'erreur (bouton copier). Tokens et clés sont masqués à l'affichage et à l'export.Les colonnes de api_logs sont reconnues par leur nom : created_at, api, method, path (ou url), status, duration_ms, result, error, request, response, site_id.
loading.tsx).onestock_api_root / onestock_token, valeurs chiffrées) 5 min. Le cache est vidé immédiatement après une installation, une désinstallation ou la modification d'un setting (interface ou API) ; le bouton « Actualiser » relit OneStock à la demande. Une réponse en erreur ou incomplète n'est jamais mise en cache.api_logs faite après l'envoi de la page (after), hors du temps de réponse.L'interface applique le design system OneStock (@onestock-public/design-system) : police Roboto, couleur primaire #24bdb0, cartes bordées sans ombre, onglets OsTabs, badges teintés, alertes OsAlert. Les tokens et composants CSS sont dans app/globals.css, les primitives (icônes, alertes, modale) dans components/ui.tsx. Les formulaires de création / modification s'ouvrent dans une modale (fermeture par Échap ou clic à l'extérieur).
Créées automatiquement au premier accès (ou via npm run db:migrate) :
extensions (id interne, name unique, description, icon, url, test_url, rank, injection_points JSONB)Table existante utilisée : settings (key, value, updated_at, site_id, extension_id, environment, scope).
| Variable | Rôle |
|---|---|
DATABASE_URL / POSTGRES_URL | Connexion à la base Postgres Vercel (Neon), créée par Vercel quand la base est reliée au projet. La première variable contenant une vraie URL postgresql://… parmi DATABASE_URL, POSTGRES_URL, DATABASE_URL_UNPOOLED, POSTGRES_URL_NON_POOLING, POSTGRES_PRISMA_URL est utilisée (une valeur qui n'est pas une URL est ignorée). |
SETTINGS_ENCRYPTION_KEY | Obligatoire pour enregistrer une valeur sensible : clé de chiffrement AES-256 (openssl rand -hex 32). À conserver précieusement : sans elle, les valeurs chiffrées sont illisibles. |
SETTINGS_API_KEYS | Clé(s) d'accès à l'API /api/settings, séparées par des virgules (openssl rand -hex 32). Sans clé, l'API est désactivée (503). |
ONESTOCK_ALLOWED_HOSTS | Optionnel : hôtes acceptés comme racine de l'API depuis le parent_url du contexte (suffixes séparés par des virgules, défaut onestock-retail.com). |
ONESTOCK_ENVIRONMENT | Optionnel : environnement des settings onestock_api_root / onestock_token à utiliser (sinon « qualif » si l'URL parente le contient, ou le seul environnement disponible, ou prod). |
ONESTOCK_API_VERSION | Optionnel : version ajoutée à onestock_api_root (ex. v3). Par défaut l'URL est utilisée telle quelle. |
NEON_API_KEY | Optionnel : clé API Neon, pour proposer aussi dans l'onglet Copie de base les bases Neon non reliées au projet (NEON_ORG_ID en plus avec une clé personnelle). |
ADMIN_PASSWORD | Optionnel : authentification basique sur toutes les pages (peu adaptée à l'affichage en iframe). |
npm install
npm run db:migrate
npm run dev
onestock_token depuis une autre applicationLes valeurs sensibles sont stockées chiffrées (enc:v1:…). Une autre application qui lit la même table settings doit les déchiffrer avec la même clé :
SETTINGS_ENCRYPTION_KEY avec ses projets Vercel (variable d'environnement partagée de l'équipe, reliée à chaque projet).shared/settings-secrets.mjs (autonome, sans dépendance) et appeler decryptSetting(row.value) sur la valeur lue. Une valeur encore en clair est renvoyée telle quelle : le module peut être déployé avant la migration.npm run secrets:encrypt.API REST pour lire et modifier la table settings depuis d'autres applications. Authentification par clé : Authorization: Bearer <clé> (ou en-tête x-api-key), clés définies dans SETTINGS_API_KEYS. Chaque appel est journalisé dans api_logs (API « Settings API », valeurs sensibles masquées).
Un setting est identifié par key + site_id (vide = global) + extension_id + environment. Les clés sensibles (onestock_token…) sont chiffrées automatiquement à l'écriture ; à la lecture leur value vaut null sauf avec ?decrypt=1. En lecture, ?parent_url=<parent_url du contexte> fait renvoyer pour onestock_api_root l'URL d'API utilisée pour ce parent_url, et pour onestock_token uniquement le token de ce parent_url (champ from_context: true ; 404 s'il n'est pas saisi ; dans la liste, les tokens des autres parent_url sont écartés). Avec parent_url, site_id / extension_id / environment sont inutiles pour lire onestock_token.
| Méthode | URL | Rôle |
|---|---|---|
GET | /api/settings?key=&site_id=&extension_id=&environment=&scope=&limit=&decrypt=1 | Liste (filtres facultatifs ; site_id= vide → globaux) |
POST | /api/settings | Crée un setting (objet) ou plusieurs (tableau, 500 max) — 409 s'il existe |
GET | /api/settings/item?key=…&site_id=…&extension_id=…&environment=… | Lit un setting — 404 s'il n'existe pas |
PUT | /api/settings/item?key=…&site_id=…&extension_id=…&environment=… | Met à jour { value?, scope? } — 404 s'il n'existe pas, sauf &upsert=1 (création) |
DELETE | /api/settings/item?key=…&site_id=…&extension_id=…&environment=… | Supprime un setting |
GET | /api/settings/token?parent_url=…&api_url=…&decrypt=1 | Token OneStock de ce parent_url et URL de l'API à appeler avec : { parent_url, api_root, token, encrypted, updated_at } (&site_id= pour une URL d'API propre au site) (token null sans decrypt=1) — 404 s'il n'est pas saisi, 400 si le parent_url n'est pas un hôte OneStock autorisé |
PUT | /api/settings/token?parent_url=… | Enregistre le token (chiffré) et/ou l'URL de l'API de ce parent_url : { "token": "…", "api_root": "https://api-qualif.onestock-retail.com" } |
API=https://<app>.vercel.app/api/settings
AUTH="Authorization: Bearer $SETTINGS_API_KEY"
# Lire les settings globaux d'un site
curl -H "$AUTH" "$API?site_id=o0057&extension_id=*&environment=qualif"
# Créer
curl -H "$AUTH" -H 'content-type: application/json' -X POST "$API" \
-d '{"key":"default_lang","value":"fr","site_id":"o0057","extension_id":"*","environment":"qualif"}'
# Token OneStock du parent_url du contexte (déchiffré)
curl -H "$AUTH" "$API/token?parent_url=https%3A%2F%2Fadmin.eu1.qualif.onestock-retail.com&decrypt=1"
# Lire onestock_token déchiffré (par environnement, hors contexte)
curl -H "$AUTH" "$API/item?key=onestock_token&site_id=&extension_id=*&environment=qualif&decrypt=1"
# Mettre à jour (ou créer avec upsert=1)
curl -H "$AUTH" -H 'content-type: application/json' -X PUT \
"$API/item?key=default_lang&site_id=o0057&extension_id=*&environment=qualif" -d '{"value":"en"}'
# Supprimer
curl -H "$AUTH" -X DELETE "$API/item?key=default_lang&site_id=o0057&extension_id=*&environment=qualif"
Réponses : { "settings": [...], "count": n } (liste), { "setting": {...} } (un setting), { "deleted": true } ; erreurs { "error": "…" } avec 400 (champ manquant, JSON invalide), 401 (clé absente ou invalide), 404, 409 (déjà existant), 500 (encryption_key_missing si SETTINGS_ENCRYPTION_KEY manque pour une clé sensible), 503 (API désactivée). Un setting renvoyé contient key, site_id, extension_id, environment, scope, value, updated_at, secret, encrypted (et decrypt_error si le déchiffrement a échoué).
Depuis l'application : onglet Copie de base. Saisir le nom de la base d'origine et celui de la base de destination (l'autocomplétion propose les bases disponibles), choisir le mode, Analyser (tables et nombre de lignes, rien n'est écrit), cocher les tables, recopier le nom de la base de destination pour confirmer, Copier. Les URL ne sont ni affichées ni enregistrées (Logs API, « Copie de base », indique les noms et hôtes).
Bases proposées, sans configuration :
PROD_DATABASE_URL_UNPOOLED…) ; ce préfixe devient le nom (prod), la base de l'application (sans préfixe) s'appelle app ;NEON_API_KEY est définie (clé créée dans la console Neon → Organization settings → API keys ; NEON_ORG_ID en plus avec une clé personnelle) ;Une copie volumineuse est limitée par la durée maximale d'une fonction Vercel (5 min) ; au-delà, utiliser le script ci-dessous.
En ligne de commande : npm run db:copy copie les tables d'une base Postgres (Neon / Vercel) vers une autre, sans pg_dump.
SOURCE_DATABASE_URL="postgresql://…@ep-source….neon.tech/neondb?sslmode=require" \
TARGET_DATABASE_URL="postgresql://…@ep-cible….neon.tech/neondb?sslmode=require" \
npm run db:copy -- --dry-run # voir ce qui sera copié, sans rien écrire
npm run db:copy # copier (confirmation demandée : taper COPIER)
| Option | Effet |
|---|---|
--tables=settings,extensions | Tables à copier (défaut : toutes les tables du schéma) |
--mode=replace | Défaut : vide les tables de la cible puis les remplit |
--mode=append | Ajoute les lignes, garde celles déjà présentes dans la cible (même clé) |
--no-create | Ne crée pas les tables absentes de la cible |
--schema=public | Schéma à copier |
--dry-run / --yes | Simulation / pas de confirmation |
-pooler, variable DATABASE_URL_UNPOOLED sur Vercel) ; les passer en variables d'environnement, jamais dans un fichier commité.scope) sont recalculées par la cible ; les séquences (serial, identity) sont recalées sur la valeur maximale copiée.onestock_token…) sont copiées telles quelles : l'application branchée sur la cible doit avoir la même SETTINGS_ENCRYPTION_KEY.