fourmovie commited on
Commit
02f17f2
·
1 Parent(s): 9d5ffec
Files changed (2) hide show
  1. endpoints/recaptchav3.js +0 -82
  2. index.js +140 -48
endpoints/recaptchav3.js DELETED
@@ -1,82 +0,0 @@
1
- async function recaptchav3({ domain, siteKey, proxy, action = 'homepage' }, page) {
2
- if (!domain) throw new Error("Missing domain parameter");
3
- if (!siteKey) throw new Error("Missing siteKey parameter");
4
-
5
- const timeout = global.timeOut || 60000;
6
- let isResolved = false;
7
-
8
- const cl = setTimeout(async () => {
9
- if (!isResolved) {
10
- throw new Error("Timeout Error");
11
- }
12
- }, timeout);
13
-
14
- try {
15
- if (proxy?.username && proxy?.password) {
16
- await page.authenticate({
17
- username: proxy.username,
18
- password: proxy.password,
19
- });
20
- }
21
-
22
- const htmlContent = `
23
- <!DOCTYPE html>
24
- <html lang="en">
25
- <head>
26
- <script src="https://www.google.com/recaptcha/api.js?render=${siteKey}"></script>
27
- </head>
28
- <body>
29
- <input type="hidden" id="recaptchav3Token" name="recaptchav3">
30
- <script>
31
- grecaptcha.ready(function() {
32
- grecaptcha.execute("${siteKey}", {action: "${action}"}).then(function(token) {
33
- document.getElementById("recaptchav3Token").value = token;
34
- });
35
- });
36
- </script>
37
- </body>
38
- </html>
39
- `;
40
-
41
- await page.setRequestInterception(true);
42
- page.removeAllListeners("request");
43
- page.on("request", async (request) => {
44
- if ([domain, domain + "/"].includes(request.url()) && request.resourceType() === "document") {
45
- await request.respond({
46
- status: 200,
47
- contentType: "text/html",
48
- body: htmlContent,
49
- });
50
- } else {
51
- await request.continue();
52
- }
53
- });
54
-
55
- await page.goto(domain, { waitUntil: "domcontentloaded" });
56
-
57
- await page.waitForFunction(() => {
58
- const tokenInput = document.getElementById("recaptchav3Token");
59
- return tokenInput && tokenInput.value.length > 10;
60
- }, { timeout });
61
-
62
- const token = await page.evaluate(() => {
63
- try {
64
- return document.getElementById("recaptchav3Token").value;
65
- } catch {
66
- return null;
67
- }
68
- });
69
-
70
- isResolved = true;
71
- clearTimeout(cl);
72
-
73
- if (!token || token.length < 10) throw new Error("Failed to get token");
74
- return token;
75
-
76
- } catch (e) {
77
- clearTimeout(cl);
78
- throw e;
79
- }
80
- }
81
-
82
- module.exports = recaptchav3;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
index.js CHANGED
@@ -4,9 +4,9 @@ const fs = require("fs")
4
  const path = require("path")
5
 
6
  const app = express()
7
- const port = process.env.PORT || 8080
8
  const authToken = process.env.authToken || null
9
- const DOMAIN = 'https://rezaharis-cf.hf.space'
10
 
11
  global.browserLimit = Number(process.env.browserLimit) || 20
12
  global.timeOut = Number(process.env.timeOut) || 60000
