const express = require('express'); const cors = require('cors'); const { connect } = require("puppeteer-real-browser"); const app = express(); app.use(cors()); app.use(express.json()); const PORT = process.env.PORT || 7860; const MAX_CONCURRENT_BROWSERS = 2; // Limit 2 Browser agar RAM aman let activeBrowsers = 0; async function createBrowser(proxy = null) { const options = { headless: false, // Wajib false turnstile: true, // Auto-click turnstile bawaan library args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--no-first-run', '--no-zygote', '--disable-gpu', '--window-size=1920,1080', '--disable-blink-features=AutomationControlled' ], executablePath: process.env.CHROME_PATH || "/usr/bin/google-chrome-stable", customConfig: {}, connectOption: { defaultViewport: null } }; if (proxy) { options.args.push(`--proxy-server=${proxy.hostname}:${proxy.port}`); } const { browser, page } = await connect(options); if (proxy && proxy.username && proxy.password) { await page.authenticate({ username: proxy.username, password: proxy.password }); } return { browser, page }; } async function handleTurnstile(page, url, siteKey) { return new Promise(async (resolve, reject) => { // PERBAIKAN 1: Timeout diperpanjang jadi 120 detik (2 menit) const timeout = setTimeout(() => { reject(new Error("Timeout Turnstile (120s limit reached)")); }, 120000); try { console.log(`[Turnstile] Preparing Injection for ${url}`); // Template HTML minimalis untuk memancing Turnstile keluar const htmlContent = ` Turnstile Solver
`; // Intercept request ke URL target dan ganti isinya dengan HTML di atas await page.setRequestInterception(true); page.once('request', request => { request.respond({ status: 200, contentType: 'text/html', body: htmlContent }); }); // Handle request lain (gambar/css) agar tidak error page.on('request', req => { if (req.isInterceptResolutionHandled()) return; req.continue(); }); // Buka URL (ini akan memicu intercept di atas) console.log(`[Turnstile] Navigating...`); await page.goto(url, { waitUntil: "domcontentloaded", timeout: 60000 }); // Polling Token const checkToken = setInterval(async () => { try { const token = await page.evaluate(() => window.cf_token); if (token) { clearInterval(checkToken); clearTimeout(timeout); console.log(`[Turnstile] Token found: ${token.substring(0, 15)}...`); resolve({ token: token }); } } catch (e) {} }, 1000); } catch (e) { clearTimeout(timeout); reject(e); } }); } app.post('/bypass', async (req, res) => { const { url, mode, siteKey, proxy } = req.body; if (!url || !mode) return res.status(400).json({ status: "error", message: "Missing url or mode" }); if (activeBrowsers >= MAX_CONCURRENT_BROWSERS) { return res.status(429).json({ status: "busy", message: "Server busy, try again in a few seconds" }); } activeBrowsers++; let browserInstance = null; try { console.log(`[REQ] Processing: ${url} | Mode: ${mode}`); const { browser, page } = await createBrowser(proxy); browserInstance = browser; let result; if (mode === 'turnstile') { result = await handleTurnstile(page, url, siteKey); } else { throw new Error("Invalid mode (only turnstile supported in this version)"); } console.log(`[SUCCESS] Operation completed.`); res.json({ status: "success", ...result }); } catch (error) { console.error(`[ERROR] ${error.message}`); res.status(500).json({ status: "error", message: error.message }); } finally { if (browserInstance) { try { await browserInstance.close(); } catch {} } activeBrowsers--; } }); app.get('/', (req, res) => res.send("Turnstile Solver Ready (v3 - 120s Timeout)")); app.listen(PORT, () => console.log(`Server listening on port ${PORT}`));