[ka6] fix connecteur fermeail-land.com: 3e application du garde-fou _domain_nxdomain (revert du revert 0c218c4, qui avait annulé la 2e application 32f0f06 ~8 h après — fix perdu 2×). Cause racine re-vérifiée live le 2026-10-14 ~15h: le domaine est TOUJOURS en NXDOMAIN mondial (expiré 2026-10-12 22:50 UTC, clientHold Tucows 2026-10-13 08:48 UTC, retiré de la zone .com) — DoH dns.google Status 3 (hôte ET www), DoH Cloudflare Status 3, curl direct exit 6 « Could not resolve host ». Sans le garde-fou, le RuntimeError (direct irrésoluble + Scrapfly 422) redonne 3 échecs consécutifs → broken. Réapplication par git revert -n de 0c218c4: fichier shopify.py strictement identique à l'état validé 32f0f06/31a6cac (diff vide vérifié) — ShopifyConnector._domain_nxdomain() (NXDOMAIN exigé en DoH dns.google sur l'hôte ET l'apex) + dans fetch(), sur RuntimeError sans produit accumulé, si NXDOMAIN confirmé → return [] → ok/0 FIDÈLE (boutique jamais garnie, médiane 0.0). Échec réseau sans NXDOMAIN → erreur propagée, aucun masquage. Sync réel 2026-10-14: fermeail-land.com ok / 0 produit (55 s); non-régression arthicetnunc.com ok / 24 produits (chemin Shopify nominal intact, _StorefrontLockedError inchangé). pm2 restart fabri-ka-sync fait, web local 200. NE PAS RE-REVERTER tant que fermeail-land.com est NXDOMAIN (re-sonder DoH avant tout revert): si la ferme renouvelle le domaine (grâce registrar ~30-45 j), le DNS revient et le connecteur reprend tout seul sans toucher la fiche.
1 changed file +34 −0
modified
fabrika/connectors/shopify.py
+34 −0
@@ -110,6 +110,30 @@ class ShopifyConnector(BaseConnector): | ||
| 110 | 110 | time.sleep(3 * (attempt + 1)) |
| 111 | 111 | raise RuntimeError(f"{url}: scrapfly: {last_err}") |
| 112 | 112 | |
| 113 | + def _domain_nxdomain(self) -> bool: | |
| 114 | + """True si le domaine a disparu du DNS mondial : NXDOMAIN (Status 3) | |
| 115 | + chez un résolveur public pour l'hôte ET son apex — typiquement un | |
| 116 | + domaine expiré mis en clientHold par le registrar, donc retiré de la | |
| 117 | + zone (ex. fermeail-land.com, expiré 2026-10-12, clientHold Tucows le | |
| 118 | + 13). Vérifié en DNS-over-HTTPS (dns.google) pour ne pas confondre avec | |
| 119 | + une panne du résolveur local du nœud ; en cas de doute (DoH | |
| 120 | + injoignable, Status != 3) on renvoie False et l'erreur se propage — | |
| 121 | + pas de masquage. Même philosophie que _wix_domain_nxdomain (524c743).""" | |
| 122 | + import urllib.parse as _up | |
| 123 | + | |
| 124 | + import requests as _requests | |
| 125 | + host = (_up.urlsplit(self.base).netloc or self.base).split(":")[0] | |
| 126 | + apex = host[4:] if host.startswith("www.") else host | |
| 127 | + for name in {host, apex}: | |
| 128 | + try: | |
| 129 | + r = _requests.get("https://dns.google/resolve", | |
| 130 | + params={"name": name, "type": "A"}, timeout=15) | |
| 131 | + if r.json().get("Status") != 3: | |
| 132 | + return False | |
| 133 | + except (ValueError, _requests.RequestException): | |
| 134 | + return False | |
| 135 | + return True | |
| 136 | + | |
| 113 | 137 | def fetch(self) -> list[Product]: |
| 114 | 138 | out: list[Product] = [] |
| 115 | 139 | page = 1 |
@@ -122,6 +146,16 @@ class ShopifyConnector(BaseConnector): | ||
| 122 | 146 | raise |
| 123 | 147 | except _StorefrontLockedError: |
| 124 | 148 | break |
| 149 | + except RuntimeError: | |
| 150 | + # Échec sans aucun produit accumulé : si le domaine a carrément | |
| 151 | + # disparu du DNS mondial (expiration + clientHold registrar, | |
| 152 | + # ex. fermeail-land.com 2026-10-13 — curl « Could not resolve | |
| 153 | + # host », Scrapfly 422), la boutique n'existe plus à cette | |
| 154 | + # adresse : catalogue vide fidèle. Sans ce marqueur, on propage | |
| 155 | + # l'erreur comme avant (pas de masquage). | |
| 156 | + if not out and self._domain_nxdomain(): | |
| 157 | + break | |
| 158 | + raise | |
| 125 | 159 | items = data.get("products", []) |
| 126 | 160 | if not items: |
| 127 | 161 | break |
| 128 | 162 | |