kavion commited on
Commit
46fb463
·
unverified ·
1 Parent(s): e56c61c

feat: add Express API server as Turnstile solver endpoint

Browse files

- Add app.js: Express API server compatible with /bypass/turnstile-min
- Add package.json: dependencies (express, puppeteer-real-browser)
- Add Dockerfile: HuggingFace Space Docker config with Chromium + Xvfb
- Update README.md: HF Space YAML front matter + API docs

Files changed (4) hide show
  1. Dockerfile +65 -0
  2. README.md +43 -224
  3. app.js +102 -0
  4. package.json +18 -0
Dockerfile ADDED
@@ -0,0 +1,65 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Dockerfile untuk HuggingFace Space (Node.js + Puppeteer)
2
+ FROM node:20-slim
3
+
4
+ # Install dependensi sistem yang dibutuhkan Puppeteer + Xvfb + ffmpeg
5
+ RUN apt-get update && apt-get install -y \
6
+ chromium \
7
+ xvfb \
8
+ ffmpeg \
9
+ ca-certificates \
10
+ fonts-liberation \
11
+ libasound2 \
12
+ libatk-bridge2.0-0 \
13
+ libatk1.0-0 \
14
+ libc6 \
15
+ libcairo2 \
16
+ libcups2 \
17
+ libdbus-1-3 \
18
+ libexpat1 \
19
+ libfontconfig1 \
20
+ libgbm1 \
21
+ libgcc1 \
22
+ libglib2.0-0 \
23
+ libgtk-3-0 \
24
+ libnspr4 \
25
+ libnss3 \
26
+ libpango-1.0-0 \
27
+ libpangocairo-1.0-0 \
28
+ libstdc++6 \
29
+ libx11-6 \
30
+ libx11-xcb1 \
31
+ libxcb1 \
32
+ libxcomposite1 \
33
+ libxcursor1 \
34
+ libxdamage1 \
35
+ libxext6 \
36
+ libxfixes3 \
37
+ libxi6 \
38
+ libxrandr2 \
39
+ libxrender1 \
40
+ libxss1 \
41
+ libxtst6 \
42
+ lsb-release \
43
+ wget \
44
+ xdg-utils \
45
+ --no-install-recommends \
46
+ && rm -rf /var/lib/apt/lists/*
47
+
48
+ # Arahkan Puppeteer ke Chromium sistem (bukan download sendiri)
49
+ ENV PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
50
+ ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
51
+
52
+ # Port yang diekspos HuggingFace Space
53
+ ENV PORT=7860
54
+
55
+ WORKDIR /app
56
+
57
+ # Install dependencies npm
58
+ COPY package.json .
59
+ RUN npm install --omit=dev
60
+
61
+ # Copy semua source
62
+ COPY . .
63
+
64
+ # Jalankan dengan Xvfb (virtual display untuk Puppeteer non-headless)
65
+ CMD ["sh", "-c", "Xvfb :99 -screen 0 1280x720x24 & sleep 1 && DISPLAY=:99 node app.js"]
README.md CHANGED
@@ -1,255 +1,74 @@
1
- # 🔓 Cloudflare Solver
2
-
3
- Dua solver otomatis untuk proteksi Cloudflare menggunakan `puppeteer-real-browser`:
4
-
5
- - **`cloudflare-solver.js`** — Bypass Cloudflare challenge page dan ekstrak cookie `cf_clearance`
6
- - **`turnstile-solver.js`** — Solve Cloudflare Turnstile widget dan dapatkan token
7
-
8
  ---
9
-
10
- ## ✨ Fitur
11
-
12
- ### Cloudflare Solver
13
- - Bypass halaman challenge Cloudflare secara otomatis
14
- - Deteksi tipe challenge: `non-interactive`, `managed`, `interactive`
15
- - Klik iframe Cloudflare (checkbox → label → widget → body fallback)
16
- - Ekstrak cookie `cf_clearance` + semua cookies + cookie string siap pakai
17
- - Support proxy URL (`http://user:pass@ip:port`) dan proxy file
18
- - Random User-Agent dari `data/useragents.txt`
19
-
20
- ### Turnstile Solver
21
- - **Mode Sitekey** — inject fake page dengan sitekey eksplisit
22
- - **Mode Page** — solve langsung dari halaman target (auto-detect turnstile)
23
- - Screen recording tiap sesi (`.mp4`) via `puppeteer-screen-recorder`
24
- - Support proxy dengan autentikasi
25
- - Output token dalam format JSON
26
-
27
  ---
28
 
29
- ## 📦 Instalasi
30
-
31
- ```bash
32
- npm install puppeteer-real-browser puppeteer-screen-recorder
33
- ```
34
-
35
- > **Requirements tambahan:**
36
- > - Node.js >= 18
37
- > - `ffmpeg` — dibutuhkan `puppeteer-screen-recorder` untuk encode MP4
38
- > - `Xvfb` — untuk headless di Linux (dihandle otomatis oleh `puppeteer-real-browser`)
39
-
40
- ---
41
-
42
- ## 🔧 Penggunaan
43
-
44
- ### Cloudflare Solver
45
-
46
- ```js
47
- const { solveCloudflare } = require('./cloudflare-solver');
48
-
49
- const result = await solveCloudflare({
50
- url: 'https://example.com',
51
- headless: false, // tampilkan browser
52
- timeout: 30, // timeout dalam detik
53
- // proxy: 'http://user:pass@ip:port',
54
- // proxyFile: 'data/proxies.txt',
55
- });
56
-
57
- console.log(result);
58
- ```
59
 
60
- #### Options
61
 
62
- | Option | Tipe | Default | Deskripsi |
63
- |--------|------|---------|-----------|
64
- | `url` | `string` | — | **Wajib.** URL target |
65
- | `headless` | `boolean` | `true` | Tampilkan browser atau tidak |
66
- | `proxy` | `string` | `null` | Proxy URL: `http://user:pass@ip:port` |
67
- | `proxyFile` | `string` | `null` | Path ke file daftar proxy (satu per baris) |
68
- | `timeout` | `number` | `30` | Batas waktu solve challenge (detik) |
69
 
70
- #### Output
71
 
72
- Berhasil:
 
 
 
 
73
 
 
74
  ```json
75
  {
76
- "success": true,
77
- "url": "https://example.com",
78
- "proxy": null,
79
- "user_agent": "Mozilla/5.0 ...",
80
- "cf_clearance": "abc123...",
81
- "all_cookies": [...],
82
- "cookie_string": "cf_clearance=abc123; ...",
83
- "unix_timestamp": 1747000000,
84
- "timestamp": "2025-05-12T00:00:00.000Z",
85
- "domain": ".example.com"
86
- }
87
- ```
88
-
89
- Gagal:
90
-
91
- ```json
92
- {
93
- "success": false,
94
- "url": "https://example.com",
95
- "error": "Failed to obtain cf_clearance cookie"
96
- }
97
- ```
98
-
99
- ---
100
-
101
- ### Turnstile Solver
102
-
103
- ```js
104
- const TurnstileSolver = require('./turnstile-solver');
105
-
106
- const solver = new TurnstileSolver({
107
- timeout: 60000,
108
- record: true,
109
- recordDir: './recordings',
110
- // proxy: { host: '...', port: 8080, username: 'user', password: 'pass' },
111
- width: 1280,
112
- height: 720,
113
- });
114
-
115
- await solver.initialize();
116
-
117
- // Mode 1: pakai sitekey eksplisit
118
- const result = await solver.solve('https://example.com', '0x4AAAAAAA...');
119
-
120
- // Mode 2: auto-detect dari halaman target
121
- const result = await solver.solve('https://example.com');
122
-
123
- console.log(result);
124
- await solver.cleanup();
125
- ```
126
-
127
- #### Options Konstruktor
128
-
129
- | Option | Tipe | Default | Deskripsi |
130
- |--------|------|---------|-----------|
131
- | `timeout` | `number` | `60000` | Batas waktu tunggu token (ms) |
132
- | `record` | `boolean` | `false` | Aktifkan screen recording |
133
- | `recordDir` | `string` | `./recordings` | Folder output recording |
134
- | `proxy` | `object` | `null` | `{ host, port, username, password }` |
135
- | `width` | `number` | `1280` | Viewport & recording width |
136
- | `height` | `number` | `720` | Viewport & recording height |
137
-
138
- #### Methods
139
-
140
- | Method | Deskripsi |
141
- |--------|-----------|
142
- | `initialize()` | Launch browser (opsional, auto-called saat `solve`) |
143
- | `solve(url, siteKey?)` | Solve turnstile — sitekey mode kalau diisi, page mode kalau tidak |
144
- | `solveWithSitekey(url, siteKey)` | Inject fake page dengan sitekey eksplisit |
145
- | `solveFromPage(url)` | Solve langsung dari halaman target |
146
- | `cleanup()` | Tutup browser dan reset state |
147
-
148
- #### Output
149
-
150
- Berhasil:
151
-
152
- ```json
153
- {
154
- "success": true,
155
  "creator": "XAi Community",
156
  "token": "0.eyJhbGci...",
157
  "time": 4.231
158
  }
159
  ```
160
 
161
- Gagal:
162
-
163
  ```json
164
  {
165
- "success": false,
166
- "error": "Token invalid or empty",
167
- "time": 60.012
168
  }
169
  ```
170
 
171
- ---
172
-
173
- ## 📁 Screen Recording
174
-
175
- Kalau `record: true`, setiap sesi disimpan ke `recordDir` dengan format:
176
-
177
- ```
178
- recordings/
179
- ├── sitekey_0x4AAAAAAA__1747123456789.mp4 ← mode sitekey
180
- └── page_solve_1747123456789.mp4 ← mode page
181
- ```
182
-
183
- ---
184
-
185
- ## ⚙️ Cara Kerja
186
-
187
- ### Cloudflare Solver
188
-
189
- 1. Launch browser via `puppeteer-real-browser` dengan `turnstile: false`
190
- 2. Navigate ke URL target
191
- 3. Cek apakah `cf_clearance` sudah ada di cookies
192
- 4. Kalau belum, deteksi tipe challenge dari HTML (`cType: '...'`)
193
- 5. Cari iframe `challenges.cloudflare.com` lalu klik: checkbox → label → widget → body (fallback)
194
- 6. Poll cookies sampai `cf_clearance` muncul atau timeout
195
- 7. Return `cf_clearance`, semua cookies, dan `cookie_string` siap pakai
196
-
197
- ### Turnstile Solver — Mode Sitekey (`solveWithSitekey`)
198
-
199
- 1. Aktifkan request interception
200
- 2. Intercept request ke URL target, balas dengan fake HTML yang berisi widget Turnstile
201
- 3. `puppeteer-real-browser` dengan `turnstile: true` handle solve otomatis
202
- 4. Token diambil dari input hidden `[name="cf-response"]`
203
-
204
- ### Turnstile Solver — Mode Page (`solveFromPage`)
205
-
206
- 1. Inject polling script via `evaluateOnNewDocument` yang cek `window.turnstile.getResponse()`
207
- 2. Saat token tersedia, tulis ke input hidden `[name="cf-response"]`
208
- 3. `waitForSelector` menunggu sampai token siap
209
-
210
- ---
211
 
212
- ## 🚀 Contoh Lengkap (`run.js`)
213
 
214
  ```js
215
- const { solveCloudflare } = require('./cloudflare-solver');
216
- const TurnstileSolver = require('./turnstile-solver');
217
-
218
- // Cloudflare challenge
219
- async function runCloudflare() {
220
- const result = await solveCloudflare({
221
- url: 'https://example.com',
222
- headless: false,
223
- timeout: 60,
224
- });
225
- console.log(result);
226
- }
227
-
228
- // Turnstile
229
- async function runTurnstile() {
230
- const solver = new TurnstileSolver({ record: true });
231
- try {
232
- const res = await solver.solve('https://www.waifu2x.net', '0x4AAAAAABqlY7DKXMzoS81U');
233
- console.log(res);
234
- } finally {
235
- await solver.cleanup();
236
- }
237
- }
238
-
239
- (async () => {
240
- // await runCloudflare();
241
- await runTurnstile();
242
- })();
243
  ```
244
 
245
- ---
246
 
247
- ## ⚠️ Disclaimer
 
 
248
 
249
- Tool ini dibuat untuk keperluan edukasi dan testing. Penggunaan untuk bypass proteksi tanpa izin pemilik situs adalah tanggung jawab pengguna sepenuhnya.
250
 
251
- ---
 
 
 
252
 
253
- ## 📜 License
254
 
255
- MIT License Copyright (c) 2025 XAi Community
 
 
 
 
 
 
 
 
1
  ---
2
+ title: Cloudflare Turnstile Solver API
3
+ emoji: 🔓
4
+ colorFrom: gray
5
+ colorTo: blue
6
+ sdk: docker
7
+ pinned: false
 
 
 
 
 
 
 
 
 
 
 
 
8
  ---
9
 
10
+ # 🔓 Cloudflare Turnstile Solver API
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
11
 
12
+ Self-hosted API pengganti layanan Turnstile eksternal. Menggunakan `puppeteer-real-browser` untuk solve Turnstile secara otomatis.
13
 
14
+ ## Endpoint
 
 
 
 
 
 
15
 
16
+ ### `GET /bypass/turnstile-min`
17
 
18
+ | Parameter | Wajib | Deskripsi |
19
+ |-----------|-------|-----------|
20
+ | `url` | ✅ | URL target yang memiliki Turnstile |
21
+ | `siteKey` | ❌ | Sitekey Turnstile (pakai mode page kalau kosong) |
22
+ | `apikey` | ❌ | API key (sesuai env `API_KEY` jika dikonfigurasi) |
23
 
24
+ **Response sukses:**
25
  ```json
26
  {
27
+ "status": true,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
  "creator": "XAi Community",
29
  "token": "0.eyJhbGci...",
30
  "time": 4.231
31
  }
32
  ```
33
 
34
+ **Response gagal:**
 
35
  ```json
36
  {
37
+ "status": false,
38
+ "message": "Pesan error"
 
39
  }
40
  ```
41
 
42
+ ### `GET /`
43
+ Health check — cek status solver dan daftar endpoint.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
44
 
45
+ ## Penggunaan di Bot
46
 
47
  ```js
48
+ const apiUrl = global.API(
49
+ 'theresav',
50
+ '/bypass/turnstile-min',
51
+ {
52
+ url: 'https://izen.lol',
53
+ siteKey: '0x4AAAAAADNEi_2N24gpQqY0'
54
+ },
55
+ 'apikey'
56
+ );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  ```
58
 
59
+ Atau langsung ke Space ini:
60
 
61
+ ```
62
+ https://kavion-cf.hf.space/bypass/turnstile-min?url=https://izen.lol&siteKey=0x4AAAAAADNEi_2N24gpQqY0
63
+ ```
64
 
65
+ ## Environment Variables
66
 
67
+ | Variabel | Deskripsi |
68
+ |----------|-----------|
69
+ | `API_KEY` | (Opsional) Kunci proteksi endpoint. Kosongkan jika publik. |
70
+ | `PORT` | Port server (default: 7860) |
71
 
72
+ ## Disclaimer
73
 
74
+ Dibuat untuk keperluan edukasi dan testing. MIT License — XAi Community.
app.js ADDED
@@ -0,0 +1,102 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use strict';
2
+
3
+ const express = require('express');
4
+ const TurnstileSolver = require('./turnstile-solver');
5
+
6
+ const app = express();
7
+ const PORT = process.env.PORT || 7860;
8
+
9
+ app.use(express.json());
10
+
11
+ // Singleton solver — browser tetap hidup selama server berjalan
12
+ const solver = new TurnstileSolver({
13
+ timeout: 90000,
14
+ record: false,
15
+ width: 1280,
16
+ height: 720,
17
+ });
18
+
19
+ // Inisialisasi browser saat startup
20
+ let ready = false;
21
+ (async () => {
22
+ try {
23
+ await solver.initialize();
24
+ ready = true;
25
+ console.log('[API] Solver ready.');
26
+ } catch (e) {
27
+ console.error('[API] Solver init failed:', e.message);
28
+ }
29
+ })();
30
+
31
+ /**
32
+ * GET /bypass/turnstile-min
33
+ * Query params:
34
+ * - url : URL target (wajib)
35
+ * - siteKey : Sitekey Turnstile (opsional, kalau tidak ada pakai mode page)
36
+ * - apikey : API key sederhana untuk proteksi (opsional, cek env API_KEY)
37
+ *
38
+ * Response (sukses):
39
+ * { status: true, token: "...", time: 3.2 }
40
+ *
41
+ * Response (gagal):
42
+ * { status: false, message: "..." }
43
+ */
44
+ app.get('/bypass/turnstile-min', async (req, res) => {
45
+ // Cek API key kalau dikonfigurasi
46
+ const apiKey = process.env.API_KEY;
47
+ if (apiKey && req.query.apikey !== apiKey) {
48
+ return res.status(401).json({ status: false, message: 'Invalid API key.' });
49
+ }
50
+
51
+ const { url, siteKey } = req.query;
52
+
53
+ if (!url) {
54
+ return res.status(400).json({ status: false, message: 'Parameter "url" wajib diisi.' });
55
+ }
56
+
57
+ if (!ready) {
58
+ return res.status(503).json({ status: false, message: 'Solver belum siap, coba lagi sebentar.' });
59
+ }
60
+
61
+ try {
62
+ const result = await solver.solve(url, siteKey || null);
63
+
64
+ if (!result.success) {
65
+ return res.status(500).json({
66
+ status: false,
67
+ message: result.error || 'Gagal mendapatkan token Turnstile.',
68
+ time: result.time,
69
+ });
70
+ }
71
+
72
+ return res.json({
73
+ status: true,
74
+ creator: 'XAi Community',
75
+ token: result.token,
76
+ time: result.time,
77
+ });
78
+ } catch (e) {
79
+ return res.status(500).json({ status: false, message: e.message });
80
+ }
81
+ });
82
+
83
+ // Health check
84
+ app.get('/', (req, res) => {
85
+ res.json({
86
+ status: 'ok',
87
+ ready,
88
+ endpoints: [
89
+ 'GET /bypass/turnstile-min?url=<URL>&siteKey=<SITEKEY>&apikey=<KEY>',
90
+ ],
91
+ });
92
+ });
93
+
94
+ app.listen(PORT, '0.0.0.0', () => {
95
+ console.log(`[API] Server jalan di port ${PORT}`);
96
+ });
97
+
98
+ // Cleanup saat proses mati
99
+ process.on('SIGTERM', async () => {
100
+ await solver.cleanup();
101
+ process.exit(0);
102
+ });
package.json ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "name": "cloudflare-solver-api",
3
+ "version": "1.0.0",
4
+ "description": "Self-hosted Turnstile solver API — pengganti API Turnstile eksternal",
5
+ "main": "app.js",
6
+ "scripts": {
7
+ "start": "node app.js",
8
+ "dev": "node app.js"
9
+ },
10
+ "dependencies": {
11
+ "express": "^4.19.2",
12
+ "puppeteer-real-browser": "^1.3.5",
13
+ "puppeteer-screen-recorder": "^3.0.6"
14
+ },
15
+ "engines": {
16
+ "node": ">=18"
17
+ }
18
+ }