docs: README à jour avec screenshot
2 changed files +91 −150
modified
README.md
+91 −150
@@ -12,8 +12,8 @@ | ||
| 12 | 12 |  |
| 13 | 13 |  |
| 14 | 14 | |
| 15 | − | |
| 16 | − | |
| 15 | + | |
| 16 | + | |
| 17 | 17 |  |
| 18 | 18 |  |
| 19 | 19 |  |
@@ -24,15 +24,33 @@ | ||
| 24 | 24 |  |
| 25 | 25 |  |
| 26 | 26 | |
| 27 | −*Agrégateur de découverte des produits vendus en ligne par les entreprises du Québec — | |
| 28 | −chaque produit renvoie vers la boutique d'origine. Aucune vente sur Fabri-Ka : | |
| 29 | −les prix, photos et stocks appartiennent aux marchands sources.* | |
| 30 | − | |
| 31 | 27 | </div> |
| 32 | 28 | |
| 29 | + | |
| 30 | + | |
| 33 | 31 | --- |
| 34 | 32 | |
| 35 | −## Aperçu | |
| 33 | +## Description | |
| 34 | + | |
| 35 | +**Fabri-Ka** est le **répertoire des produits québécois** : un agrégateur de découverte qui recense **291 557 produits** provenant de **1 265 boutiques en ligne** du Québec, couvrant les **17 régions administratives** de la province. Chaque produit renvoie vers la **boutique d'origine** — **aucune vente sur Fabri-Ka** : les prix, photos et stocks appartiennent aux marchands sources. | |
| 36 | + | |
| 37 | +Acheter québécois demande de connaître des centaines de boutiques dispersées — érablières, microbrasseries, savonneries, ébénistes, torréfacteurs, fermes… **Fabri-Ka retourne le problème** : un pipeline de découverte recense les boutiques en ligne du Québec (**3 224 boutiques vérifiées** au registre), des connecteurs par *plateforme* aspirent leurs catalogues publics complets, et un **moteur de recherche unique** les rend trouvables — filtrés par **catégorie**, **région** et **origine de fabrication** (classes **A–E**). | |
| 38 | + | |
| 39 | +> Contrairement à un agrégateur classique (1 connecteur / site), Fabri-Ka utilise **1 connecteur / plateforme ecommerce** : le même code **Shopify** sert des centaines de boutiques, piloté par le registre `data/stores.json` produit par la recherche. | |
| 40 | + | |
| 41 | +## Fonctionnalités | |
| 42 | + | |
| 43 | +- **Recherche de produits** — plein-texte **FTS5 accent-insensible** sur **291 557 produits**, filtres **catégorie / région / origine / prix**, tri, pagination, fiches riches (galerie, boutique, produits liés). | |
| 44 | +- **Répertoire de boutiques** — **1 265 boutiques** avec produits (sur **3 224 vérifiées**), fiches enrichies : logo, région, plateforme, **stats de prix**, catégories, boutiques similaires. | |
| 45 | +- **Statistiques** — tableau de bord analytique `/stats` (module commun **ka-stats**) : totaux, médianes, histogrammes, ventilation régionale, croissance — et **rapport de marché PDF** généré à la volée (`/api/report.pdf`, moteur **kapdf**). | |
| 46 | +- **Catégories** — taxonomie de **20 catégories** (mode 38 k · bijoux 34 k · épicerie fine 29 k · …) avec **~350 pages programmatiques SEO** (`/categorie/erable`, `/region/monteregie`, combinaisons non vides). | |
| 47 | +- **Classification d'origine A–E** — chaque boutique justifiée par une **évidence conservée** (certifications *Aliments du Québec* / *Les Produits du Québec*, associations, wording du site). | |
| 48 | +- **SEO / SSR** — **292 000+ pages indexables** : HTML complet dès la 1ʳᵉ requête, **JSON-LD** schema.org, **sitemaps** exhaustifs, 301/404/410 propres (détails plus bas). | |
| 49 | +- **KA ID (SSO)** + favoris **« Mon univers Ka »** synchronisés au hub **groupe-ka.com**. | |
| 50 | +- **Widget KA Agent** — bulle de chat Groupe KA (`ka-agent.js`). | |
| 51 | +- **Mobile-first** — barre d'onglets, filtres en bottom sheet, galeries swipe, CTA collant, PWA-lite, **fr-CA** d'abord. | |
| 52 | + | |
| 53 | +### Galerie | |
| 36 | 54 | |
| 37 | 55 | | Accueil | Fiche produit | |
| 38 | 56 | |---|---| |
@@ -51,22 +69,20 @@ les prix, photos et stocks appartiennent aux marchands sources.* | ||
| 51 | 69 | |
| 52 | 70 | <img src="docs/screenshots/mobile-produit.png" width="240" alt="Fiche produit mobile"/> |
| 53 | 71 | </p> |
| 54 | −<p align="center"><i>Mobile-first : barre d'onglets, filtres en bottom sheet, galeries swipe, CTA collant, favoris ♥ synchronisés au hub Groupe KA.</i></p> | |
| 55 | 72 | |
| 56 | −## Pourquoi Fabri-Ka ? | |
| 73 | +## Stack technique | |
| 57 | 74 | |
| 58 | −Acheter québécois demande de connaître des centaines de boutiques dispersées — | |
| 59 | −érablières, microbrasseries, savonneries, ébénistes, torréfacteurs, fermes… | |
| 60 | −**Fabri-Ka retourne le problème** : un pipeline de découverte recense les boutiques | |
| 61 | −en ligne du Québec, des connecteurs par *plateforme* aspirent leurs catalogues | |
| 62 | −publics complets, et un moteur de recherche unique les rend trouvables — filtrés | |
| 63 | −par catégorie, région et origine de fabrication (classes A–E). | |
| 64 | − | |
| 65 | −> Contrairement à un agrégateur classique (1 connecteur / site), Fabri-Ka utilise | |
| 66 | −> **1 connecteur / plateforme ecommerce** : le même code Shopify sert des centaines | |
| 67 | −> de boutiques, piloté par le registre `data/stores.json` produit par la recherche. | |
| 75 | +| Couche | Technologies | | |
| 76 | +|---|---| | |
| 77 | +| **Backend** | **Python 3.14** · **FastAPI** (API + SSR SEO + service du frontend) | | |
| 78 | +| **Base de données** | **SQLite + FTS5** — upsert par **hash de contenu**, retraits avec délai de grâce (2 syncs) | | |
| 79 | +| **Frontend** | **React 18 + Vite + TypeScript** — 9 pages, identité famille KA « éditorial sharp » (Space Grotesk + Inter + JetBrains Mono, palette crème/terracotta/pin/miel), tokens **ka-ui** | | |
| 80 | +| **Connecteurs** | **Shopify** `products.json` (transport curl anti-429 TLS) · **WooCommerce** Store API · **Wix Stores** GraphQL · **Squarespace** `?format=json` · **connecteur générique** (sitemap → JSON-LD/microdata/OG : PrestaShop, Lightspeed, Snipcart, Magento, sites custom) · **Scrapfly/Firecrawl** en secours anti-bot | | |
| 81 | +| **Découverte** | **35+ sources** : sitemaps d'annuaires, API cachées de cartes (wpgmza, FacetWP), pagination htmx, sessions **Firecrawl** pour répertoires JS | | |
| 82 | +| **Comptes** | **KA ID** SSO (JWT HS256, stdlib pure) · favoris HMAC-SHA256 vers le hub central | | |
| 83 | +| **Prod** | **PM2** + **ngrok** sur le cluster **MacLustr** | | |
| 68 | 84 | |
| 69 | −## L'architecture en 30 secondes | |
| 85 | +### Architecture en 30 secondes | |
| 70 | 86 | |
| 71 | 87 | ```mermaid |
| 72 | 88 | flowchart LR |
@@ -87,26 +103,9 @@ flowchart LR | ||
| 87 | 103 | F --> U["🍁 Acheteur local"] |
| 88 | 104 | ``` |
| 89 | 105 | |
| 90 | −| Couche | Rôle | Fichiers | | |
| 91 | −|---|---|---| | |
| 92 | −| **Découverte** | Sitemaps d'annuaires, API cachées de cartes (wpgmza, FacetWP), pagination htmx, résolution de redirections affiliées, sessions Firecrawl pour répertoires JS (CMAQ) | `scripts/harvest.py`, `scripts/cmaq_firecrawl.py` | | |
| 93 | −| **Vérification** | Plateforme ecommerce + test réel de l'endpoint catalogue + signaux de localisation QC (codes postaux, indicatifs, TLD, wording) | `scripts/verify.py` | | |
| 94 | −| **Classification** | Origine A–E avec évidence conservée (certifications → a priori ; wording du site → ajustement) | `scripts/build_registry.py` | | |
| 95 | −| **Connecteurs** | Shopify `products.json` (transport curl anti-429 TLS), WooCommerce Store API, Wix Stores GraphQL (jeton public), Squarespace `?format=json`, **connecteur générique** (sitemap → JSON-LD/microdata/OG sur PrestaShop, Lightspeed, Snipcart, Magento, sites custom), Scrapfly/Firecrawl en secours anti-bot | `fabrika/connectors/` | | |
| 96 | −| **Diff engine** | Upsert par hash de contenu, retraits avec délai de grâce (2 syncs), FTS5 accent-insensible | `fabrika/db.py` | | |
| 97 | −| **API** | Recherche FTS5, facettes, filtres catégorie/région/origine/prix, stats, rapport PDF, sync | `fabrika/web.py` | | |
| 98 | −| **SEO / SSR** | HTML complet dès la 1ʳᵉ requête sur toutes les routes, pages programmatiques, JSON-LD, sitemaps, 301/404/410 | `fabrika/seo.py` | | |
| 99 | −| **Comptes** | « Se connecter avec KA ID » (SSO du Groupe KA), favoris « Mon univers Ka » poussés au hub central | `fabrika/auth.py`, `fabrika/hubfav.py`, `fabrika/hubprofile.py` | | |
| 100 | −| **Frontend** | Identité famille KA « éditorial sharp » (cf. Food-Ka/Lou-Ka) — Space Grotesk + Inter + JetBrains Mono, bordures encre, ombres décalées, palette crème/terracotta/pin/miel, PWA-lite, fr-CA | `frontend/` | | |
| 101 | − | |
| 102 | −## SEO & rendu serveur — 292 000+ pages indexables | |
| 103 | − | |
| 104 | −Le site est une SPA React, mais **aucun moteur de recherche ne voit une page vide** : | |
| 105 | −le catch-all FastAPI (`fabrika/seo.py`) injecte dans le HTML du build Vite, pour | |
| 106 | −chaque route, un `<head>` complet **et le contenu essentiel de la page dans `#root`** | |
| 107 | −— React remplace ce contenu au montage (hydratation), le dynamique reste intact. | |
| 106 | +## SEO & rendu serveur — **292 000+ pages indexables** | |
| 108 | 107 | |
| 109 | −**Ce que reçoit un robot (et un visiteur sans JavaScript) :** | |
| 108 | +Le site est une SPA React, mais **aucun moteur de recherche ne voit une page vide** : le catch-all FastAPI (`fabrika/seo.py`) injecte dans le HTML du build Vite, pour chaque route, un `<head>` complet **et le contenu essentiel de la page dans `#root`** — React remplace ce contenu au montage (hydratation). | |
| 110 | 109 | |
| 111 | 110 | | Élément | Détail | |
| 112 | 111 | |---|---| |
@@ -116,87 +115,59 @@ chaque route, un `<head>` complet **et le contenu essentiel de la page dans `#ro | ||
| 116 | 115 | | **JSON-LD schema.org** | `Product` + `Offer` (prix CAD, dispo, vendeur), `BreadcrumbList`, `OnlineStore`, `CollectionPage` + `ItemList`, `WebSite` + `SearchAction` | |
| 117 | 116 | | **Contenu HTML complet** | H1, stats, description, galerie (lazy + dimensions), grille de produits, maillage interne | |
| 118 | 117 | |
| 119 | −**Pages programmatiques** — une URL propre par recherche type : | |
| 120 | − | |
| 121 | −``` | |
| 122 | −/categorie/erable → Érable & miel du Québec (6 300+ produits, prix moyen/médian) | |
| 123 | −/region/monteregie → Produits québécois — Montérégie | |
| 124 | −/categorie/erable/region/estrie → Érable & miel — Estrie | |
| 125 | −``` | |
| 126 | − | |
| 127 | −~350 pages (20 catégories × 17 régions + combinaisons non vides), chacune avec H1, | |
| 128 | −statistiques (nombre de produits, **prix moyen et médian**, fourchette), 24 produits | |
| 129 | −en HTML, pagination crawlable et liens vers les pages voisines. Les routes React | |
| 130 | −miroirs gardent l'expérience SPA (filtres verrouillés sur la dimension de l'URL). | |
| 131 | − | |
| 132 | −**URLs & statuts propres :** | |
| 133 | − | |
| 134 | −``` | |
| 135 | −/produits/sirop-d-erable-edition-limitee-…-b63130ffc8b18c8cc650 ← slug lisible + uid | |
| 136 | −/produits/b63130ffc8b18c8cc650 → 301 vers l'URL slugée | |
| 137 | −produit retiré du catalogue → 410 Gone + lien catégorie | |
| 138 | −uid inconnu → 404 (fini le soft-404) | |
| 139 | −``` | |
| 140 | − | |
| 141 | −**Sitemaps & robots :** `/robots.txt` (bloque `/api/`, `/profil`) → `/sitemap.xml` | |
| 142 | −(index) → `sitemap-pages.xml` + `sitemap-boutiques.xml` + `sitemap-produits-{1..8}.xml` | |
| 143 | −(40 000 URLs/chunk, `lastmod` réel, régénérés automatiquement, cache 1 h). | |
| 118 | +**URLs & statuts propres** : slug lisible + uid (`/produits/sirop-d-erable-…-b63130ffc8b18c8cc650`), **301** de l'uid nu vers l'URL slugée, **410 Gone** pour les produits retirés, **404** pour les uid inconnus — fini le soft-404. | |
| 144 | 119 | |
| 145 | −**Performance :** GZip (‑80 % sur le HTML), `Cache-Control: immutable` sur les assets | |
| 146 | −hashés, images `loading="lazy"` avec dimensions explicites, polices en `swap`. | |
| 120 | +**Sitemaps & robots** : `/robots.txt` → `/sitemap.xml` (index) → `sitemap-pages.xml` + `sitemap-boutiques.xml` + `sitemap-produits-{1..8}.xml` (**40 000 URLs/chunk**, `lastmod` réel, régénérés automatiquement, cache 1 h). | |
| 147 | 121 | |
| 148 | −<details> | |
| 149 | −<summary><b>Vérifier soi-même (curl)</b></summary> | |
| 122 | +**Performance** : **GZip** (‑80 % sur le HTML), `Cache-Control: immutable` sur les assets hashés, images `loading="lazy"` avec dimensions explicites, polices en `swap`. | |
| 150 | 123 | |
| 151 | −```bash | |
| 152 | −curl -s https://www.fabri-ka.com/categorie/erable | grep -o '<h1>[^<]*</h1>' | |
| 153 | −# <h1>Érable & miel du Québec</h1> | |
| 154 | − | |
| 155 | −curl -s https://www.fabri-ka.com/produits/b63130ffc8b18c8cc650 -o /dev/null -w '%{http_code} %{redirect_url}\n' | |
| 156 | −# 301 https://www.fabri-ka.com/produits/sirop-d-erable-edition-limitee-… | |
| 124 | +## API | |
| 157 | 125 | |
| 158 | −curl -s https://www.fabri-ka.com/sitemap.xml | grep -c '<sitemap>' | |
| 159 | −# 10 | |
| 160 | −``` | |
| 161 | −</details> | |
| 126 | +| Endpoint | Description | | |
| 127 | +|---|---| | |
| 128 | +| `GET /api/products?q=&category=®ion=&origin=&price_min=&price_max=&sort=&page=` | Recherche plein-texte + filtres, triée | | |
| 129 | +| `GET /api/products/{uid}` | Fiche produit (galerie, boutique, produits liés) | | |
| 130 | +| `GET /api/stores?region=&q=&with_products=` | Répertoire des boutiques (logo, région, plateforme) | | |
| 131 | +| `GET /api/stores/{id}` | Fiche boutique enrichie : stats de prix, catégories, boutiques similaires | | |
| 132 | +| `GET /api/facets` | Catégories/régions/origines avec compteurs | | |
| 133 | +| `GET /api/stats` · `GET /api/stats/extended` | Totaux, ventilation régionale · stats riches (médianes, histogrammes, croissance) | | |
| 134 | +| `GET /api/report.pdf` | Rapport de marché PDF — généré à la volée | | |
| 135 | +| `POST /api/sync` | Déclenche une synchronisation en arrière-plan | | |
| 136 | +| `GET /api/auth/ka/login` → `…/callback` · `GET /api/auth/me` · `POST /api/auth/logout` | SSO **KA ID** | | |
| 137 | +| `GET /api/favorites` · `POST /api/favorites/toggle` | Favoris « Mon univers Ka » (hub central) | | |
| 138 | +| `GET /robots.txt` · `GET /sitemap.xml` (+ sous-sitemaps) | Infrastructure SEO | | |
| 162 | 139 | |
| 163 | −## Écosystème Groupe KA | |
| 140 | +## Classification d'origine | |
| 164 | 141 | |
| 165 | −Fabri-Ka fait partie du **Groupe KA** (immo-ka, ora-ka, …) et partage son | |
| 166 | −infrastructure de comptes : | |
| 142 | +| Classe | Signification | | |
| 143 | +|---|---| | |
| 144 | +| **A** | Fabriqué / cultivé / transformé au Québec | | |
| 145 | +| **B** | Conçu au Québec, fabriqué ailleurs | | |
| 146 | +| **C** | Détaillant québécois (revente) | | |
| 147 | +| **D** | Mixte | | |
| 148 | +| **E** | Incertain — à vérifier | | |
| 167 | 149 | |
| 168 | −- **KA ID (SSO)** — « Se connecter avec KA ID » : flot `authorize → callback` avec | |
| 169 | − JWT HS256 signé d'un secret partagé, vérifié en stdlib pure (hmac/hashlib — | |
| 170 | − aucune dépendance), session locale en cookie httpOnly signé 30 jours. | |
| 171 | − Le même `ka_id` identifie le membre sur toutes les plateformes du groupe. | |
| 172 | −- **Favoris « Mon univers Ka »** — aucun stockage local : chaque ♥ est poussé/lu | |
| 173 | − au hub central (groupe-ka.com) via requêtes signées HMAC-SHA256, cache 30 s, | |
| 174 | − invalidation au toggle. Le hub est l'unique source de vérité. | |
| 175 | −- **Profil** — la page `/profil` affiche le profil Groupe KA (bio, ville, réseaux), | |
| 176 | − source de vérité : le hub. | |
| 150 | +Chaque classe est justifiée par une **évidence conservée** — jamais déduite du seul fait qu'une entreprise « semble » québécoise. | |
| 177 | 151 | |
| 178 | −## Chiffres (2026-08-13) | |
| 152 | +## Chiffres (2026-08-17, site live) | |
| 179 | 153 | |
| 180 | 154 | | Mesure | Valeur | |
| 181 | 155 | |---|---| |
| 182 | −| Domaines candidats découverts et dédupliqués | 3 481 | | |
| 156 | +| Produits agrégés (catalogues complets) | **291 557** | | |
| 157 | +| Boutiques avec produits en ligne | **1 265** | | |
| 158 | +| Régions administratives couvertes | **17 / 17** | | |
| 183 | 159 | | Boutiques québécoises actives vérifiées (registre) | **3 224** | |
| 184 | 160 | | — avec ecommerce détecté | 1 970+ | |
| 185 | −| Produits agrégés (catalogues complets) | **291 017** | | |
| 186 | −| — avec prix | 285 316 (médiane 29,95 $ · moyenne 297 $) | | |
| 187 | −| Boutiques avec produits en ligne | **1 266** | | |
| 188 | −| Régions administratives couvertes | **17 / 17** | | |
| 189 | −| Catégories | 20 (mode 38 k · bijoux 34 k · épicerie fine 29 k · …) | | |
| 161 | +| Domaines candidats découverts et dédupliqués | 3 481 | | |
| 162 | +| Catégories | **20** (mode 38 k · bijoux 34 k · épicerie fine 29 k · …) | | |
| 190 | 163 | | Pages indexables (produits + boutiques + programmatiques) | **292 000+** | |
| 191 | 164 | |
| 192 | −Détails, méthodologie, taux de découverte marginal et angles morts : | |
| 193 | −[`deliverables/research_report.md`](deliverables/research_report.md) · | |
| 194 | −stratégie de découverte continue : [`deliverables/discovery_strategy.md`](deliverables/discovery_strategy.md). | |
| 165 | +Détails et méthodologie : [`deliverables/research_report.md`](deliverables/research_report.md) · stratégie de découverte continue : [`deliverables/discovery_strategy.md`](deliverables/discovery_strategy.md). | |
| 195 | 166 | |
| 196 | 167 | ## Structure du projet |
| 197 | 168 | |
| 198 | 169 | ``` |
| 199 | −qc-store/ | |
| 170 | +fabri-ka/ | |
| 200 | 171 | ├── fabrika/ # plateforme |
| 201 | 172 | │ ├── web.py # API FastAPI + service du frontend + routes SEO |
| 202 | 173 | │ ├── seo.py # rendu serveur : meta, JSON-LD, sitemaps, 301/404/410 |
@@ -212,11 +183,11 @@ qc-store/ | ||
| 212 | 183 | ├── scripts/ # pipeline de découverte (harvest → verify → registry) |
| 213 | 184 | ├── data/ # stores.json (registre) · fabrika.db · caches (non versionnés) |
| 214 | 185 | ├── deliverables/ # rapport de recherche, CSV des boutiques, sources |
| 215 | −├── docs/screenshots/ # captures de ce README | |
| 186 | +├── docs/ # captures d'écran de ce README | |
| 216 | 187 | └── run.py # CLI : sync · serve · watch |
| 217 | 188 | ``` |
| 218 | 189 | |
| 219 | −## Démarrage rapide | |
| 190 | +## Démarrage local | |
| 220 | 191 | |
| 221 | 192 | ```bash |
| 222 | 193 | git clone https://git.spboucher.ai/fabri-ka.git && cd fabri-ka |
@@ -241,39 +212,11 @@ EOF | ||
| 241 | 212 | .venv/bin/python run.py watch 360 # resynchronisation en boucle (minutes) |
| 242 | 213 | ``` |
| 243 | 214 | |
| 244 | −## API | |
| 215 | +## Déploiement | |
| 245 | 216 | |
| 246 | −| Endpoint | Description | | |
| 247 | −|---|---| | |
| 248 | −| `GET /api/products?q=&category=®ion=&origin=&price_min=&price_max=&sort=&page=` | Recherche plein-texte + filtres, triée | | |
| 249 | −| `GET /api/products/{uid}` | Fiche produit (galerie, boutique, produits liés) | | |
| 250 | −| `GET /api/stores?region=&q=&with_products=` | Répertoire des boutiques (logo, région, plateforme) | | |
| 251 | −| `GET /api/stores/{id}` | Fiche boutique enrichie : stats de prix, catégories, boutiques similaires | | |
| 252 | −| `GET /api/facets` | Catégories/régions/origines avec compteurs | | |
| 253 | −| `GET /api/stats` · `GET /api/stats/extended` | Totaux, ventilation régionale · stats riches (médianes, histogrammes, croissance) | | |
| 254 | −| `GET /api/report.pdf` | Rapport de marché PDF — généré à la volée | | |
| 255 | −| `POST /api/sync` | Déclenche une synchronisation en arrière-plan | | |
| 256 | −| `GET /api/auth/ka/login` → `…/callback` · `GET /api/auth/me` · `POST /api/auth/logout` | SSO KA ID | | |
| 257 | −| `GET /api/favorites` · `POST /api/favorites/toggle` | Favoris « Mon univers Ka » (hub central) | | |
| 258 | −| `GET /robots.txt` · `GET /sitemap.xml` (+ sous-sitemaps) | Infrastructure SEO | | |
| 217 | +En production sur le **nœud M4M64a** du cluster **MacLustr** (répertoire `~/fabri-ka`), servi sur le **port 8097** et exposé via **ngrok** sur **[www.fabri-ka.com](https://www.fabri-ka.com)**. | |
| 259 | 218 | |
| 260 | −## Classification d'origine | |
| 261 | − | |
| 262 | −| Classe | Signification | | |
| 263 | −|---|---| | |
| 264 | −| **A** | Fabriqué / cultivé / transformé au Québec | | |
| 265 | −| **B** | Conçu au Québec, fabriqué ailleurs | | |
| 266 | −| **C** | Détaillant québécois (revente) | | |
| 267 | −| **D** | Mixte | | |
| 268 | −| **E** | Incertain — à vérifier | | |
| 269 | − | |
| 270 | −Chaque classe est justifiée par une **évidence conservée** (certification Aliments | |
| 271 | −du Québec / Les Produits du Québec, adhésion à une association de producteurs, | |
| 272 | −wording du site) — jamais déduite du seul fait qu'une entreprise « semble » québécoise. | |
| 273 | − | |
| 274 | −## Production | |
| 275 | − | |
| 276 | −Déployé sous **PM2** (3 processus) derrière **ngrok** sur un nœud du cluster MacLustr : | |
| 219 | +**3 processus PM2 :** | |
| 277 | 220 | |
| 278 | 221 | ``` |
| 279 | 222 | fabri-ka-web .venv/bin/python run.py serve 8097 # API + frontend + SSR SEO |
@@ -281,35 +224,33 @@ fabri-ka-sync .venv/bin/python run.py watch 360 # resync aux 6 h | ||
| 281 | 224 | fabri-ka-ngrok ngrok http --url=www.fabri-ka.com 8097 # tunnel |
| 282 | 225 | ``` |
| 283 | 226 | |
| 284 | −Redéploiement : `rsync` du dépôt (en excluant `data/` — la prod garde sa base, | |
| 285 | −resynchronisée en continu) puis `pm2 restart fabri-ka-web`. | |
| 227 | +Redéploiement : build + **`pm2 restart fabri-ka-web`** (la prod garde sa base `data/` — resynchronisée en continu, jamais écrasée). | |
| 228 | + | |
| 229 | +## Développement remote-first (IMPORTANT) | |
| 230 | + | |
| 231 | +La **source de vérité est le repo git sur le nœud M4M64a** (`~/fabri-ka`) — **pas** une copie locale sur le laptop. Toute modification se fait **sur le nœud via SSH** : édition, build, `pm2 restart`, puis commit/push **depuis le nœud** (agent forwarding actif). | |
| 232 | + | |
| 233 | +- Remote `origin` = **spbgit** (git perso **[git.spboucher.ai](https://git.spboucher.ai)**), via l'alias SSH **`gitsrv`** : `gitsrv:srv/git/fabri-ka.git` — bare repo hébergé sur M3U96a. | |
| 234 | +- **Ne pas éditer de copies laptop** ; référence complète : `~/Desktop/cluster-skill/KA-REMOTE-DEV.md`. | |
| 286 | 235 | |
| 287 | 236 | ## Principes |
| 288 | 237 | |
| 289 | −1. **Politesse** — throttle global par plateforme (Shopify : 0,7 s/req toutes | |
| 290 | − boutiques confondues), délais entre requêtes, User-Agent identifié. | |
| 291 | −2. **Fidélité** — aucun prix inventé ; entités HTML décodées ; si la source ne | |
| 292 | − publie pas une donnée, elle est `null`. | |
| 293 | −3. **Traçabilité** — chaque produit renvoie vers la boutique d'origine ; chaque | |
| 294 | − boutique conserve sa provenance de découverte et son évidence d'origine. | |
| 295 | −4. **Robustesse** — une boutique qui casse n'affecte jamais les autres ; | |
| 296 | − journal `sync_log` avec raison d'échec par boutique. | |
| 297 | −5. **Trouvabilité** — tout ce qui existe dans la base existe pour Google : | |
| 298 | − HTML complet, données structurées, sitemaps exhaustifs, jamais de soft-404. | |
| 238 | +1. **Politesse** — throttle global par plateforme (Shopify : **0,7 s/req** toutes boutiques confondues), délais entre requêtes, User-Agent identifié. | |
| 239 | +2. **Fidélité** — aucun prix inventé ; entités HTML décodées ; si la source ne publie pas une donnée, elle est `null`. | |
| 240 | +3. **Traçabilité** — chaque produit renvoie vers la **boutique d'origine** ; chaque boutique conserve sa provenance de découverte et son évidence d'origine. | |
| 241 | +4. **Robustesse** — une boutique qui casse n'affecte jamais les autres ; journal `sync_log` avec raison d'échec par boutique. | |
| 242 | +5. **Trouvabilité** — tout ce qui existe dans la base existe pour Google : HTML complet, données structurées, sitemaps exhaustifs, jamais de soft-404. | |
| 299 | 243 | |
| 300 | 244 | --- |
| 301 | 245 | |
| 302 | 246 | <div align="center"> |
| 303 | 247 | |
| 304 | −## Auteur | |
| 305 | − | |
| 306 | 248 | **Simon-Pierre Boucher** |
| 307 | 249 | |
| 308 | 250 | [](mailto:contact@spboucher.ai) |
| 309 | 251 | [](https://git.spboucher.ai) |
| 310 | 252 | |
| 311 | −*Du recensement provincial (3 224 boutiques vérifiées, 35+ sources) au moteur de | |
| 312 | −recherche indexable en production — conçu, construit et déployé en deux jours.* | |
| 253 | +Un service **[Groupe Ka](https://www.groupe-ka.com)** | |
| 313 | 254 | |
| 314 | 255 | © 2026 Simon-Pierre Boucher — tous droits réservés. |
| 315 | 256 | |
added
docs/screenshot.png
+0 −0
Binary file not shown.