spb/fabri-ka Public
Agrégateur de produits québécois — www.fabri-ka.com
HTML 57.9%
Python 18.6%
TypeScript 15.6%
CSS 7.8%
1# -----------------------------------------------------------------------------2# Fabri-Ka — Agrégateur de produits québécois3# Auteur : Simon-Pierre Boucher — contact@spboucher.ai4# connectors/wix.py : connecteur générique Wix Stores.5# 1) GET /_api/v1/access-tokens -> jeton d'instance public de l'app Wix Stores6# 2) GraphQL storefront getFilteredProducts (catalogue complet, paginé)7# -----------------------------------------------------------------------------8from __future__ import annotations910from ..schema import Product, parse_price11from .base import BaseConnector1213WIX_STORES_APP = "1380b703-ce81-ff05-f115-39571d94dfcd"14ALL_PRODUCTS_CATEGORY = "00000000-000000-000000-000000000001"1516GQL = """query getFilteredProducts($limit:Int!,$offset:Int!){17 catalog{category(categoryId:"%s"){numOfProducts18 productsWithMetaData(limit:$limit,offset:$offset,onlyVisible:true){19 list{id name urlPart price comparePrice formattedPrice description20 media{url} isInStock productType ribbon21 options{title selections{description}}}}}}}""" % ALL_PRODUCTS_CATEGORY222324class WixConnector(BaseConnector):25 platform = "wix"2627 def fetch(self) -> list[Product]:28 r = self.get(f"{self.base}/_api/v1/access-tokens")29 apps = r.json().get("apps", {})30 inst = (apps.get(WIX_STORES_APP) or {}).get("instance")31 if not inst:32 return [] # site Wix sans app Boutique — rien à agréger33 out: list[Product] = []34 offset, total = 0, None35 while offset < (total if total is not None else 1) and offset < 20000:36 resp = self.session.post(37 f"{self.base}/_api/wix-ecommerce-storefront-web/api",38 headers={"Authorization": inst, "Content-Type": "application/json"},39 json={"query": GQL, "variables": {"limit": 100, "offset": offset}},40 timeout=self.timeout)41 resp.raise_for_status()42 cat = (((resp.json().get("data") or {}).get("catalog") or {})43 .get("category") or {})44 if total is None:45 total = int(cat.get("numOfProducts") or 0)46 if total == 0:47 break48 items = ((cat.get("productsWithMetaData") or {}).get("list")) or []49 if not items:50 break51 for it in items:52 imgs = []53 for m in (it.get("media") or []):54 u = m.get("url") or ""55 if u and not u.startswith("http"):56 u = "https://static.wixstatic.com/media/" + u.lstrip("/")57 if u:58 imgs.append(u)59 price = parse_price(it.get("price"))60 compare = parse_price(it.get("comparePrice"))61 out.append(Product(62 store_id=self.store_id,63 external_id=str(it.get("id", "")),64 url=f"{self.base}/product-page/{it.get('urlPart', '')}",65 title=it.get("name", ""),66 description=it.get("description", "") or "",67 price=price,68 price_max=price,69 compare_at_price=compare if compare and price and compare > price else None,70 images=imgs,71 product_type=it.get("productType", "") or "",72 tags=[it["ribbon"]] if it.get("ribbon") else [],73 available=bool(it.get("isInStock", True)),74 ))75 offset += 10076 return out77