Forgets commited on
Commit
de5e8e0
·
verified ·
1 Parent(s): 724aa42

Update Api.js

Browse files
Files changed (1) hide show
  1. Api.js +93 -137
Api.js CHANGED
@@ -1,224 +1,180 @@
1
  /**
2
- * API.js
 
3
  */
4
  const express = require('express');
5
  const { connect } = require("puppeteer-real-browser");
6
  const fs = require('fs');
7
  const path = require('path');
8
 
 
 
 
 
 
 
9
  const app = express();
10
  const port = process.env.PORT || 7860;
11
- const authToken = process.env.authToken || null; // set in env/secrets
12
 
13
  global.browserLimit = 100;
14
  global.timeOut = 300000;
15
 
16
- // cache
17
  const CACHE_DIR = path.join(__dirname, "cache");
18
- const CACHE_FILE = path.join(CACHE_DIR, "cache.json");
19
  const CACHE_TTL = 5 * 60 * 1000;
20
  const CACHE_AUTOSAVE = process.env.CACHE_AUTOSAVE === "true";
21
 
22
- function readCache(type, taskId) {
23
- const file = path.join(CACHE_DIR, type, `${taskId}.json`);
24
- if (!fs.existsSync(file)) return null;
25
-
26
- try {
27
- const data = JSON.parse(fs.readFileSync(file, 'utf-8'));
28
- console.log(`cache check: ${type}:${taskId} => ${data ? "HIT" : "MISS"}`);
29
- if (Date.now() - data.timestamp < CACHE_TTL) {
30
- return data;
31
- }
32
- return null;
33
- } catch {
34
- return null;
35
- }
36
- }
37
 
38
  function writeCache(type, taskId, value) {
39
  const dir = path.join(CACHE_DIR, type);
40
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
41
-
42
  const file = path.join(dir, `${taskId}.json`);
43
  const data = { timestamp: Date.now(), ...value };
44
-
45
  fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf-8');
46
- console.log(`cache saved: ${type}:${taskId}`);
47
  }
48
 
49
  function cleanCache() {
50
  const types = ["turnstile", "recaptcha3", "recaptcha2", "interstitial", "error"];
51
  const now = Date.now();
52
- const TTL = 60 * 60 * 1000;
53
-
54
  types.forEach(type => {
55
  const dir = path.join(CACHE_DIR, type);
56
- if (!fs.existsSync(dir)) return;
57
-
58
- fs.readdirSync(dir).forEach(file => {
59
- const filePath = path.join(dir, file);
60
- try {
61
- const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
62
- if (now - data.timestamp > TTL) {
63
- fs.unlinkSync(filePath);
64
- console.log(`cache expired: ${filePath}`);
65
- }
66
- } catch {
67
- fs.unlinkSync(filePath);
68
- }
69
- });
70
  });
71
  }
72
-
73
  setInterval(cleanCache, 600 * 1000);
74
 
75
  app.use(express.json());
76
  app.use(express.urlencoded({ extended: true }));
 
77
  const tasks = {};
78
 
79
- /**
80
- * Authorization middleware
81
- * - If authToken is not set on server, middleware allows requests (convenience for dev).
82
- * - If authToken is set, requires either:
83
- * Authorization: Bearer <token>
84
- * OR header: key: <token>
85
- */
86
  app.use((req, res, next) => {
87
- if (!authToken) return next(); // no token configured => allow
88
-
89
- const authHdr = (req.get('Authorization') || req.get('authorization') || '').trim();
90
- const bearer = authHdr.startsWith('Bearer ') ? authHdr.slice(7).trim() : authHdr;
91
- const keyHeader = (req.get('key') || req.get('Key') || '').trim();
92
-
93
- if (bearer === authToken || keyHeader === authToken) return next();
94
-
95
  res.status(401).json({ status: "error", message: "Unauthorized" });
96
  });
97
 
