Fabri·Ka
Tous les produits québécois. Un seul endroit.
Agrégateur de découverte des produits vendus en ligne par les entreprises du Québec — chaque produit renvoie vers la boutique d'origine. Aucune vente sur Fabri-Ka : les prix, photos et stocks appartiennent aux marchands sources.
Aperçu
| Accueil | Catalogue | Boutiques |
|---|---|---|
![]() |
![]() |
![]() |
| Page boutique (landing) | Statistiques |
|---|---|
![]() |
![]() |

Mobile-first : barre d'onglets, filtres en bottom sheet, galeries swipe, pages boutiques avec logo/couverture, page statistiques.
Pourquoi Fabri-Ka ?
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, 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).
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.jsonproduit par la recherche.
L'architecture en 30 secondes
flowchart LR
subgraph Discovery["Pipeline de découverte (scripts/)"]
A["30+ annuaires sources<br/>certifications · associations ·<br/>marques régionales · balayages"] --> B["harvest → aggregate →<br/>verify → classify A–E"]
B --> R[("stores.json<br/>3 131 boutiques QC")]
end
subgraph FabriKa["Plateforme (fabrika/)"]
R --> C["Connecteurs plateforme<br/>Shopify · WooCommerce · Wix ·<br/>Squarespace · Scrapfly/Firecrawl"]
C --> D[("SQLite + FTS5<br/>hash + diff")]
D --> E["API FastAPI<br/>/api/products · /api/facets"]
E --> F["React 18 + Vite<br/>recherche · filtres · fiches"]
end
W["⏱ Watcher 6 h (PM2)"] -.-> C
F --> U["🍁 Acheteur local"]
| Couche | Rôle | Fichiers |
|---|---|---|
| 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 |
| Vérification | Plateforme ecommerce + test réel de l'endpoint catalogue + signaux de localisation QC (codes postaux, indicatifs, TLD, wording) | scripts/verify.py |
| Classification | Origine A–E avec évidence conservée (certifications → a priori ; wording du site → ajustement) | scripts/build_registry.py |
| Connecteurs | Shopify products.json (transport curl anti-429 TLS), WooCommerce Store API, Wix Stores GraphQL (jeton public), Squarespace ?format=json, Firecrawl en secours |
fabrika/connectors/ |
| Diff engine | Upsert par hash de contenu, retraits avec délai de grâce (2 syncs) | fabrika/db.py |
| API | Recherche FTS5 accent-insensible, facettes, filtres catégorie/région/origine/prix, stats, sync | fabrika/web.py |
| Frontend | « Chaleur artisanale, précision éditoriale » — Fraunces + Outfit, crème/terracotta/pin, PWA-lite, fr-CA | frontend/ |
Chiffres (2026-08-12)
| Mesure | Valeur |
|---|---|
| Domaines candidats découverts et dédupliqués | 3 481 |
| Boutiques québécoises actives vérifiées | 3 131 |
| — avec ecommerce détecté | 1 970 |
| — connectables par un connecteur plateforme | 1 369 |
| Produits agrégés (catalogues complets) | 281 000+ |
| Boutiques avec produits en ligne | 1 204 |
| Régions administratives couvertes | 17 / 17 |
| Classes d'origine | A : 3 062 · C : 19 · E : 50 |
Détails, méthodologie, taux de découverte marginal et angles morts :
deliverables/research_report.md ·
stratégie de découverte continue : deliverables/discovery_strategy.md.
Démarrage rapide
git clone https://git.spboucher.ai/fabri-ka.git && cd fabri-ka
# Backend
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
# Frontend
cd frontend && npm install && npm run build && cd ..
# (optionnel) répertoires JS / sites anti-bot
echo "FIRECRAWL_API_KEY=fc-votre-cle" > .env
# Ingestion puis service
.venv/bin/python run.py sync # toutes les boutiques connectables
.venv/bin/python run.py serve 8090 # → http://localhost:8090
.venv/bin/python run.py watch 360 # resynchronisation en boucle (minutes)API
| Endpoint | Description |
|---|---|
GET /api/products?q=&category=®ion=&origin=&price_min=&price_max=&sort=&page= |
Recherche plein-texte + filtres, triée |
GET /api/products/{uid} |
Fiche produit (galerie, boutique, produits liés) |
GET /api/stores?region=&q=&with_products= |
Répertoire des boutiques (logo, région, plateforme) |
GET /api/stores/{id} |
Fiche boutique enrichie : logo, couverture, stats de prix, catégories, boutiques similaires |
GET /api/facets |
Catégories/régions/origines avec compteurs |
GET /api/stats |
Totaux, ventilation régionale, journal de sync |
GET /api/stats/extended |
Statistiques riches : prix (médian/moyen/histogramme), catégories, régions, plateformes, croissance, top boutiques |
GET /api/report.pdf |
Rapport de marché PDF (couverture de marque, KPIs, graphiques, tableaux) — généré à la volée |
POST /api/sync |
Déclenche une synchronisation en arrière-plan |
Classification d'origine
| Classe | Signification |
|---|---|
| A | Fabriqué / cultivé / transformé au Québec |
| B | Conçu au Québec, fabriqué ailleurs |
| C | Détaillant québécois (revente) |
| D | Mixte |
| E | Incertain — à vérifier |
Chaque classe est justifiée par une évidence conservée (certification Aliments du Québec / Les Produits du Québec, adhésion à une association de producteurs, wording du site) — jamais déduite du seul fait qu'une entreprise « semble » québécoise.
Production
Déployé sous PM2 (3 processus) derrière ngrok sur le nœud M4M64a :
fabri-ka-web .venv/bin/python run.py serve 8097 # API + frontend
fabri-ka-sync .venv/bin/python run.py watch 360 # resync aux 6 h
fabri-ka-ngrok ngrok http --url=www.fabri-ka.com 8097 # tunnelPrincipes
- Politesse — throttle global par plateforme (Shopify : 0,7 s/req toutes boutiques confondues), délais entre requêtes, User-Agent identifié.
- Fidélité — aucun prix inventé ; entités HTML décodées ; si la source ne
publie pas une donnée, elle est
null. - Traçabilité — chaque produit renvoie vers la boutique d'origine ; chaque boutique conserve sa provenance de découverte et son évidence d'origine.
- Robustesse — une boutique qui casse n'affecte jamais les autres ;
journal
sync_logavec raison d'échec par boutique.




