SPB Git forge

spb/fabri-ka

Public

Agrégateur de produits québécois — www.fabri-ka.com

217commits 1branches 0releases
66.1 MBsize
maindefault branch
5 h agolast push
Python 38.4% HTML 30.3% TypeScript 17.2% CSS 11% JavaScript 3.2%

docs: README v3 — chiffres live vérifiés, pastilles dynamiques, sections complètes

Simon-Pierre Boucher committed 1 mo ago (Aug 24, 2026) parent db542b0

1 changed file +160 −80

modified README.md +160 −80
@@ -1,54 +1,66 @@
1 −<div align="center">
2 −
3 −# Fabri·Ka
4 −
5 −### Tous les produits québécois. Un seul endroit.
1 +<p align="center">
2 + <a href="https://www.fabri-ka.com"><img src="https://www.fabri-ka.com/og.png" width="760" alt="Fabri·Ka — Tous les produits québécois. Un seul endroit."></a>
3 +</p>
6 4
7 −**[www.fabri-ka.com](https://www.fabri-ka.com)**
5 +<h1 align="center">Fabri·Ka</h1>
8 6
9 −![Python](https://img.shields.io/badge/Python-3.14-1E1B16?style=for-the-badge&logo=python&logoColor=C4532E)
10 −![FastAPI](https://img.shields.io/badge/FastAPI-API_+_SSR-1E1B16?style=for-the-badge&logo=fastapi&logoColor=C4532E)
11 −![React](https://img.shields.io/badge/React_18-Vite_+_TS-1E1B16?style=for-the-badge&logo=react&logoColor=C4532E)
12 −![SQLite](https://img.shields.io/badge/SQLite-FTS5-1E1B16?style=for-the-badge&logo=sqlite&logoColor=C4532E)
13 −![Firecrawl](https://img.shields.io/badge/Firecrawl-rendu_JS-1E1B16?style=for-the-badge&logoColor=C4532E)
7 +<p align="center"><b>Tous les produits québécois. Un seul endroit.</b><br><a href="https://www.fabri-ka.com">www.fabri-ka.com</a></p>
14 8
15 −![Produits](https://img.shields.io/badge/produits_agr%C3%A9g%C3%A9s-301_534-234438?style=flat-square)
16 −![Boutiques connectées](https://img.shields.io/badge/boutiques_avec_produits-1_377-234438?style=flat-square)
17 −![Registre](https://img.shields.io/badge/boutiques_QC_v%C3%A9rifi%C3%A9es-3_507-234438?style=flat-square)
18 −![Connecteurs](https://img.shields.io/badge/connecteurs_plateforme-9-234438?style=flat-square)
19 −![Régions](https://img.shields.io/badge/r%C3%A9gions_couvertes-17%2F17-234438?style=flat-square)
20 −![Sources](https://img.shields.io/badge/sources_de_d%C3%A9couverte-35%2B-234438?style=flat-square)
9 +<div align="center">
21 10
22 −![SEO](https://img.shields.io/badge/SEO-SSR_+_JSON--LD_+_sitemaps-C4532E?style=flat-square)
23 −![Pages indexables](https://img.shields.io/badge/pages_indexables-301_000%2B-C4532E?style=flat-square)
24 −![SSO](https://img.shields.io/badge/KA_ID-SSO_Groupe_KA-C4532E?style=flat-square)
25 −![Langue](https://img.shields.io/badge/fr--CA-d'abord-C4532E?style=flat-square)
11 +<a href="https://www.fabri-ka.com"><img src="https://img.shields.io/website?url=https%3A%2F%2Fwww.fabri-ka.com&style=flat-square&label=www.fabri-ka.com&up_color=c4532e" alt="Site"></a>
12 +<a href="https://www.fabri-ka.com/doc/fabri-ka-documentation.pdf"><img src="https://img.shields.io/badge/guide-PDF-c4532e?style=flat-square" alt="Guide PDF"></a>
13 +<a href="https://www.fabri-ka.com/stats"><img src="https://img.shields.io/badge/📊_statistiques-%2Fstats-c4532e?style=flat-square" alt="Stats"></a>
14 +<img src="https://img.shields.io/badge/n%C5%93ud-M4M64a-1f6feb?style=flat-square" alt="Nœud">
15 +<img src="https://img.shields.io/badge/port-8097-555?style=flat-square" alt="Port">
16 +<img src="https://img.shields.io/badge/PM2-3_processus-2b037a?style=flat-square" alt="PM2">
17 +
18 +<a href="https://www.fabri-ka.com/api/stats"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.fabri-ka.com%2Fapi%2Fstats&query=%24.totals.products&label=produits&color=c4532e&style=flat-square" alt="Produits (live)"></a>
19 +<a href="https://www.fabri-ka.com/api/stats"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.fabri-ka.com%2Fapi%2Fstats&query=%24.totals.stores_live&label=boutiques%20live&color=c4532e&style=flat-square" alt="Boutiques live"></a>
20 +<a href="https://www.fabri-ka.com/api/stats"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.fabri-ka.com%2Fapi%2Fstats&query=%24.totals.stores_registry&label=registre%20QC&color=c4532e&style=flat-square" alt="Registre"></a>
21 +<a href="https://www.fabri-ka.com/api/stats"><img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.fabri-ka.com%2Fapi%2Fstats&query=%24.totals.regions&label=r%C3%A9gions&color=c4532e&style=flat-square" alt="Régions"></a>
22 +<img src="https://img.shields.io/badge/connecteurs_plateforme-9-234438?style=flat-square" alt="Connecteurs">
23 +<img src="https://img.shields.io/badge/cat%C3%A9gories-20-234438?style=flat-square" alt="Catégories">
24 +
25 +<img src="https://img.shields.io/badge/Python-FastAPI-3776ab?style=flat-square&logo=python&logoColor=white" alt="Python FastAPI">
26 +<img src="https://img.shields.io/badge/React_18-Vite_%2B_TS-61dafb?style=flat-square&logo=react&logoColor=black" alt="React 18">
27 +<img src="https://img.shields.io/badge/SQLite-FTS5-003b57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLite FTS5">
28 +<img src="https://img.shields.io/badge/Firecrawl-rendu_JS-ff6b35?style=flat-square" alt="Firecrawl">
29 +<img src="https://img.shields.io/badge/Scrapfly-anti--bot-6f42c1?style=flat-square" alt="Scrapfly">
30 +<img src="https://img.shields.io/badge/PM2-prod-2b037a?style=flat-square&logo=pm2&logoColor=white" alt="PM2">
31 +<img src="https://img.shields.io/badge/ngrok-tunnel-1f1e37?style=flat-square&logo=ngrok&logoColor=white" alt="ngrok">
32 +<img src="https://img.shields.io/badge/KA_ID-SSO-c4532e?style=flat-square" alt="KA ID">
33 +<img src="https://img.shields.io/badge/Groupe-KA-b7f000?style=flat-square" alt="Groupe KA">
34 +<a href="https://git.spboucher.ai"><img src="https://img.shields.io/badge/remote--first-spbgit-f05032?style=flat-square&logo=git&logoColor=white" alt="spbgit"></a>
26 35
27 36 </div>
28 37
38 +*Les pastilles « produits / boutiques live / registre QC / régions » sont **dynamiques** : elles interrogent `https://www.fabri-ka.com/api/stats` en direct.*
39 +
29 40 ![Aperçu de Fabri-Ka](docs/screenshot.png)
30 41
31 42 ---
32 43
33 44 ## Description
34 45
35 −**Fabri-Ka** est le **répertoire des produits québécois** : un agrégateur de découverte qui recense **301 534 produits** provenant de **1 377 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.
46 +**Fabri-Ka** est le **répertoire des produits québécois** : un agrégateur de découverte qui recense **385 308 produits** (au 2026-08-24) provenant de **1 582 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 47
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 507 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**).
48 +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 (**4 082 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 49
39 50 > 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 51
41 52 ## Fonctionnalités
42 53
43 −- **Recherche de produits** — plein-texte **FTS5 accent-insensible** sur **301 534 produits**, filtres **catégorie / région / origine / prix**, tri, pagination, fiches riches (galerie, boutique, produits liés).
44 −- **Répertoire de boutiques** — **1 377 boutiques** avec produits (sur **3 507 vérifiées**), fiches enrichies : logo, région, plateforme, **stats de prix**, **politique de livraison** (colonne `shipping_info`, extraite des pages politiques), 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).
54 +- **Recherche de produits** — plein-texte **FTS5 accent-insensible** sur **385 308 produits**, filtres **catégorie / région / origine / prix**, tri, pagination, fiches riches (galerie, boutique, produits liés).
55 +- **Répertoire de boutiques** — **1 582 boutiques** avec produits (sur **4 082 vérifiées**), fiches enrichies : logo, région, plateforme, **stats de prix**, **politique de livraison** (colonne `shipping_info`, extraite des pages politiques), catégories, boutiques similaires.
56 +- **Statistiques** — tableau de bord analytique `/stats` (module commun **ka-stats**) : totaux, médianes, histogrammes, ventilation régionale, croissance — et **rapports de marché PDF** générés à la volée (`/api/report.pdf`, moteur **kapdf**, 5 modes + rapports personnalisés).
57 +- **Catégories** — taxonomie de **20 catégories** (épicerie fine 44 k · mode 43 k · bijoux 39 k · …) avec **~350 pages programmatiques SEO** (`/categorie/erable`, `/region/monteregie`, combinaisons non vides).
47 58 - **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** — **301 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).
59 +- **SEO / SSR** — **385 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 60 - **KA ID (SSO)** + favoris **« Mon univers Ka »** synchronisés au hub **groupe-ka.com**.
50 61 - **Widget KA Agent** — bulle de chat Groupe KA (`ka-agent.js`).
51 62 - **Mobile-first** — barre d'onglets, filtres en bottom sheet, galeries swipe, CTA collant, PWA-lite, **fr-CA** d'abord.
63 +- **Supervision** — connecteurs surveillés par les gardiens autonomes du Groupe KA (agents ka6, supervision api-ka) ; `/api/stats` expose le `sync_log` et les boutiques désactivées.
52 64
53 65 ### Galerie
54 66
@@ -70,6 +82,22 @@ Acheter québécois demande de connaître des centaines de boutiques dispersées
70 82 <img src="docs/screenshots/mobile-produit.png" width="240" alt="Fiche produit mobile"/>
71 83 </p>
72 84
85 +## Chiffres (au 2026-08-24, `/api/stats` live + registre)
86 +
87 +| Mesure | Valeur |
88 +|---|---|
89 +| Produits agrégés (catalogues complets) | **385 308** (dont 370 533 avec prix — prix médian 25 $, moyen 228 $) |
90 +| Boutiques avec produits en ligne | **1 582** |
91 +| Boutiques québécoises vérifiées au registre (`data/stores.json`) | **4 082** — dont 2 466 avec ecommerce détecté et 1 701 activées pour la sync |
92 +| Régions administratives couvertes | **17 / 17** (Montréal 56 k produits · Outaouais 32 k · Saguenay–Lac-Saint-Jean 24 k · …) |
93 +| Connecteurs plateforme | **9** — boutiques actives : Shopify 715 · WooCommerce 545 · Wix 302 · Votresite.ca 45 · Square 36 · Squarespace 27 · générique 22 · autres 9 |
94 +| Classes d'origine au registre | A : 3 863 · E : 180 · C : 39 |
95 +| Catégories | **20** (épicerie fine 44 k · mode 43 k · bijoux 39 k · …) |
96 +| Pages indexables (produits + boutiques + programmatiques) | **385 000+** |
97 +| Base | `data/fabrika.db` **3,2 Go** (SQLite + FTS5) |
98 +
99 +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) · conformité par source : [`docs/CONFORMITE.md`](docs/CONFORMITE.md).
100 +
73 101 ## Stack technique
74 102
75 103 | Couche | Technologies |
@@ -77,10 +105,10 @@ Acheter québécois demande de connaître des centaines de boutiques dispersées
77 105 | **Backend** | **Python 3.14** · **FastAPI** (API + SSR SEO + service du frontend) |
78 106 | **Base de données** | **SQLite + FTS5** — upsert par **hash de contenu**, retraits avec délai de grâce (2 syncs) |
79 107 | **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` · **Square Online** API storefront publique (`/app/store/api/v13/…/products`, IDs user/site extraits du HTML de la page d'accueil) · **Votresite.ca** (plateforme QC : Drupal 8 + boutique OpenCart en sous-répertoire — sitemap OpenCart ou crawl des catégories, puis fiche par fiche) · **connecteur générique** (sitemap → JSON-LD/microdata/OG : PrestaShop, Lightspeed, Snipcart, Magento, sites custom) · **Scrapfly/Firecrawl** en secours anti-bot. **Ecwid** non couvert : storefront 100 % client-side (l'API REST v3 exige un token secret — le token `pub…` de script.js est générique et refusé (403) ; aucun rendu serveur du catalogue, ni sur les instant sites ni via le plugin WordPress) |
108 +| **Connecteurs** | **Shopify** `products.json` (transport curl anti-429 TLS) · **WooCommerce** Store API (+ fallback sitemap Yoast/AIOSEO, permaliens personnalisés) · **Wix Stores** GraphQL · **Squarespace** `?format=json` · **Square Online** API storefront publique (`/app/store/api/v13/…/products`, IDs user/site extraits du HTML de la page d'accueil) · **Votresite.ca** (plateforme QC : Drupal 8 + boutique OpenCart en sous-répertoire — sitemap OpenCart ou crawl des catégories, puis fiche par fiche) · **connecteur générique** (sitemap → JSON-LD/microdata/OG : PrestaShop, Lightspeed, Snipcart, Magento, sites custom) · **Scrapfly/Firecrawl** en secours anti-bot. **Ecwid** non couvert : storefront 100 % client-side (l'API REST v3 exige un token secret — le token `pub…` de script.js est générique et refusé (403) ; aucun rendu serveur du catalogue, ni sur les instant sites ni via le plugin WordPress) |
81 109 | **Découverte** | **35+ sources** : sitemaps d'annuaires, API cachées de cartes (wpgmza, FacetWP), pagination htmx, sessions **Firecrawl** pour répertoires JS |
82 110 | **Comptes** | **KA ID** SSO (JWT HS256, stdlib pure) · favoris HMAC-SHA256 vers le hub central |
83 −| **Prod** | **PM2** + **ngrok** sur le cluster **MacLustr** |
111 +| **Prod** | **PM2** + **ngrok** sur le cluster **MacLustr** (nœud M4M64a) |
84 112
85 113 ### Architecture en 30 secondes
86 114
@@ -88,10 +116,10 @@ Acheter québécois demande de connaître des centaines de boutiques dispersées
88 116 flowchart LR
89 117 subgraph Discovery["Pipeline de découverte (scripts/)"]
90 118 A["35+ annuaires sources<br/>certifications · associations ·<br/>marques régionales · balayages"] --> B["harvest → aggregate →<br/>verify → classify A–E"]
91 − B --> R[("stores.json<br/>3 507 boutiques QC")]
119 + B --> R[("stores.json<br/>4 082 boutiques QC")]
92 120 end
93 121 subgraph FabriKa["Plateforme (fabrika/)"]
94 − R --> C["Connecteurs plateforme<br/>Shopify · Woo · Wix · Squarespace · Square ·<br/>générique (JSON-LD) · Scrapfly"]
122 + R --> C["Connecteurs plateforme<br/>Shopify · Woo · Wix · Squarespace · Square ·<br/>Votresite.ca · générique (JSON-LD) · Scrapfly"]
95 123 C --> D[("SQLite + FTS5<br/>hash + diff")]
96 124 D --> E["API FastAPI<br/>/api/products · /api/facets"]
97 125 D --> S["Rendu serveur SEO<br/>HTML complet · JSON-LD ·<br/>sitemaps · 301/404/410"]
@@ -103,7 +131,7 @@ flowchart LR
103 131 F --> U["🍁 Acheteur local"]
104 132 ```
105 133
106 −## SEO & rendu serveur — **301 000+ pages indexables**
134 +## SEO & rendu serveur — **385 000+ pages indexables**
107 135
108 136 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).
109 137
@@ -117,25 +145,44 @@ Le site est une SPA React, mais **aucun moteur de recherche ne voit une page vid
117 145
118 146 **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.
119 147
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).
148 +**Sitemaps & robots** : `/robots.txt` → `/sitemap.xml` (index) → `sitemap-pages.xml` + `sitemap-boutiques.xml` + `sitemap-produits-{n}.xml` (**40 000 URLs/chunk**, `lastmod` réel, régénérés automatiquement, cache 1 h).
121 149
122 150 **Performance** : **GZip** (‑80 % sur le HTML), `Cache-Control: immutable` sur les assets hashés, images `loading="lazy"` avec dimensions explicites, polices en `swap`.
123 151
124 152 ## API
125 153
126 −| Endpoint | Description |
127 −|---|---|
128 −| `GET /api/products?q=&category=&region=&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 |
154 +| Endpoint | Méthode | Description |
155 +|---|---|---|
156 +| `/api/products?q=&category=&region=&origin=&price_min=&price_max=&sort=&page=` | GET | recherche plein-texte + filtres, triée |
157 +| `/api/products/{uid}` | GET | fiche produit (galerie, boutique, produits liés) |
158 +| `/api/stores?region=&q=&with_products=` | GET | répertoire des boutiques (logo, région, plateforme) |
159 +| `/api/stores/{id}` | GET | fiche boutique enrichie : stats de prix, catégories, boutiques similaires |
160 +| `/api/facets` | GET | catégories/régions/origines avec compteurs |
161 +| `/api/stats` | GET | totaux live, ventilation régionale, `sync_log`, boutiques désactivées |
162 +| `/api/stats/extended` | GET | stats riches : médianes, histogrammes, croissance, ventilation par catégorie |
163 +| `/api/report.pdf` | GET | rapport de marché PDF — généré à la volée (5 modes + `ReportBuilder` personnalisé) |
164 +| `/api/sync` | POST | déclenche une synchronisation en arrière-plan |
165 +| `/api/auth/ka/login` → `…/callback` · `/api/auth/me` · `/api/auth/logout` | GET/POST | SSO **KA ID** |
166 +| `/api/favorites` · `/api/favorites/toggle` | GET/POST | favoris « Mon univers Ka » (hub central) |
167 +| `/robots.txt` · `/sitemap.xml` (+ sous-sitemaps) | GET | infrastructure SEO |
168 +
169 +## Connecteurs & cadences
170 +
171 +**9 connecteurs plateforme** (`fabrika/connectors/`) pilotés par le registre `data/stores.json` :
172 +
173 +| Connecteur | Boutiques actives | Technique | Cadence |
174 +|---|---|---|---|
175 +| `shopify.py` | 715 | `products.json` paginé, transport curl anti-429, retry durci | resync complète aux 6 h |
176 +| `woocommerce.py` | 545 | Store API `/wp-json/wc/store/v1/products` + fallback sitemap → fiches | aux 6 h |
177 +| `wix.py` | 302 | GraphQL storefront public (jeton public) | aux 6 h |
178 +| `votresite.py` | 45 | plateforme QC (Drupal 8 + OpenCart) : sitemap ou crawl catégories → fiche par fiche | aux 6 h |
179 +| `square.py` | 36 | API storefront publique v13, IDs extraits du HTML | aux 6 h |
180 +| `squarespace.py` | 27 | `?format=json` par collection | aux 6 h |
181 +| `generic.py` | 22 | sitemap → JSON-LD/microdata/OG (PrestaShop, Lightspeed, Snipcart, Magento, custom) | aux 6 h |
182 +| `scrapfly.py` / Firecrawl | secours | rendu JS / anti-bot pour les sites qui bloquent | à la demande |
183 +
184 +- Journal `sync_log` par boutique (found/added/updated/removed + raison d'échec), exposé par `/api/stats`.
185 +- Une boutique qui casse **n'affecte jamais les autres** ; les faux positifs de découverte sont désactivés (`enabled=0`) avec garde anti-réintroduction (`reprobe_stores.py`).
139 186
140 187 ## Classification d'origine
141 188
@@ -147,22 +194,7 @@ Le site est une SPA React, mais **aucun moteur de recherche ne voit une page vid
147 194 | **D** | Mixte |
148 195 | **E** | Incertain — à vérifier |
149 196
150 −Chaque classe est justifiée par une **évidence conservée** — jamais déduite du seul fait qu'une entreprise « semble » québécoise.
151 −
152 −## Chiffres (2026-08-18, site live)
153 −
154 −| Mesure | Valeur |
155 −|---|---|
156 −| Produits agrégés (catalogues complets) | **301 534** |
157 −| Boutiques avec produits en ligne | **1 377** |
158 −| Régions administratives couvertes | **17 / 17** |
159 −| Boutiques québécoises actives vérifiées (registre) | **3 507** |
160 −| — avec ecommerce détecté | 2 141 |
161 −| Domaines candidats découverts et dédupliqués | 3 980 |
162 −| Catégories | **20** (mode 38 k · bijoux 32 k · épicerie fine 28 k · …) |
163 −| Pages indexables (produits + boutiques + programmatiques) | **301 000+** |
164 −
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).
197 +Chaque classe est justifiée par une **évidence conservée** — jamais déduite du seul fait qu'une entreprise « semble » québécoise. Répartition au registre (2026-08-24) : **A 3 863 · E 180 · C 39**.
166 198
167 199 ## Structure du projet
168 200
@@ -178,45 +210,62 @@ fabri-ka/
178 210 │ ├── auth.py # SSO « KA ID » (JWT HS256, zéro dépendance)
179 211 │ ├── hubfav.py # favoris poussés au hub Groupe KA (HMAC)
180 212 │ ├── hubprofile.py # profil membre lu du hub
181 −│ └── connectors/ # shopify · woocommerce · wix · squarespace · generic · scrapfly
213 +│ └── connectors/ # shopify · woocommerce · wix · squarespace · square · votresite · generic · scrapfly
182 214 ├── frontend/ # React 18 + Vite + TS — 9 pages, PWA-lite, fr-CA
183 −├── scripts/ # pipeline de découverte (harvest → verify → registry)
184 −├── data/ # stores.json (registre) · fabrika.db · caches (non versionnés)
215 +├── scripts/ # pipeline de découverte (harvest → aggregate → verify → registry),
216 +│ # enrichissements (contacts, livraison, collections), reprobe, docs connecteurs
217 +├── data/ # stores.json (registre 4 082) · fabrika.db (3,2 Go) · caches (non versionnés)
185 218 ├── deliverables/ # rapport de recherche, CSV des boutiques, sources
186 −├── docs/ # captures d'écran de ce README
219 +├── docs/ # captures d'écran, CONFORMITE.md, fiches des connecteurs
187 220 └── run.py # CLI : sync · serve · watch
188 221 ```
189 222
190 −## Démarrage local
223 +## Démarrage rapide
191 224
192 225 ```bash
193 −git clone https://git.spboucher.ai/fabri-ka.git && cd fabri-ka
226 +git clone https://git.spboucher.ai/fabri-ka.git && cd fabri-ka # (prod : ssh M4M64a && cd ~/fabri-ka)
194 227
195 228 # Backend
196 229 python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
197 230
198 −# Frontend
231 +# Frontend (dev : npm run dev ; prod : build servi par FastAPI)
199 232 cd frontend && npm install && npm run build && cd ..
200 233
201 −# (optionnel) répertoires JS / sites anti-bot / SSO
202 −cat > .env <<EOF
203 −FIRECRAWL_API_KEY=fc-votre-cle
204 −KA_SSO_SECRET=… # SSO KA ID (facultatif — le bouton n'apparaît pas sans)
205 −KA_HUB_URL=https://www.groupe-ka.com
206 −FABRIKA_BASE_URL=https://www.fabri-ka.com
207 −EOF
208 −
209 234 # Ingestion puis service
210 235 .venv/bin/python run.py sync # toutes les boutiques connectables
211 −.venv/bin/python run.py serve 8090 # → http://localhost:8090
236 +.venv/bin/python run.py serve 8097 # → http://localhost:8097
212 237 .venv/bin/python run.py watch 360 # resynchronisation en boucle (minutes)
238 +
239 +# Après un changement en prod
240 +pm2 restart fabri-ka-web # (ou fabri-ka-sync selon le changement)
213 241 ```
214 242
243 +## Variables d'environnement
244 +
245 +Noms seulement — les valeurs vivent dans le `.env` du nœud (jamais dans le repo) :
246 +
247 +| Variable | Rôle |
248 +|---|---|
249 +| `FABRIKA_BASE_URL` | URL publique du site (canonicals, sitemaps) |
250 +| `FABRIKA_ADMIN_TOKEN` | jeton des opérations d'administration (`/api/sync`) |
251 +| `KA_SSO_SECRET` · `KA_HUB_URL` · `SESSION_SECRET` | SSO KA ID (JWT HS256) et hub groupe-ka.com |
252 +| `FIRECRAWL_API_KEY` | sessions Firecrawl (répertoires JS, découverte) |
253 +| `SCRAPFLY_API_KEY` | secours anti-bot de certains connecteurs |
254 +
255 +## Données & conformité
256 +
257 +- **Provenance** : le catalogue provient des **endpoints publics des boutiques elles-mêmes** (Shopify `products.json`, WooCommerce Store API, GraphQL Wix, etc.) — pas d'annuaire tiers, aucune donnée derrière authentification.
258 +- **Revue de conformité par source** : chaque source est évaluée AVANT connexion (robots.txt/CGU lus et datés, décision consignée) — voir [`docs/CONFORMITE.md`](docs/CONFORMITE.md) (dernière revue complète : 2026-08-19).
259 +- **Politesse réseau** : throttle global par plateforme (Shopify : **0,7 s/req** toutes boutiques confondues), backoff 429, caches disque, User-Agent identifié.
260 +- **Traçabilité** : chaque produit renvoie vers la boutique d'origine ; chaque boutique conserve sa provenance de découverte (`discovery_sources`) et son évidence d'origine (`origin_evidence`).
261 +- **Cadence** : resynchronisation complète **aux 6 heures** (`run.py watch 360`) ; retraits avec délai de grâce (2 syncs).
262 +- **Retrait sur demande** : tout ayant droit peut demander le retrait de ses données (contact ci-dessous).
263 +
215 264 ## Déploiement
216 265
217 266 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)**.
218 267
219 −**3 processus PM2 :**
268 +**3 processus PM2 (vérifiés live) :**
220 269
221 270 ```
222 271 fabri-ka-web .venv/bin/python run.py serve 8097 # API + frontend + SSR SEO
@@ -233,6 +282,19 @@ La **source de vérité est le repo git sur le nœud M4M64a** (`~/fabri-ka`) —
233 282 - 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 283 - **Ne pas éditer de copies laptop** ; référence complète : `~/Desktop/cluster-skill/KA-REMOTE-DEV.md`.
235 284
285 +## Historique
286 +
287 +Jalons réels du repo (`git log`, 95 commits) :
288 +
289 +| Jalon | Commit |
290 +|---|---|
291 +| Naissance — agrégateur de produits québécois | `4d4f1b5` |
292 +| Expansion majeure : 149 500+ produits, 3 131 boutiques, 17/17 régions | `a4bbc59` |
293 +| Stats v2 — dashboard ultra complet + 5 rapports PDF (kapdf v2) | `6112f03` |
294 +| Qualité produits — listing_status, quarantaine, prix « sur devis », galeries enrichies | `1f5ecb8` |
295 +| Connecteur Votresite.ca (plateforme QC) — 45 boutiques, +3 190 produits | `037494b` |
296 +| Stats v3 — rapports PDF personnalisés (`ReportBuilder`) · gardiens ka6 sur la supervision des connecteurs | `1fbc242` · `db542b0` |
297 +
236 298 ## Principes
237 299
238 300 1. **Politesse** — throttle global par plateforme (Shopify : **0,7 s/req** toutes boutiques confondues), délais entre requêtes, User-Agent identifié.
@@ -241,6 +303,24 @@ La **source de vérité est le repo git sur le nœud M4M64a** (`~/fabri-ka`) —
241 303 4. **Robustesse** — une boutique qui casse n'affecte jamais les autres ; journal `sync_log` avec raison d'échec par boutique.
242 304 5. **Trouvabilité** — tout ce qui existe dans la base existe pour Google : HTML complet, données structurées, sitemaps exhaustifs, jamais de soft-404.
243 305
306 +## Écosystème Groupe KA
307 +
308 +| Plateforme | Univers |
309 +|---|---|
310 +| [groupe-ka.com](https://www.groupe-ka.com) | portail & KA ID |
311 +| [lou-ka.com](https://www.lou-ka.com) | logements à louer |
312 +| [immo-ka.com](https://www.immo-ka.com) | propriétés à vendre |
313 +| [vrai-prix.com](https://www.vrai-prix.com) | estimation immobilière |
314 +| [auto-ka.com](https://www.auto-ka.com) | véhicules |
315 +| [fabri-ka.com](https://www.fabri-ka.com) | produits québécois |
316 +| [food-ka.com](https://www.food-ka.com) | épicerie/alimentation |
317 +| [resto-ka.com](https://www.resto-ka.com) | restaurants |
318 +| [sorti-ka.com](https://www.sorti-ka.com) | sorties et événements |
319 +| [job-ka.com](https://www.job-ka.com) | emplois |
320 +| [crea-ka.com](https://www.crea-ka.com) | créateurs |
321 +| [trouve-ka.com](https://www.trouve-ka.com) | petites annonces |
322 +| [api-ka.com](https://www.api-ka.com) | API de données |
323 +
244 324 ---
245 325
246 326 <div align="center">
247 327