/** * API.js */ 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; const authToken = process.env.authToken || null; global.browserLimit = 100; global.timeOut = 300000; // cache const CACHE_DIR = path.join(__dirname, "cache"); const CACHE_FILE = path.join(CACHE_DIR, "cache.json"); const CACHE_TTL = 5 * 60 * 1000; const CACHE_AUTOSAVE = process.env.CACHE_AUTOSAVE === "true"; 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')); console.log(`cache check: ${type}:${taskId} => ${data ? "HIT" : "MISS"}`); 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}`); } function cleanCache() { const types = ["turnstile", "recaptcha3", "recaptcha2", "interstitial", "error"]; const now = Date.now(); const TTL = 60 * 60 * 1000; 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 > TTL) { fs.unlinkSync(filePath); console.log(`cache expired: ${filePath}`); } } catch { fs.unlinkSync(filePath); } }); }); } setInterval(cleanCache, 600 * 1000); app.use(express.json()); app.use(express.urlencoded({ extended: true })); const tasks = {}; // Api_route app.get("/", (req, res) => { const baseUrl = `${req.protocol}://${req.get('host')}`; const uptime = process.uptime(); res.json({ message: "Welcome", server: { domain: baseUrl, version: "7.3.0", uptime: `${Math.floor(uptime)} seconds`, limit: global.browserLimit, timeout: global.timeOut, status: "recently running" }, solvers: ["turnstile", "recaptcha2", "recaptcha3", "interstitial"] }); }); app.post('/solve', async (req, res) => { const { type, domain, siteKey, taskId, action, proxy, isInvisible } = req.body; 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" }); } return res.json(task); } const newTaskId = Date.now().toString(36); tasks[newTaskId] = { status: "pending" }; console.log(`New : ${newTaskId}=${type}:${domain}`); (async () => { try { const ctx = await init_browser(proxy?.server); const page = ctx.page; let result; switch (type) { case "turnstile": result = await turnstile({ domain, siteKey, action, proxy }, page); tasks[newTaskId] = { status: "done", ...result }; console.log(`done: ${newTaskId}=${type}:${domain}`); if (CACHE_AUTOSAVE) writeCache("turnstile", newTaskId, tasks[newTaskId]); break; case "interstitial": result = await interstitial({ domain, proxy }, page); tasks[newTaskId] = { status: "done", ...result }; console.log(`done: ${newTaskId}=${type}:${domain}`); if (CACHE_AUTOSAVE) writeCache("interstitial", newTaskId, tasks[newTaskId]); break; case "recaptcha2": result = await recaptchaV2({ domain, siteKey, action, isInvisible, proxy }, page); tasks[newTaskId] = { status: "done", ...result }; console.log(`done: ${newTaskId}=${type}:${domain}`); if (CACHE_AUTOSAVE) writeCache("recaptcha2", newTaskId, tasks[newTaskId]); break; case "recaptcha3": result = await recaptchaV3({ domain, siteKey, action, proxy }, page); tasks[newTaskId] = { status: "done", ...result }; console.log(`done: ${newTaskId}=${type}:${domain}`); if (CACHE_AUTOSAVE) writeCache("recaptcha3", newTaskId, tasks[newTaskId]); break; default: tasks[newTaskId] = { status: "error", message: "Invalid type" }; } await ctx.browser.close(); console.log(`Browser closed ${newTaskId}`); } catch (err) { tasks[newTaskId] = { status: "error", message: "totally failed" }; console.error(`failed: ${newTaskId}=${type}:${domain}`); console.error("Detailed error:", err); } })(); res.json({ taskId: newTaskId, status: "pending" }); }); // init_browser async function init_browser(proxyServer = null) { const connectOptions = { headless: false, turnstile: true, connectOption: { defaultViewport: null }, disableXvfb: false, }; if (proxyServer) connectOptions.args = [`--proxy-server=${proxyServer}`]; const { browser } = await connect(connectOptions); const [page] = await browser.pages(); await page.goto('about:blank'); 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(`initialized${proxyServer ? "proxy= " + proxyServer : ""}`); return { browser, page }; } const turnstile = require('./Api/turnstile'); const interstitial = require('./Api/interstitial'); const recaptchaV2 = require('./Api/recaptcha2'); const recaptchaV3 = require('./Api/recaptcha3'); app.use((req, res) => { res.status(404).json({ message: 'Not Found' }); console.warn(`error: ${req.method} ${req.originalUrl}`); }); app.listen(port, () => { console.log(`Server running: http://localhost:${port}`); });