98
- // Api_route
99
  app.get("/", (req, res) => {
100
- const baseUrl = `${req.protocol}://${req.get('host')}`;
101
- const uptime = process.uptime();
102
  res.json({
103
- message: "Welcome",
104
- server: {
105
- domain: baseUrl,
106
- version: "7.3.0",
107
- uptime: `${Math.floor(uptime)} seconds`,
108
- limit: global.browserLimit,
109
- timeout: global.timeOut,
110
- status: "recently running"
111
- },
112
  solvers: ["turnstile", "recaptcha2", "recaptcha3", "interstitial"]
113
  });
114
  });
115
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  app.post('/solve', async (req, res) => {
118
  const { type, domain, siteKey, taskId, action, proxy, isInvisible } = req.body;
119
 
 
120
  if (taskId) {
121
  const task = tasks[taskId];
122
  if (!task) return res.status(404).json({ status: "error", message: "Task not found" });
123
-
124
- if (task.status === "pending") {
125
- return res.json({ status: "processing" });
126
- }
127
-
128
  return res.json(task);
129
  }
130
 
131
  const newTaskId = Date.now().toString(36);
132
  tasks[newTaskId] = { status: "pending" };
133
- console.log(`New : ${newTaskId}=${type}:${domain}`);
134
 
 
135
  (async () => {
 
136
  try {
137
- const ctx = await init_browser(proxy?.server);
138
- const page = ctx.page;
139
  let result;
140
 
141
  switch (type) {
142
  case "turnstile":
143
- result = await turnstile({ domain, siteKey, action, proxy }, page);
144
- tasks[newTaskId] = { status: "done", ...result };
145
- console.log(`done: ${newTaskId}=${type}:${domain}`);
146
- if (CACHE_AUTOSAVE) writeCache("turnstile", newTaskId, tasks[newTaskId]);
147
  break;
148
-
149
  case "interstitial":
150
- result = await interstitial({ domain, proxy }, page);
151
- tasks[newTaskId] = { status: "done", ...result };
152
- console.log(`done: ${newTaskId}=${type}:${domain}`);
153
- if (CACHE_AUTOSAVE) writeCache("interstitial", newTaskId, tasks[newTaskId]);
154
  break;
155
-
156
  case "recaptcha2":
157
- result = await recaptchaV2({ domain, siteKey, action, isInvisible, proxy }, page);
158
- tasks[newTaskId] = { status: "done", ...result };
159
- console.log(`done: ${newTaskId}=${type}:${domain}`);
160
- if (CACHE_AUTOSAVE) writeCache("recaptcha2", newTaskId, tasks[newTaskId]);
161
  break;
162
-
163
  case "recaptcha3":
164
- result = await recaptchaV3({ domain, siteKey, action, proxy }, page);
165
- tasks[newTaskId] = { status: "done", ...result };
166
- console.log(`done: ${newTaskId}=${type}:${domain}`);
167
- if (CACHE_AUTOSAVE) writeCache("recaptcha3", newTaskId, tasks[newTaskId]);
168
  break;
169
-
170
  default:
171
- tasks[newTaskId] = { status: "error", message: "Invalid type" };
172
  }
173
 
174
- await ctx.browser.close();
175
- console.log(`Browser closed ${newTaskId}`);
 
 
176
  } catch (err) {
177
- tasks[newTaskId] = { status: "error", message: "totally failed" };
178
- console.error(`failed: ${newTaskId}=${type}:${domain}`);
179
- console.error("Detailed error:", err);
 
 
 
 
180
  }
181
  })();
182
 
183
  res.json({ taskId: newTaskId, status: "pending" });
184
  });
185
 
