Spaces:
Paused
Paused
| /** | |
| * 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()} | |
| ========================================== | |
| `); | |
| }); | |