File size: 5,610 Bytes
d8ea8ce
 
 
 
 
 
 
 
 
ca7264e
d8ea8ce
 
 
 
ca7264e
 
d8ea8ce
 
 
 
 
 
 
0554a46
bc15be9
 
d8ea8ce
0554a46
d8ea8ce
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ca7264e
0554a46
ca7264e
 
 
 
e30a13d
0554a46
ca7264e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e30a13d
ca7264e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e30a13d
d8ea8ce
ca7264e
 
c092763
f266c6c
ca7264e
 
f266c6c
ca7264e
 
 
08b3eb8
ca7264e
 
d07887a
097870f
ca7264e
f266c6c
 
 
 
 
 
 
 
d8ea8ce
 
ca7264e
0554a46
ca7264e
 
 
 
d8ea8ce
 
 
ca7264e
d8ea8ce
f266c6c
d8ea8ce
 
ca7264e
f266c6c
ca7264e
 
 
 
 
 
 
d8ea8ce
ca7264e
d8ea8ce
bc15be9
 
d8ea8ce
ca7264e
 
 
d8ea8ce
 
 
 
ca7264e
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
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 = `
            <!DOCTYPE html>
            <html lang="en">
            <head>
                <meta charset="UTF-8">
                <meta name="viewport" content="width=device-width, initial-scale=1.0">
                <title>Turnstile Solver</title>
            </head>
            <body>
                <div class="cf-turnstile" data-sitekey="${siteKey}" data-callback="turnstileCallback"></div>
                
                <script src="https://challenges.cloudflare.com/turnstile/v0/api.js" async defer></script>
                
                <script>
                    console.log("Widget rendered...");
                    function turnstileCallback(token) { 
                        console.log("Token received in browser!");
                        window.cf_token = token; 
                    }
                </script>
            </body>
            </html>`;

            // 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}`));