From 9f1dd4e3b855393565d25feed120209ca56a7b5b Mon Sep 17 00:00:00 2001 From: Dicken Date: Sat, 11 Jul 2026 09:08:43 +0200 Subject: [PATCH] feat: MakerWorld Raw-Fetch-Diagnose vor Puppeteer-Fallback --- backend/src/tools/makerworld/routes.js | 56 +++++++++++++++++++++++--- 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/backend/src/tools/makerworld/routes.js b/backend/src/tools/makerworld/routes.js index ba1a8d9..9ff3853 100644 --- a/backend/src/tools/makerworld/routes.js +++ b/backend/src/tools/makerworld/routes.js @@ -110,6 +110,33 @@ function mapStatisticalData(parsed) { }; } +// Diagnose/Fallback: Abruf OHNE Browser, nur reiner HTTP-fetch() mit dem +// übergebenen Cookie-String. Testet, ob ein reiner Cookie-Replay reicht, oder +// ob Cloudflare zusätzlich am TLS-Fingerprint der Verbindung erkennt, dass es +// kein "echter" Browser ist (dann hilft auch kein Puppeteer-Cookie-Inject). +async function tryRawFetch(userId, cookieString) { + try { + const res = await fetch(DATA_URL, { + headers: { + 'Cookie': cookieString, + 'User-Agent': USER_AGENT, + 'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8', + 'Accept-Language': 'de,en-US;q=0.9,en;q=0.8', + 'Accept-Encoding': 'gzip, deflate', + }, + }); + console.log(`[MakerWorld:${userId}] Raw-Fetch-Test: HTTP ${res.status}`); + if (!res.ok) return null; + const html = await res.text(); + const parsed = extractNextData(html); + console.log(`[MakerWorld:${userId}] Raw-Fetch-Test: __NEXT_DATA__ ${parsed ? 'gefunden ✓' : 'nicht gefunden (vermutlich Just-a-moment-Seite)'}`); + return parsed; + } catch (e) { + console.log(`[MakerWorld:${userId}] Raw-Fetch-Test fehlgeschlagen: ${e.message}`); + return null; + } +} + // ── Browser-Helfer ──────────────────────────────────────────────────────────── // Nach einem abgebrochenen Vorgang (Container-Neustart mitten im Betrieb, // Absturz o.ä.) kann Chromium veraltete Sperrdateien im Profil hinterlassen — @@ -475,17 +502,34 @@ router.delete('/credentials', authenticate, (req, res) => { // Manueller Fallback: Cookie-String direkt ins eigene Profil einspielen router.post('/cookie', authenticate, async (req, res) => { const { cookie } = req.body; - console.log(`[MakerWorld:${req.user.id}] POST /cookie empfangen (${cookie?.length || 0} Zeichen)`); + const userId = req.user.id; + console.log(`[MakerWorld:${userId}] POST /cookie empfangen (${cookie?.length || 0} Zeichen)`); if (!cookie || typeof cookie !== 'string' || cookie.trim().length < 10) { return res.status(400).json({ error: 'Ungültiger Cookie-String' }); } + const cache = getCache(userId); + + // Erst den günstigen Weg testen: reiner HTTP-fetch() ohne Browser + const rawParsed = await tryRawFetch(userId, cookie.trim()); + if (rawParsed) { + try { + const data = mapStatisticalData(rawParsed); + cache.data = data; cache.fetchedAt = Date.now(); cache.error = null; + console.log(`[MakerWorld:${userId}] Raw-Fetch erfolgreich, Puppeteer nicht nötig`); + return res.json({ data: cache.data, error: null, fetchedAt: cache.fetchedAt, needsCode: false, method: 'raw-fetch' }); + } catch (e) { + console.log(`[MakerWorld:${userId}] Raw-Fetch lieferte Seite, aber Mapping fehlgeschlagen: ${e.message}`); + } + } + + // Fallback: über Puppeteer ins persistente Profil einspielen try { - await injectCookies(req.user.id, cookie.trim()); - const result = await fetchStatsViaBrowser(req.user.id, true); - console.log(`[MakerWorld:${req.user.id}] POST /cookie fertig: needsCode=${result.needsCode}, error=${result.error||'-'}`); - res.json(result); + await injectCookies(userId, cookie.trim()); + const result = await fetchStatsViaBrowser(userId, true); + console.log(`[MakerWorld:${userId}] POST /cookie fertig (Puppeteer-Fallback): needsCode=${result.needsCode}, error=${result.error||'-'}`); + res.json({ ...result, method: 'puppeteer' }); } catch (e) { - console.error(`[MakerWorld:${req.user.id}] POST /cookie Exception: ${e.message}`); + console.error(`[MakerWorld:${userId}] POST /cookie Exception: ${e.message}`); res.status(500).json({ error: e.message }); } });