Architecture HallesX
La carte complète de la plateforme — pour reprendre le projet sans archéologie. Mise à jour : juillet 2026.
Vue d'ensemble
RNM/FranceAgriMer ─┐ ┌─ hallesx.com (Cloud Run, europe-west1)
Agence Bio ────────┤ scripts/* (crawlers) │ Next.js 15 standalone, ~640 pages SSG
OSM / Overpass ────┼─▶ data/*.json ────────▶│ /api/v1 ouverte (CORS)
Agreste (xlsx) ────┤ (columnar, committé) │ next/og : ~300 vignettes de partage
data.gouv (468) ───┘ └─ Firebase Auth (hallesx-prod)
Tout est statique au build quand c'est possible ; les données vivent dans le repo (data/), rafraîchies par cron GitHub Actions → commit → redéploiement. Aucune base de données serveur : la « base » est columnar JSON + le graphe en mémoire, reconstruits au build.
Données (data/ + lib/)
| Source | Script | Fichiers | Lib |
|---|---|---|---|
| Mercuriales RNM (79 marchés, 3 781 séances, 6 630 séries, 2018+) | build-rnm-db.mjs (+ discover-rnm-markets.mjs) | data/rnmdb/*.json (columnar : dates[], labels[], series) | lib/rnm.ts — getCandles, rnmCoverage, allMarketStats, latestSession, marketMovers |
| Annuaire bio (60 874 producteurs, 883 importateurs) | fetch-bio.mjs (param activite, singulier !) | data/bio/*.json | lib/bio.ts |
| Marchés de plein vent (4 077) | fetch-marches-locaux.mjs (Overpass, miroir kumi) | data/marches-locaux.json | pages /marches-locaux |
| Production régionale (18,6 Mt) | fetch-agreste.py (openpyxl + reset_dimensions, quintaux÷10) | data/agreste.json | /regions |
| HVE (25 329) · EGalim (66 135) | harvest | data/hve.json, data/acheteurs.json | graphe |
| Catalogue open data (468 jeux) + miroir 191 Mo | harvest-opendata.mjs, mirror-opendata.mjs | data/opendata-catalog.json, data/raw/** (hors Docker) | /donnees |
Graphe TAO (lib/graph.ts) : 86 101 nœuds / 86 225 arêtes typées (PRODUCED_BY, QUOTED_ON, SELLS_AT, IN_DEPT…), ids internés, adjacence in/out, mémoïsé au module. Alimente /graphe (carte à bulles canvas) et l'API.
Marché simulé au-dessus du réel (lib/market.ts) : cotes par produit seedées (mulberry32) et recalées sur les prix RNM réels quand la série existe (lib/rnm.ts findMatch : mot entier + famille marée/viande/f&l).
Pages (app/)
/home (héros + KPIs + movers + halles + partenaires) ·/marche(la cote, recherche XL) ·/produit/[slug](chandelles OHLC temps réel-gap, carnet d'ordres deux sens, achat/vente) ·/mercuriale/[slug](79) ·/marches,/marches-locaux,/halles(+slug),/regions,/graphe,/donnees/producteurs/annuaire(+[ref])— les 60 874, partenaires épinglés « ✓ Validée »/pour+ 52 landings (lib/landings.ts: pains/features/KPIs par audience)/pro(stock photo→IA, prévisions, commandes) ·/pro/ferme(jeu 3D Babylon, Norbert l'assistant, création sur adresse BAN réelle uniquement) ·/pro/factures(factures conformes, PDP stublib/pdp.ts, parrainage)/compte— auth Firebase (voir plus bas) ·/admin— console (état, pack d'images + prompts, docs) ·/api/v1/*— API publique CORS (produits, cotes, mercuriales, graphe)- SEO :
sitemap.tscomplet,robots.txt,metadataBase, ~300 OG dynamiques (opengraph-image.tsx: racine, landings, produits, halles, mercuriales)
Auth (Firebase — projet GCP hallesx-prod)
- Fournisseurs : Google (client OAuth auto-créé), téléphone/SMS (reCAPTCHA invisible), e-mail/mot de passe, anonyme. Domaines autorisés : hallesx.com, www, localhost.
lib/firebaseClient.ts(config publique) ·components/AuthPanel.tsx— flux identifier-first (l'e-mail détecte compte existant/nouveau ;emailPrivacyConfigdésactivé côté Identity Platform pour le permettre).- Header : avatar vivant (import Firebase différé post-montage dans
Nav.tsx). - Pas encore de session serveur : l'auth est client-side ; brancher les server actions sur le token (Admin SDK) est l'étape suivante naturelle.
Design system (app/globals.css)
Crème #F7F4EC · vert jardin #17803D / feuille #7CB944 · terracotta #D96B3F · miel #F6B93B · charbon #211F1A. Fraunces (display) + DM Sans via next/font. Mobile : overflow-x: clip global, tables→cartes ≤800 px (data-th), tabbar avec safe-area-inset. Dataviz : chandelles vert/rouge sur surface claire, axe X proportionnel au temps (les trous saisonniers se voient), ≤64 bougies agrégées.
Infra & déploiement
- Repo : github.com/Bros-AI/hallesx (privé) · commits conventionnels FR
- CI (
ci.yml) :tsc --noEmit+next buildsur chaque push - Deploy (
deploy.yml) : Docker multi-stage (standalone, non-root, port 8080,--build-arg GIT_SHA, copiepublic/etdata/) → Artifact Registry → Cloud Runeurope-west1(1 Gi, 0→3 instances). Auth keyless (WIF : poolgithub, conditionassertion.repository=='Bros-AI/hallesx') - Data refresh (
refresh-data.yml) : RNM 22 h 30 quotidien, bio dimanche 23 h 30 → commitdata/→ redéclenche le deploy - Domaine : hallesx.com via Cloudflare (zone importée, enregistrements gris/DNS only — TLS géré par Google), mapping Cloud Run
- Cache : HTML
max-age=0, must-revalidate·/_next/staticimmuable 1 an ·/img30 j + SWR · OG 24 h + SWR ·generateBuildId = GIT_SHA, version visible au footer - Images : pack Nano Banana 122 images bi-style généré une fois puis committé (voir IMAGES.md) ; OG rendues au build ; coût récurrent 0 €
Conventions & pièges connus
- Heredocs bash + texte français = casse silencieuse → écrire un patch
.pydans le scratchpad et l'exécuter (python patch.py). - Agence Bio : le paramètre est
activite(singulier) —activitesest ignoré sans erreur. - RNM : pages UTF-8 (ne pas décoder latin1) ; dates quotidiennes
du JJ/MM/AAET hebdodu JJ-MM-AAAA; matching produit = mot entier + famille. - satori/next-og : tout
<div>multi-enfants doit êtredisplay:flex; pas de glyphes ▲▼ (fetch de police dynamique qui échoue). - Windows dev : tuer le port 3000 avant
npm start(Get-NetTCPConnection -LocalPort 3000 | Stop-Process).
Backlog assumé
Surfaces/rendements Agreste + Douanes en couches carte · crawler des ateliers de transformation · aliasing des séries RNM renommées pré-2022 · Norbert branché sur un vrai LLM · vérification SIRET des revendications de ferme · sessions serveur (Admin SDK) · vidéos Remotion (« la semaine du marché »).