feat: MakerWorld Debug-Screenshot + bessere Fehlerdiagnose bei fehlgeschlagenem Abruf
This commit is contained in:
@@ -130,7 +130,16 @@ async function getNextDataFromPage(page) {
|
||||
}
|
||||
async function isCloudflareChallenge(page) {
|
||||
const title = await page.title().catch(() => '');
|
||||
return /just a moment|checking your browser|attention required/i.test(title);
|
||||
return /just a moment|checking your browser|attention required|einen moment|überprüfung|einen augenblick|verifizierung ihres browsers/i.test(title);
|
||||
}
|
||||
// Speichert einen Screenshot + die aktuelle URL/Titel zur Fehlerdiagnose
|
||||
async function saveDebugScreenshot(userId, page) {
|
||||
const dir = `${PROFILE_BASE_DIR}/${userId}`;
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
await page.screenshot({ path: `${dir}/last-failure.png`, fullPage: false });
|
||||
fs.writeFileSync(`${dir}/last-failure.json`, JSON.stringify({
|
||||
url: page.url(), title: await page.title().catch(() => '?'), at: new Date().toISOString(),
|
||||
}));
|
||||
}
|
||||
function isLoginUrl(url) {
|
||||
return /bambulab\.com\/[^/]*\/?sign-in|makerworld\.com\/[a-z]{2}\/sign-in/i.test(url || '');
|
||||
@@ -155,7 +164,8 @@ async function attemptLogin(userId, browser, page) {
|
||||
]);
|
||||
const pwField = await findFirstSelector(page, ['input[type="password"]']);
|
||||
if (!emailField || !pwField) {
|
||||
throw new Error('Login-Formular nicht gefunden (Selektoren evtl. veraltet)');
|
||||
await saveDebugScreenshot(userId, page).catch(() => {});
|
||||
throw new Error(`Login-Formular nicht gefunden (Selektoren evtl. veraltet) — Seite: "${await page.title().catch(()=>'?')}" (${page.url()})`);
|
||||
}
|
||||
|
||||
await page.type(emailField, creds.username, { delay: 25 });
|
||||
@@ -192,6 +202,7 @@ async function attemptLogin(userId, browser, page) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
await saveDebugScreenshot(userId, page).catch(() => {});
|
||||
throw new Error('Login fehlgeschlagen (weder Daten noch Code-Feld erkannt) — evtl. falsche Zugangsdaten oder geändertes Seitenlayout');
|
||||
}
|
||||
|
||||
@@ -205,14 +216,17 @@ async function fetchStatsViaBrowserInner(userId, forceRefresh) {
|
||||
}
|
||||
|
||||
let browser;
|
||||
let page;
|
||||
try {
|
||||
browser = await launchBrowser(userId);
|
||||
const page = await browser.newPage();
|
||||
page = await browser.newPage();
|
||||
await page.setUserAgent(USER_AGENT);
|
||||
await page.setViewport({ width: 1366, height: 900 });
|
||||
await page.goto(DATA_URL, { waitUntil: 'networkidle2', timeout: 45000 });
|
||||
|
||||
for (let i = 0; i < 3 && await isCloudflareChallenge(page); i++) {
|
||||
// Bis zu 20 Sekunden Zeit geben, falls Cloudflare eine automatische
|
||||
// Managed-Challenge zeigt (löst sich bei echtem Chromium meist selbst)
|
||||
for (let i = 0; i < 5 && await isCloudflareChallenge(page); i++) {
|
||||
await new Promise(r => setTimeout(r, 4000));
|
||||
}
|
||||
|
||||
@@ -223,7 +237,10 @@ async function fetchStatsViaBrowserInner(userId, forceRefresh) {
|
||||
}
|
||||
|
||||
if (!parsed) {
|
||||
throw new Error('__NEXT_DATA__ nicht gefunden (weder Daten noch erkennbare Login-/Challenge-Seite) — Seitenlayout evtl. geändert');
|
||||
const debugTitle = await page.title().catch(() => '?');
|
||||
const debugUrl = page.url();
|
||||
await saveDebugScreenshot(userId, page).catch(() => {});
|
||||
throw new Error(`__NEXT_DATA__ nicht gefunden — Seite: "${debugTitle}" (${debugUrl}). Debug-Screenshot verfügbar über GET /debug-screenshot`);
|
||||
}
|
||||
|
||||
const data = mapStatisticalData(parsed);
|
||||
@@ -325,15 +342,26 @@ router.get('/login-status', authenticate, (req, res) => {
|
||||
const userId = req.user.id;
|
||||
const cache = getCache(userId);
|
||||
const creds = getCredentials(userId);
|
||||
const debugPath = `${PROFILE_BASE_DIR}/${userId}/last-failure.json`;
|
||||
let debugInfo = null;
|
||||
try { debugInfo = JSON.parse(fs.readFileSync(debugPath, 'utf8')); } catch {}
|
||||
res.json({
|
||||
configured: !!creds,
|
||||
username: creds?.username || null,
|
||||
lastFetchedAt: cache.fetchedAt || null,
|
||||
lastError: cache.error || null,
|
||||
needsCode: pendingLoginByUser.has(userId),
|
||||
debugScreenshot: debugInfo,
|
||||
});
|
||||
});
|
||||
|
||||
// Letzter Fehl-Screenshot (Diagnose) — nur der eigene
|
||||
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);
|
||||
});
|
||||
|
||||
router.post('/credentials', authenticate, async (req, res) => {
|
||||
const { username, password } = req.body;
|
||||
if (!username || !password) return res.status(400).json({ error: 'Benutzername und Passwort erforderlich' });
|
||||
|
||||
Reference in New Issue
Block a user