# Fabri·Ka ### Tous les produits québécois. Un seul endroit. **[www.fabri-ka.com](https://www.fabri-ka.com)** ![Python](https://img.shields.io/badge/Python-3.14-1E1B16?style=for-the-badge&logo=python&logoColor=C4532E) ![FastAPI](https://img.shields.io/badge/FastAPI-API-1E1B16?style=for-the-badge&logo=fastapi&logoColor=C4532E) ![React](https://img.shields.io/badge/React_18-Vite_+_TS-1E1B16?style=for-the-badge&logo=react&logoColor=C4532E) ![SQLite](https://img.shields.io/badge/SQLite-FTS5-1E1B16?style=for-the-badge&logo=sqlite&logoColor=C4532E) ![Firecrawl](https://img.shields.io/badge/Firecrawl-rendu_JS-1E1B16?style=for-the-badge&logoColor=C4532E) ![Produits](https://img.shields.io/badge/produits_agr%C3%A9g%C3%A9s-281_000%2B-234438?style=flat-square) ![Boutiques connectées](https://img.shields.io/badge/boutiques_avec_produits-1_204-234438?style=flat-square) ![Registre](https://img.shields.io/badge/boutiques_QC_v%C3%A9rifi%C3%A9es-3_224-234438?style=flat-square) ![Connecteurs](https://img.shields.io/badge/connecteurs_plateforme-6-234438?style=flat-square) ![Régions](https://img.shields.io/badge/r%C3%A9gions_couvertes-17%2F17-234438?style=flat-square) ![Sources](https://img.shields.io/badge/sources_de_d%C3%A9couverte-35%2B-234438?style=flat-square) *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 | |---|---|---| | ![Accueil](docs/screenshots/accueil.png) | ![Catalogue](docs/screenshots/catalogue.png) | ![Boutiques](docs/screenshots/boutiques.png) | | Page boutique (landing) | Statistiques | |---|---| | ![Boutique](docs/screenshots/boutique-medusa.png) | ![Stats](docs/screenshots/stats.png) |

Version mobile

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.json` produit par la recherche. ## L'architecture en 30 secondes ```mermaid flowchart LR subgraph Discovery["Pipeline de découverte (scripts/)"] A["30+ annuaires sources
certifications · associations ·
marques régionales · balayages"] --> B["harvest → aggregate →
verify → classify A–E"] B --> R[("stores.json
3 131 boutiques QC")] end subgraph FabriKa["Plateforme (fabrika/)"] R --> C["Connecteurs plateforme
Shopify · WooCommerce · Wix ·
Squarespace · Scrapfly/Firecrawl"] C --> D[("SQLite + FTS5
hash + diff")] D --> E["API FastAPI
/api/products · /api/facets"] E --> F["React 18 + Vite
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`](deliverables/research_report.md) · stratégie de découverte continue : [`deliverables/discovery_strategy.md`](deliverables/discovery_strategy.md). ## Démarrage rapide ```bash 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 # tunnel ``` ## Principes 1. **Politesse** — throttle global par plateforme (Shopify : 0,7 s/req toutes boutiques confondues), délais entre requêtes, User-Agent identifié. 2. **Fidélité** — aucun prix inventé ; entités HTML décodées ; si la source ne publie pas une donnée, elle est `null`. 3. **Traçabilité** — chaque produit renvoie vers la boutique d'origine ; chaque boutique conserve sa provenance de découverte et son évidence d'origine. 4. **Robustesse** — une boutique qui casse n'affecte jamais les autres ; journal `sync_log` avec raison d'échec par boutique. ---
## Auteur **Simon-Pierre Boucher** [![Email](https://img.shields.io/badge/contact@spboucher.ai-1E1B16?style=for-the-badge&logo=minutemailer&logoColor=C4532E)](mailto:contact@spboucher.ai) [![Git](https://img.shields.io/badge/git.spboucher.ai-1E1B16?style=for-the-badge&logo=git&logoColor=C4532E)](https://git.spboucher.ai) *Conçu, construit et déployé en une journée — du recensement provincial (3 131 boutiques vérifiées, 35+ sources) au moteur de recherche en production.* © 2026 Simon-Pierre Boucher — tous droits réservés.