/** * API.js — FINAL TOTAL * Rate limiting + Cloudflare Interstitial + Cache + Task Queue */ const express = require('express'); const { connect } = require("puppeteer-real-browser"); const fs = require('fs'); const path = require('path'); const app = express(); const port = process.env.PORT || 7860; // ================= CONFIG ================= global.browserLimit = 100; // Maksimal browser bersamaan global.timeOut = 120000; // 2 menit timeout const CACHE_DIR = path.join(__dirname, "cache"); const CACHE_TTL = 5 * 60 * 1000; // 5 menit const MAX_CONCURRENT = global.browserLimit; // ================= QUEUE ================= const taskQueue = []; let activeTasks = 0; function processQueue() { if (activeTasks >= MAX_CONCURRENT || taskQueue.length === 0) return; const { task, resolve, reject } = taskQueue.shift(); activeTasks++; task() .then(result => { activeTasks--; resolve(result); processQueue(); }) .catch(error => { activeTasks--; reject(error); processQueue(); }); } function addTask(task) { return new Promise((resolve, reject) => { taskQueue.push({ task, resolve, reject }); processQueue(); }); } // ================= CACHE ================= function readCache(type, taskId) { const file = path.join(CACHE_DIR, type, `${taskId}.json`); if (!fs.existsSync(file)) return null; try { const data = JSON.parse(fs.readFileSync(file, 'utf-8')); if (Date.now() - data.timestamp < CACHE_TTL) return data; return null; } catch { return null; } } function writeCache(type, taskId, value) { const dir = path.join(CACHE_DIR, type); if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true }); const file = path.join(dir, `${taskId}.json`); const data = { timestamp: Date.now(), ...value }; fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf-8'); console.log(`cache SAVED: ${type}:${taskId}`); } // ================= CLEAN CACHE PERIODIC ================= setInterval(() => { const types = ["recaptcha2", "recaptcha3", "turnstile", "interstitial"]; const now = Date.now(); types.forEach(type => { const dir = path.join(CACHE_DIR, type); if (!fs.existsSync(dir)) return; fs.readdirSync(dir).forEach(file => { const filePath = path.join(dir, file); try { const data = JSON.parse(fs.readFileSync(filePath, 'utf-8')); if (now - data.timestamp > CACHE_TTL) fs.unlinkSync(filePath); } catch {} }); }); }, 600000); // 10 menit // ================= EXPRESS ================= app.use(express.json()); app.use(express.urlencoded({ extended: true })); const tasks = {}; // Health endpoint app.get("/health", (req, res) => { res.json({ status: "healthy", activeTasks, queueLength: taskQueue.length, maxConcurrent: MAX_CONCURRENT, memory: process.memoryUsage() }); }); // Root endpoint app.get("/", (req, res) => { res.json({ message: "CAPTCHA Solver API", version: "8.0.0", limits: { concurrent: MAX_CONCURRENT, timeout: global.timeOut }, endpoints: ["/solve", "/health", "/stats"] }); }); // ================= SOLVE ENDPOINT ================= app.post('/solve', async (req, res) => { const { type, domain, siteKey, taskId, action, proxy, isInvisible } = req.body; // Polling jika taskId dikirim if (taskId) { const task = tasks[taskId]; if (!task) return res.status(404).json({ status: "error", message: "Task not found" }); if (task.status === "pending") return res.json({ status: "processing", position: taskQueue.length }); return res.json({ status: task.status, solution: task.solution }); } // Buat task baru const newTaskId = Date.now().toString(36) + Math.random().toString(36).substr(2); tasks[newTaskId] = { status: "pending" }; console.log(`🆕 New task: ${newTaskId}=${type}:${domain}`); // Cek cache const cached = readCache(type, newTaskId); if (cached) { tasks[newTaskId] = cached; return res.json({ taskId: newTaskId, status: "done", cached: true }); } try { const result = await addTask(async () => { console.log(`🚀 Processing: ${newTaskId}`); const ctx = await init_browser(proxy?.server); const page = ctx.page; let solution = {}; switch (type) { case "recaptcha2": solution = await recaptchaV2({ domain, siteKey, action, isInvisible, proxy }, page); break; case "recaptcha3": solution = await recaptchaV3({ domain, siteKey, action, proxy }, page); break; case "turnstile": solution = await turnstile({ domain, siteKey, action, proxy }, page); break; case "interstitial": solution = await interstitial({ domain, proxy }, page); break; default: throw new Error("Invalid type"); } tasks[newTaskId] = { status: "done", solution }; writeCache(type, newTaskId, tasks[newTaskId]); await ctx.browser.close(); console.log(`✅ Completed: ${newTaskId}`); return solution; }); res.json({ taskId: newTaskId, status: "queued", position: taskQueue.length, estimatedWait: taskQueue.length * 30000 }); } catch (error) { tasks[newTaskId] = { status: "error", message: error.message }; console.error(`❌ Failed: ${newTaskId} - ${error.message}`); res.status(500).json({ taskId: newTaskId, status: "error", message: error.message }); } }); // GET /solve?taskId=xxx — polling app.get('/solve', (req, res) => { const { taskId } = req.query; if (!taskId) return res.status(400).json({ error: "Missing taskId" }); const task = tasks[taskId]; if (!task) return res.status(404).json({ error: "Task not found" }); if (task.status === "pending") return res.json({ status: "processing", position: taskQueue.length }); return res.json({ status: task.status, solution: task.solution }); }); // ================= BROWSER INIT ================= async function init_browser(proxyServer = null) { const connectOptions = { headless: false, turnstile: true, connectOption: { defaultViewport: null, args: [ '--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage', '--disable-accelerated-2d-canvas', '--disable-gpu', '--window-size=1024,768' ] }, disableXvfb: false, }; if (proxyServer) connectOptions.connectOption.args.push(`--proxy-server=${proxyServer}`); const { browser } = await connect(connectOptions); const [page] = await browser.pages(); // Block resources yang tidak perlu await page.setRequestInterception(true); page.on('request', req => { const type = req.resourceType(); if (["image", "stylesheet", "font", "media"].includes(type)) req.abort(); else req.continue(); }); console.log(`🌐 Browser initialized${proxyServer ? " with proxy" : ""}`); return { browser, page }; } // ================= LOAD MODULES ================= const recaptchaV2 = require('./Api/recaptcha2'); const recaptchaV3 = require('./Api/recaptcha3'); const turnstile = require('./Api/turnstile'); const interstitial = require('./interstitial'); // pastikan file ini ada // ================= STATS ================= app.get('/stats', (req, res) => { res.json({ activeTasks, queueLength: taskQueue.length, maxConcurrent: MAX_CONCURRENT, memory: process.memoryUsage(), uptime: process.uptime() }); }); // 404 handler app.use((req, res) => res.status(404).json({ message: 'Endpoint not found' })); // ================= START ================= app.listen(port, () => { console.log(` ========================================== 🚀 CAPTCHA Solver API v8.0.0 📍 Port: ${port} ⚡ Concurrent Limit: ${MAX_CONCURRENT} ⏱️ Timeout: ${global.timeOut}ms 🕐 Started: ${new Date().toISOString()} ========================================== `); });