186
- // init_browser
187
- async function init_browser(proxyServer = null) {
188
- const connectOptions = {
189
- headless: false,
190
- turnstile: true,
191
- connectOption: { defaultViewport: null },
192
- disableXvfb: false,
193
- };
194
- if (proxyServer) connectOptions.args = [`--proxy-server=${proxyServer}`];
195
-
196
- const { browser } = await connect(connectOptions);
197
- const [page] = await browser.pages();
198
-
199
- await page.goto('about:blank');
200
- await page.setRequestInterception(true);
201
- page.on('request', (req) => {
202
- const type = req.resourceType();
203
- if (["image", "stylesheet", "font", "media"].includes(type)) req.abort();
204
- else req.continue();
205
- });
206
- console.log(`initialized${proxyServer ? "proxy= " + proxyServer : ""}`);
207
-
208
- return { browser, page };
209
- }
210
-
211
-
212
- const turnstile = require('./Api/turnstile');
213
- const interstitial = require('./Api/interstitial');
214
- const recaptchaV2 = require('./Api/recaptcha2');
215
- const recaptchaV3 = require('./Api/recaptcha3');
216
-
217
- app.use((req, res) => {
218
- res.status(404).json({ message: 'Not Found' });
219
- console.warn(`error: ${req.method} ${req.originalUrl}`);
220
- });
221
 
222
  app.listen(port, () => {
223
- console.log(`Server running: http://localhost:${port}`);
224
- });
 
 
 
1
  /**
2
+ * API.js - Optimized for Hugging Face Docker
3
+ * Powered by Gemini
4
  */
5
  const express = require('express');
6
  const { connect } = require("puppeteer-real-browser");
7
  const fs = require('fs');
8
  const path = require('path');
9
 
10
+ // Import Solver Modules ti awal sangkan kadefinisikeun
11
+ const turnstile = require('./Api/turnstile');
12
+ const interstitial = require('./Api/interstitial');
13
+ const recaptchaV2 = require('./Api/recaptcha2');
14
+ const recaptchaV3 = require('./Api/recaptcha3');
15
+
16
  const app = express();
17
  const port = process.env.PORT || 7860;
18
+ const authToken = process.env.authToken || null;
19
 
20
  global.browserLimit = 100;
21
  global.timeOut = 300000;
22
 
23
+ // Cache Configuration
24
  const CACHE_DIR = path.join(__dirname, "cache");
 
25
  const CACHE_TTL = 5 * 60 * 1000;
26
  const CACHE_AUTOSAVE = process.env.CACHE_AUTOSAVE === "true";
27
 
28
+ if (!fs.existsSync(CACHE_DIR)) fs.mkdirSync(CACHE_DIR, { recursive: true });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  function writeCache(type, taskId, value) {
31
  const dir = path.join(CACHE_DIR, type);
32
  if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
 
33
  const file = path.join(dir, `${taskId}.json`);
34
  const data = { timestamp: Date.now(), ...value };
 
35
  fs.writeFileSync(file, JSON.stringify(data, null, 2), 'utf-8');
 
36
  }
37
 
38
  function cleanCache() {
39
  const types = ["turnstile", "recaptcha3", "recaptcha2", "interstitial", "error"];
40
  const now = Date.now();
 
 
41
  types.forEach(type => {
42
  const dir = path.join(CACHE_DIR, type);
43
+ if (fs.existsSync(dir)) {
44
+ fs.readdirSync(dir).forEach(file => {
45
+ const filePath = path.join(dir, file);
46
+ try {
47
+ const data = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
48
+ if (now - data.timestamp > 3600000) fs.unlinkSync(filePath);
49
+ } catch (e) { fs.unlinkSync(filePath); }
50
+ });
51
+ }
 
 
 
 
 
52
  });
53
  }
 
54
  setInterval(cleanCache, 600 * 1000);
55
 
56
  app.use(express.json());
57
  app.use(express.urlencoded({ extended: true }));
58
+
59
  const tasks = {};
60
 
61
+ // Authorization Middleware
 
 
 
 
 
 
62
  app.use((req, res, next) => {
63
+ if (!authToken) return next();
64
+ const authHdr = (req.get('Authorization') || req.get('key') || '').replace('Bearer ', '').trim();
65
+ if (authHdr === authToken) return next();
 
 
 
 
 
66
  res.status(401).json({ status: "error", message: "Unauthorized" });
67
  });
68
 
69
+ // Root Route
70
  app.get("/", (req, res) => {
 
 
71
  res.json({
72
+ status: "online",
73
+ version: "7.3.0",
 
 
 
 
 
 
 
74
  solvers: ["turnstile", "recaptcha2", "recaptcha3", "interstitial"]
75
  });
76
  });