@@ -49,42 +49,97 @@ function writeCache(key, value) {
49
  app.use(express.json())
50
  app.use(express.urlencoded({ extended: true }))
51
 
 
 
 
 
 
 
 
 
 
 
 
 
52
  app.get('/', (req, res) => {
53
- res.json({
54
- message: 'Server is running',
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
55
  domain: DOMAIN,
56
- status: 'active'
 
57
  })
58
  })
59
 
60
  if (process.env.NODE_ENV !== 'development') {
61
- let server = app.listen(port, () => {
62
  console.log(`Server running on port ${port}`)
63
  console.log(`Domain: ${DOMAIN}`)
64
  })
65
  try {
66
  server.timeout = global.timeOut
 
67
  } catch {}
68
  }
69
 
70
- async function createBrowser(proxyServer = null) {
71
  const connectOptions = {
72
- headless: false,
73
  turnstile: true,
74
- connectOption: { defaultViewport: null },
75
- disableXvfb: false,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
76
  }
77
 
78
  if (proxyServer) {
79
- connectOptions.args = [`--proxy-server=${proxyServer}`]
80
  }
81
 
82
  const { browser } = await connect(connectOptions)
83
 
 
84
  const pages = await browser.pages()
85
  const page = pages[0]
86
 
87
- await page.goto('about:blank')
 
 
 
 
 
88
 
89
  await page.setRequestInterception(true)
90
  page.on('request', (req) => {
@@ -99,30 +154,29 @@ async function createBrowser(proxyServer = null) {
99
  return { browser, page }
100
  }
101
 
 
102
  const turnstile = require('./endpoints/turnstile')
103
  const cloudflare = require('./endpoints/cloudflare')
104
 
 
105
  app.post('/cloudflare', async (req, res) => {
106
  const data = req.body
107
- if (!data || typeof data.mode !== 'string') {
108
- return res.status(400).json({ message: 'Bad Request: missing or invalid mode' })
109
- }
110
- if (authToken && data.authToken !== authToken) {
111
- return res.status(401).json({ message: 'Unauthorized' })
112
  }
113
 
 
 
 
114
  if (global.browserLimit <= 0) {
115
- return res.status(429).json({ message: 'Too Many Requests' })
116
  }
117
 
118
- let cacheKey, cached
119
- if (data.mode === "iuam") {
120
-
121
- cacheKey = JSON.stringify(data)
122
- cached = readCache(cacheKey)
123
- if (cached) {
124
- return res.status(200).json({ ...cached, cached: true })
125
- }
126
  }
127
 
128
  global.browserLimit--
@@ -131,50 +185,88 @@ app.post('/cloudflare', async (req, res) => {
131
 
132
  try {
133
  const proxyServer = data.proxy ? `${data.proxy.hostname}:${data.proxy.port}` : null
134
- const ctx = await createBrowser(proxyServer)
135
  browser = ctx.browser
136
  page = ctx.page
137
 
138
- await page.goto('about:blank')
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
139
 
140
- switch (data.mode) {
141
- case "turnstile":
 
 
 
 
142
 
143
- result = await turnstile(data, page)
144
- .then(token => ({ token }))
145
- .catch(err => ({ code: 500, message: err.message }))
146
- break
147
 
148
- case "iuam":
 
 
149
 
150
- result = await cloudflare(data, page)
151
- .then(r => ({ ...r }))
152
- .catch(err => ({ code: 500, message: err.message }))
 
 
153
 
154
- if (!result.code || result.code === 200) {
155
- writeCache(cacheKey, result)
156
- }
157
- break
158
 
159
- default:
160
- result = { code: 400, message: 'Invalid mode' }
161
- }
162
  } catch (err) {
163
- result = { code: 500, message: err.message }
164
  } finally {
165
  if (browser) {
166
- try { await browser.close() } catch {}
 
 
 
 
167
  }
168
  global.browserLimit++
169
  }
170
 
171
- res.status(result.code ?? 200).json(result)
172
  })
173
 
174
  app.use((req, res) => {
175
  res.status(404).json({ message: 'Not Found' })
176
  })
177
 
 
 
 
 
 
 
178
  if (process.env.NODE_ENV === 'development') {
179
  module.exports = app
180
  }
 
4
  const path = require("path")
5
 
6
  const app = express()
7
+ const port = process.env.PORT || 7860
8
  const authToken = process.env.authToken || null
9
+ const DOMAIN = 'https://rezaharis-cf.hf.space' // DOMAINNYA DI SINI
10
 
11
  global.browserLimit = Number(process.env.browserLimit) || 20
12
  global.timeOut = Number(process.env.timeOut) || 60000
 
49
  app.use(express.json())
50
  app.use(express.urlencoded({ extended: true }))
51
 
52
+ // Middleware untuk auth token
53
+ app.use((req, res, next) => {
54
+ if (authToken && req.path !== '/health' && req.path !== '/') {
55
+ const token = req.body.authToken || req.query.authToken
56
+ if (token !== authToken) {
57
+ return res.status(401).json({ message: 'Unauthorized' })
58
+ }
59
+ }
60
+ next()
61
+ })
62
+
63
+ // Root endpoint
64
  app.get('/', (req, res) => {
65
+ res.status(200).json({
66
+ message: 'Cloudflare Solver API',
67
+ status: 'active',
68
+ domain: DOMAIN, // TAMPILKAN DOMAIN DI RESPONSE
69
+ endpoints: ['/cloudflare', '/turnstile', '/health', '/info']
70
+ })
71
+ })
72
+
73
+ // Health check endpoint
74
+ app.get('/health', (req, res) => {
75
+ res.status(200).json({
76
+ status: 'OK',
77
+ browserLimit: global.browserLimit,
78
+ domain: DOMAIN,
79
+ timestamp: new Date().toISOString()
80
+ })
81
+ })
82
+
83
+ // Info endpoint
84
+ app.get('/info', (req, res) => {
85
+ res.status(200).json({
86
+ browserLimit: global.browserLimit,
87
+ timeOut: global.timeOut,
88
  domain: DOMAIN,
89
+ cacheEnabled: true,
90
+ cacheTTL: CACHE_TTL
91
  })
92
  })
93
 
94
  if (process.env.NODE_ENV !== 'development') {
95
+ let server = app.listen(port, '0.0.0.0', () => {
96
  console.log(`Server running on port ${port}`)
97
  console.log(`Domain: ${DOMAIN}`)
98
  })
99
  try {
100
  server.timeout = global.timeOut
101
+ server.keepAliveTimeout = 60000
102
  } catch {}
103
  }
104
 
105
+ async function createBrowser(proxyServer = null, proxyAuth = null) {
106
  const connectOptions = {
107
+ headless: true,
108
  turnstile: true,
109
+ connectOption: {
110
+ defaultViewport: null,
111
+ args: [
112
+ '--no-sandbox',
113
+ '--disable-setuid-sandbox',
114
+ '--disable-dev-shm-usage',
115
+ '--disable-accelerated-2d-canvas',
116
+ '--no-first-run',
117
+ '--no-zygote',
118
+ '--disable-gpu',
119
+ '--disable-web-security',
120
+ '--disable-features=site-per-process',
121
+ '--disable-blink-features=AutomationControlled'
122
+ ]
123
+ },
124
+ disableXvfb: true,
125
  }
126
 
127
  if (proxyServer) {
128
+ connectOptions.connectOption.args.push(`--proxy-server=${proxyServer}`)
129
  }
130
 
131
  const { browser } = await connect(connectOptions)
132
 
133
+ // Set stealth
134
  const pages = await browser.pages()
135
  const page = pages[0]
136
 
137
+ // Remove webdriver property
138
+ await page.evaluateOnNewDocument(() => {
139
+ Object.defineProperty(navigator, 'webdriver', {
140
+ get: () => undefined,
141
+ })
142
+ })
143
 
144
  await page.setRequestInterception(true)
145
  page.on('request', (req) => {
 
154
  return { browser, page }
155
  }
156
 
157
+ // Import endpoints
158
  const turnstile = require('./endpoints/turnstile')
159
  const cloudflare = require('./endpoints/cloudflare')
160
 
161
+ // ENDPOINT CLOUDFLARE - GUNAKAN DOMAIN YANG SUDAH DIATUR
162
  app.post('/cloudflare', async (req, res) => {
163
  const data = req.body
164
+ if (!data) {
165
+ return res.status(400).json({ message: 'Bad Request: missing data' })
 
 
 
166
  }
167
 
168
+ // Gunakan domain dari request atau domain default
169
+ const domain = data.domain || DOMAIN
170
+
171
  if (global.browserLimit <= 0) {
172
+ return res.status(429).json({ message: 'Too Many Requests: browser limit reached' })
173
  }
174
 
175
+ // Check cache
176
+ const cacheKey = JSON.stringify({...data, domain})
177
+ const cached = readCache(cacheKey)
178
+ if (cached) {
179
+ return res.status(200).json({ ...cached, cached: true })
 
 
 
180
  }
181
 
182
  global.browserLimit--
 
185
 
186
  try {
187
  const proxyServer = data.proxy ? `${data.proxy.hostname}:${data.proxy.port}` : null
188
+ const ctx = await createBrowser(proxyServer, data.proxy)
189
  browser = ctx.browser
190
  page = ctx.page
191
 
192
+ // Pass domain ke fungsi cloudflare
193
+ result = await cloudflare({...data, domain}, page)
194
+
195
+ if (result.cf_clearance) {
196
+ writeCache(cacheKey, result)
197
+ }
198
+
199
+ } catch (err) {
200
+ result = {
201
+ cf_clearance: null,
202
+ user_agent: null,
203
+ elapsed_time: 0,
204
+ error: err.message
205
+ }
206
+ } finally {
207
+ if (browser) {
208
+ try {
209
+ await browser.close()
210
+ } catch (err) {
211
+ console.error('Error closing browser:', err.message)
212
+ }
213
+ }
214
+ global.browserLimit++
215
+ }
216
+
217
+ res.status(200).json(result)
218
+ })
219
 
220
+ // Endpoint khusus untuk Turnstile
221
+ app.post('/turnstile', async (req, res) => {
222
+ const data = req.body
223
+ if (!data || !data.sitekey || !data.url) {
224
+ return res.status(400).json({ message: 'Bad Request: missing sitekey or url' })
225
+ }
226
 
227
+ if (global.browserLimit <= 0) {
228
+ return res.status(429).json({ message: 'Too Many Requests: browser limit reached' })
229
+ }
 
230
 
231
+ global.browserLimit--
232
+ let result
233
+ let browser, page
234
 
235
+ try {
236
+ const proxyServer = data.proxy ? `${data.proxy.hostname}:${data.proxy.port}` : null
237
+ const ctx = await createBrowser(proxyServer, data.proxy)
238
+ browser = ctx.browser
239
+ page = ctx.page
240
 
241
+ const token = await turnstile(data, page)
242
+ result = { token }
 
 
243
 
 
 
 
244
  } catch (err) {
245
+ result = { error: err.message }
246
  } finally {
247
  if (browser) {
248
+ try {
249
+ await browser.close()
250
+ } catch (err) {
251
+ console.error('Error closing browser:', err.message)
252
+ }
253
  }
254
  global.browserLimit++
255
  }
256
 
257
+ res.status(200).json(result)
258
  })
259
 
260
  app.use((req, res) => {
261
  res.status(404).json({ message: 'Not Found' })
262
  })
263
 
264
+ // Error handling middleware
265
+ app.use((err, req, res, next) => {
266
+ console.error('Server Error:', err)
267
+ res.status(500).json({ message: 'Internal Server Error' })
268
+ })
269
+
270
  if (process.env.NODE_ENV === 'development') {
271
  module.exports = app
272
  }