fix(produits): recherche asynchrone — la requête était coupée par le reverse proxy

« Unexpected token '<', "<html> <h"... is not valid JSON » : ce n'était pas du
JSON parce que ce n'était pas l'application qui répondait. La recherche enchaîne
deux allers-retours Qlik (dont une extraction mensuelle) et dépassait le délai du
proxy, qui renvoyait sa page d'erreur HTML.

- L'API passe en asynchrone, sur le modèle déjà éprouvé de POST /api/qlik/sync :
  POST démarre un job et rend la main immédiatement, GET renvoie l'avancement
  puis le résultat. Le client interroge toutes les 2 s et affiche l'étape en
  cours (recherche des articles / extraction des ventes / rapprochement
  catalogue). Deux recherches simultanées au maximum.
- Le client ne présume plus que la réponse est du JSON : les statuts 504, 502,
  503, 401 et 403 sont traduits en message actionnable au lieu d'une erreur de
  parsing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DfqUihgixw4K1AmJhizWiu
This commit is contained in:
Claude committed 2026-07-30 04:57:17 +00:00
1 parent 466cb53da2
commit c9ecb47cb1
4 files changed
+295 -66

No files matched your search

+10 -1
View File
@@ -36,8 +36,17 @@ ceux-là qu'on veut voir.
3. `pgGetProduitsByCodeCentrale()` — rapprochement avec le catalogue Nancy.
Un code absent = produit réseau que nous ne référençons pas (`?cc=` sur la fiche).
L'API est **asynchrone** : `POST /api/produits/search?q=…` démarre un job et rend
la main tout de suite, `GET` renvoie l'avancement puis le résultat (polling client
toutes les 2 s). Une requête HTTP maintenue pendant toute l'extraction se faisait
couper par le reverse proxy, qui répond une page HTML — le client échouait sur
« Unexpected token '<' … is not valid JSON ». Même schéma que `POST /api/qlik/sync`.
Deux recherches simultanées au maximum (le serveur Qlik sature vite).
Repli : si Qlik est injoignable, la recherche retombe sur `pgSearchProduits()` et
la réponse le signale (`source: "db"`). Résultats mis en cache mémoire 10 min.
la réponse le signale (`source: "db"`). Un résultat Qlik exploitable est mis en
cache mémoire 10 min ; un repli ne l'est pas, sinon une panne passagère resterait
figée.
Champs de l'app FF (« Magasins Vision Consolidée ») : le code est `Article Code`,
le libellé `Article` (repli `article_libelle_ticket`, qui est le libellé ticket