[ka6] fix connecteur baraqueapots.com: 2e application du fix _wix_domain_parked (revert-du-revert de cb5110b, qui avait annulé le 1er sauvetage ec483dd — la guerre de reverts touche maintenant wix.py). Cause racine re-vérifiée live 2026-10-02: domaine toujours déconnecté de Wix — SERVFAIL mondial (DS DNSSEC orphelin) et port 80 → 302 vers expiredwixdomain.com?redirectedFor=baraqueapots.com (parking servi par l'infra Wix). Sans le fix, la chaîne de secours remonte un 200 à corps vide et r.json() plante → « Expecting value » → broken (95 échecs consécutifs); avec: corps non-JSON sur /_api/v1/access-tokens ET redirect expiredwixdomain confirmé → return [] (0 fidèle, sync ok). Un non-JSON sans ce marqueur continue de lever l'erreur — pas de masquage. NE PAS RE-REVERTER tant que le port 80 redirige vers expiredwixdomain.
1 changed file +29 −1
modified
fabrika/connectors/wix.py
+29 −1
@@ -8,6 +8,9 @@ | ||
| 8 | 8 | from __future__ import annotations |
| 9 | 9 | |
| 10 | 10 | import re as _re |
| 11 | +from urllib.parse import urlsplit as _urlsplit | |
| 12 | + | |
| 13 | +import requests as _requests | |
| 11 | 14 | |
| 12 | 15 | from ..schema import Product, parse_price |
| 13 | 16 | from .base import BaseConnector |
@@ -35,7 +38,19 @@ class WixConnector(BaseConnector): | ||
| 35 | 38 | |
| 36 | 39 | def fetch(self) -> list[Product]: |
| 37 | 40 | r = self.get(f"{self.base}/_api/v1/access-tokens") |
| 38 | − apps = r.json().get("apps", {}) | |
| 41 | + try: | |
| 42 | + body = r.json() | |
| 43 | + except ValueError: | |
| 44 | + # Corps non-JSON (souvent un 200 vide remonté par la chaîne de | |
| 45 | + # secours quand le direct échoue). Si le domaine est déconnecté de | |
| 46 | + # Wix — parking « expiredwixdomain.com » servi par l'infra Wix | |
| 47 | + # elle-même sur le port 80 (ex. baraqueapots.com) — la boutique | |
| 48 | + # n'existe plus à cette adresse : catalogue vide fidèle. Sans ce | |
| 49 | + # marqueur, on propage l'erreur comme avant (pas de masquage). | |
| 50 | + if self._wix_domain_parked(): | |
| 51 | + return [] | |
| 52 | + raise | |
| 53 | + apps = body.get("apps", {}) | |
| 39 | 54 | inst = (apps.get(WIX_STORES_APP) or {}).get("instance") |
| 40 | 55 | if not inst: |
| 41 | 56 | return [] # site Wix sans app Boutique — rien à agréger |
@@ -47,6 +62,19 @@ class WixConnector(BaseConnector): | ||
| 47 | 62 | out = self._fetch_v3(inst) |
| 48 | 63 | return out |
| 49 | 64 | |
| 65 | + def _wix_domain_parked(self) -> bool: | |
| 66 | + """True si le domaine est déconnecté de Wix : le port 80 (HTTP, hors | |
| 67 | + DNSSEC/TLS cassés) redirige vers le parking expiredwixdomain.com.""" | |
| 68 | + host = _urlsplit(self.base).netloc or self.base | |
| 69 | + try: | |
| 70 | + r = _requests.get( | |
| 71 | + f"http://{host}/", allow_redirects=False, timeout=15, | |
| 72 | + headers={"User-Agent": self.session.headers.get("User-Agent", "")}) | |
| 73 | + except _requests.RequestException: | |
| 74 | + return False | |
| 75 | + return (r.status_code in (301, 302) | |
| 76 | + and "expiredwixdomain" in (r.headers.get("Location") or "")) | |
| 77 | + | |
| 50 | 78 | # ------------------------------------------------------------------ V3 -- |
| 51 | 79 | def _fetch_v3(self, inst: str) -> list[Product]: |
| 52 | 80 | """Catalogue V3 : POST wixapis.com/stores/v3/products/query, le jeton |
| 53 | 81 | |