diff --git a/backend/package.json b/backend/package.json index f289d48..c40a198 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,6 +10,8 @@ "multer": "^1.4.5-lts.1", "ws": "^8.17.1", "puppeteer-core": "^22.0.0", + "puppeteer-extra": "^3.3.6", + "puppeteer-extra-plugin-stealth": "^2.11.2", "mqtt": "^5.10.1" } -} +} \ No newline at end of file diff --git a/backend/src/tools/makerworld/routes.js b/backend/src/tools/makerworld/routes.js index e6a90a2..af63603 100644 --- a/backend/src/tools/makerworld/routes.js +++ b/backend/src/tools/makerworld/routes.js @@ -123,8 +123,19 @@ function cleanupStaleLocks(profileDir) { async function launchBrowser(userId) { let puppeteer; - try { puppeteer = require('puppeteer-core'); } - catch { throw new Error('puppeteer-core nicht installiert'); } + try { + // puppeteer-extra + Stealth-Plugin patcht die üblichen Merkmale, an denen + // Cloudflare & Co. headless Chromium erkennen (navigator.webdriver, fehlende + // Plugins/MimeTypes, iframe-contentWindow-Check, ...). Ohne das blieb die + // Seite bei "Just a moment..." hängen, auch nach langem Warten. + const { addExtra } = require('puppeteer-extra'); + const StealthPlugin = require('puppeteer-extra-plugin-stealth'); + const puppeteerCore = require('puppeteer-core'); + puppeteer = addExtra(puppeteerCore); + puppeteer.use(StealthPlugin()); + } catch { + throw new Error('puppeteer-core / puppeteer-extra nicht installiert'); + } const profileDir = `${PROFILE_BASE_DIR}/${userId}`; fs.mkdirSync(profileDir, { recursive: true }); cleanupStaleLocks(profileDir); @@ -411,11 +422,13 @@ router.get('/login-status', authenticate, (req, res) => { }); }); -// Letzter Fehl-Screenshot (Diagnose) — nur der eigene +// Letzter Fehl-Screenshot (Diagnose) — nur der eigene, als data:-URI (CSP erlaubt +// kein blob:/externes Laden, data: URIs sind bereits über img-src freigegeben) router.get('/debug-screenshot', authenticate, (req, res) => { const path = `${PROFILE_BASE_DIR}/${req.user.id}/last-failure.png`; if (!fs.existsSync(path)) return res.status(404).json({ error: 'Kein Debug-Screenshot vorhanden' }); - res.sendFile(path); + const base64 = fs.readFileSync(path).toString('base64'); + res.json({ image: `data:image/png;base64,${base64}` }); }); router.post('/credentials', authenticate, async (req, res) => { diff --git a/frontend/src/tools/statistik.jsx b/frontend/src/tools/statistik.jsx index 833546a..e6b0d8f 100644 --- a/frontend/src/tools/statistik.jsx +++ b/frontend/src/tools/statistik.jsx @@ -274,13 +274,9 @@ function MakerworldTab({ toast }) { const viewDebugScreenshot = async () => { setLoadingScreenshot(true); try { - const token = localStorage.getItem('sk_token'); - const res = await fetch('/api/tools/makerworld/debug-screenshot', { - headers: token ? { 'Authorization': 'Bearer '+token } : {}, - }); - if (!res.ok) { toast?.('Kein Debug-Screenshot vorhanden','error'); return; } - const blob = await res.blob(); - setDebugImgUrl(URL.createObjectURL(blob)); + const r = await api('/tools/makerworld/debug-screenshot'); + if (!r.image) { toast?.('Kein Debug-Screenshot vorhanden','error'); return; } + setDebugImgUrl(r.image); // bereits eine data:-URI, CSP-konform (kein blob:) } catch(e) { toast?.(e.message,'error'); } finally { setLoadingScreenshot(false); } };