77
 
78
+ /**
79
+ * init_browser - Optimized for Docker/HuggingFace
80
+ */
81
+ async function init_browser(proxyServer = null) {
82
+ const connectOptions = {
83
+ headless: false, // Wajib false mun make puppeteer-real-browser + Xvfb
84
+ turnstile: true,
85
+ connectOption: {
86
+ defaultViewport: null,
87
+ args: [
88
+ '--no-sandbox',
89
+ '--disable-setuid-sandbox',
90
+ '--disable-dev-shm-usage', // Krusial pikeun Docker
91
+ '--disable-gpu',
92
+ '--no-zygote',
93
+ '--single-process'
94
+ ]
95
+ },
96
+ disableXvfb: false,
97
+ };
98
+
99
+ if (proxyServer) {
100
+ connectOptions.connectOption.args.push(`--proxy-server=${proxyServer}`);
101
+ }
102
+
103
+ const { browser } = await connect(connectOptions);
104
+ const [page] = await browser.pages();
105
+
106
+ // Set simple interception pikeun ngahemat resource
107
+ await page.setRequestInterception(true);
108
+ page.on('request', (req) => {
109
+ if (["image", "font", "media"].includes(req.resourceType())) req.abort();
110
+ else req.continue();
111
+ });
112
+
113
+ return { browser, page };
114
+ }
115
+
116
+
117
 
118
  app.post('/solve', async (req, res) => {
119
  const { type, domain, siteKey, taskId, action, proxy, isInvisible } = req.body;
120
 
121
+ // Check Task Status
122
  if (taskId) {
123
  const task = tasks[taskId];
124
  if (!task) return res.status(404).json({ status: "error", message: "Task not found" });
 
 
 
 
 
125
  return res.json(task);
126
  }
127
 
128
  const newTaskId = Date.now().toString(36);
129
  tasks[newTaskId] = { status: "pending" };
130
+ console.log(`[New Task] ${newTaskId} | Type: ${type} | Domain: ${domain}`);
131
 
132
+ // Async Execution
133
  (async () => {
134
+ let ctx = null;
135
  try {
136
+ ctx = await init_browser(proxy?.server);
 
137
  let result;
138
 
139
  switch (type) {
140
  case "turnstile":
141
+ result = await turnstile({ domain, siteKey, action, proxy }, ctx.page);
 
 
 
142
  break;
 
143
  case "interstitial":
144
+ result = await interstitial({ domain, proxy }, ctx.page);
 
 
 
145
  break;
 
146
  case "recaptcha2":
147
+ result = await recaptchaV2({ domain, siteKey, action, isInvisible, proxy }, ctx.page);
 
 
 
148
  break;
 
149
  case "recaptcha3":
150
+ result = await recaptchaV3({ domain, siteKey, action, proxy }, ctx.page);
 
 
 
151
  break;
 
152
  default:
153
+ throw new Error("Invalid solver type");
154
  }
155
 
156
+ tasks[newTaskId] = { status: "done", ...result };
157
+ if (CACHE_AUTOSAVE) writeCache(type, newTaskId, tasks[newTaskId]);
158
+ console.log(`[Success] Task ${newTaskId} completed`);
159
+
160
  } catch (err) {
161
+ tasks[newTaskId] = { status: "error", message: err.message || "Execution failed" };
162
+ console.error(`[Error] Task ${newTaskId}:`, err.message);
163
+ } finally {
164
+ if (ctx && ctx.browser) {
165
+ await ctx.browser.close().catch(() => {});
166
+ console.log(`[System] Browser closed for ${newTaskId}`);
167
+ }
168
  }
169
  })();
170
 
171
  res.json({ taskId: newTaskId, status: "pending" });
172
  });
173
 
174
+ app.use((req, res) => res.status(404).json({ message: 'Route not found' }));
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
175
 
176
  app.listen(port, () => {
177
+ console.log(`========================================`);
178
+ console.log(`🚀 Solver API Running on port ${port}`);
179
+ console.log(`========================================`);
180
+ });