vipsphi commited on
Commit
c135c3c
·
verified ·
1 Parent(s): 2ce98e2

Upload 2 files

Browse files
Files changed (2) hide show
  1. public/docs.html +395 -0
  2. public/index.html +960 -0
public/docs.html ADDED
@@ -0,0 +1,395 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="vi">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>API Documentation - Professional Captcha Solver</title>
8
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet" />
9
+ <link rel="stylesheet"
10
+ href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/styles/atom-one-dark.min.css" />
11
+ <style>
12
+ body {
13
+ background-color: #0d1117;
14
+ color: #c9d1d9;
15
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica,
16
+ Arial, sans-serif;
17
+ }
18
+
19
+ .sidebar {
20
+ position: fixed;
21
+ top: 0;
22
+ bottom: 0;
23
+ left: 0;
24
+ z-index: 100;
25
+ padding: 48px 0 0;
26
+ box-shadow: inset -1px 0 0 rgba(255, 255, 255, 0.1);
27
+ width: 280px;
28
+ background-color: #161b22;
29
+ }
30
+
31
+ .sidebar-sticky {
32
+ position: relative;
33
+ top: 0;
34
+ height: calc(100vh - 48px);
35
+ padding-top: 0.5rem;
36
+ overflow-x: hidden;
37
+ overflow-y: auto;
38
+ }
39
+
40
+ .nav-link {
41
+ font-weight: 500;
42
+ color: #8b949e;
43
+ margin-bottom: 5px;
44
+ }
45
+
46
+ .nav-link:hover,
47
+ .nav-link.active {
48
+ color: #58a6ff;
49
+ background-color: rgba(255, 255, 255, 0.05);
50
+ border-radius: 6px;
51
+ }
52
+
53
+ main {
54
+ margin-left: 280px;
55
+ padding: 40px;
56
+ }
57
+
58
+ h1,
59
+ h2,
60
+ h3 {
61
+ color: #ffffff;
62
+ margin-top: 40px;
63
+ margin-bottom: 20px;
64
+ border-bottom: 1px solid #30363d;
65
+ padding-bottom: 10px;
66
+ }
67
+
68
+ h1 {
69
+ margin-top: 0;
70
+ border: none;
71
+ }
72
+
73
+ .card {
74
+ background-color: #161b22;
75
+ border: 1px solid #30363d;
76
+ margin-bottom: 20px;
77
+ }
78
+
79
+ .card-header {
80
+ background-color: #21262d;
81
+ border-bottom: 1px solid #30363d;
82
+ font-weight: bold;
83
+ color: #fff;
84
+ }
85
+
86
+ code {
87
+ color: #ff7b72;
88
+ word-wrap: break-word;
89
+ }
90
+
91
+ pre {
92
+ background-color: #0d1117;
93
+ border-radius: 6px;
94
+ padding: 15px;
95
+ border: 1px solid #30363d;
96
+ }
97
+
98
+ .badge-method {
99
+ background: #238636;
100
+ color: white;
101
+ padding: 5px 10px;
102
+ border-radius: 4px;
103
+ font-weight: bold;
104
+ }
105
+
106
+ .table {
107
+ color: #c9d1d9;
108
+ }
109
+
110
+ .table-dark {
111
+ --bs-table-bg: #161b22;
112
+ }
113
+
114
+ .api-endpoint {
115
+ background: #161b22;
116
+ padding: 15px;
117
+ border-radius: 6px;
118
+ border: 1px solid #30363d;
119
+ font-family: monospace;
120
+ font-size: 1.1em;
121
+ color: #a5d6ff;
122
+ }
123
+
124
+ .highlight-box {
125
+ background: rgba(56, 139, 253, 0.1);
126
+ border-left: 4px solid #58a6ff;
127
+ padding: 15px;
128
+ margin: 20px 0;
129
+ border-radius: 0 6px 6px 0;
130
+ }
131
+
132
+ .step-badge {
133
+ display: inline-block;
134
+ width: 24px;
135
+ height: 24px;
136
+ background: #58a6ff;
137
+ color: #0d1117;
138
+ border-radius: 50%;
139
+ text-align: center;
140
+ line-height: 24px;
141
+ font-weight: bold;
142
+ margin-right: 10px;
143
+ }
144
+ </style>
145
+ </head>
146
+
147
+ <body>
148
+ <nav class="sidebar">
149
+ <div class="position-sticky sidebar-sticky px-3">
150
+ <h4 class="text-white px-3 mb-4">📚 API Docs</h4>
151
+ <ul class="nav flex-column">
152
+ <li class="nav-item">
153
+ <a class="nav-link active" href="#intro">Giới thiệu</a>
154
+ </li>
155
+ <li class="nav-item">
156
+ <a class="nav-link" href="#auth">Xác thực (Auth)</a>
157
+ </li>
158
+ <li class="nav-item">
159
+ <a class="nav-link" href="#endpoint">API Endpoint</a>
160
+ </li>
161
+ <li class="nav-item">
162
+ <a class="nav-link" href="#proxy">Cấu hình Proxy</a>
163
+ </li>
164
+ <li class="nav-item">
165
+ <a class="nav-link" href="#integration">Quy trình Tích hợp</a>
166
+ </li>
167
+ <li class="nav-item">
168
+ <a class="nav-link" href="#nodejs">Code mẫu: Node.js</a>
169
+ </li>
170
+ <li class="nav-item">
171
+ <a class="nav-link" href="#python">Code mẫu: Python</a>
172
+ </li>
173
+ <li class="nav-item">
174
+ <a class="nav-link" href="#csharp">Code mẫu: C#</a>
175
+ </li>
176
+ </ul>
177
+ <div class="mt-5 px-3">
178
+ <a href="/" class="btn btn-outline-primary w-100">← Quay lại Dashboard</a>
179
+ </div>
180
+ </div>
181
+ </nav>
182
+
183
+ <main>
184
+ <section id="intro">
185
+ <h1>Giới thiệu</h1>
186
+ <p class="lead">
187
+ Enterprise Captcha Solver cung cấp giải pháp tự động vượt qua các hệ thống bảo mật captcha tiên tiến nhất hiện
188
+ nay.
189
+ </p>
190
+ <div class="highlight-box">
191
+ <strong>Smart Config Implementation:</strong> Hệ thống của chúng tôi sử dụng công nghệ "Smart Selection".
192
+ </div>
193
+ </section>
194
+
195
+ <section id="auth">
196
+ <h2>�� Xác thực (Authentication)</h2>
197
+ <p>
198
+ Mọi request gửi đến API đều cần phải có
199
+ <strong>x-api-key</strong> trong Header.
200
+ </p>
201
+ <pre><code class="language-http">x-api-key: YOUR_API_KEY_HERE</code></pre>
202
+ </section>
203
+
204
+ <section id="endpoint">
205
+ <h2>📡 API Endpoint</h2>
206
+ <p>Gửi yêu cầu POST đến địa chỉ sau để nhận Token:</p>
207
+
208
+ <div class="api-endpoint mb-4">
209
+ <span class="badge-method me-2">POST</span>
210
+ https://kinxsoftware.online/api/solve
211
+ </div>
212
+
213
+ <pre><code class="language-json">{
214
+ "action": "FLOW_GENERATION",
215
+ "proxy": "http://user:pass@ip:port"
216
+ }</code></pre>
217
+ </section>
218
+
219
+ <section id="proxy">
220
+ <h2>🌐 Cấu hình Proxy</h2>
221
+ <p>Hệ thống hỗ trợ truyền Proxy riêng biệt cho mỗi yêu cầu. Khi được truyền vào, trình duyệt giải captcha sẽ sử
222
+ dụng Proxy này để tránh bị Google chặn IP.</p>
223
+
224
+ <div class="card">
225
+ <div class="card-header">Định dạng Proxy hỗ trợ</div>
226
+ <div class="card-body text-light">
227
+ <ul>
228
+ <li><strong>Không xác thực:</strong> <code>http://host:port</code></li>
229
+ <li><strong>Có xác thực:</strong> <code>http://username:password@host:port</code></li>
230
+ </ul>
231
+ <p class="mb-0 text-warning"><i class="bi bi-exclamation-triangle"></i> Lưu ý: Hiện tại hệ thống hỗ trợ tốt
232
+ nhất cho HTTP Proxy.</p>
233
+ </div>
234
+ </div>
235
+ </section>
236
+
237
+ <section id="integration">
238
+ <h2>⚙️ Quy trình Tích hợp Chuẩn</h2>
239
+ <p>
240
+ Sau khi nhận được <code>token</code> thành công, hãy thực hiện tích hợp vào Request mục tiêu theo các bước sau:
241
+ </p>
242
+
243
+ <h3><span class="step-badge">1</span> Chèn vào Headers</h3>
244
+ <p>Thêm các header sau để xác thực Token với máy chủ mục tiêu:</p>
245
+ <pre><code class="language-http">x-goog-recaptcha-token: [CAPTCHA_TOKEN]
246
+ x-recaptcha-token: [CAPTCHA_TOKEN]</code></pre>
247
+
248
+ <h3><span class="step-badge">2</span> Chèn vào Body (Context)</h3>
249
+ <p>
250
+ Trong Payload của request chính, hãy đảm bảo cấu trúc <code>clientContext</code> được thiết lập đúng:
251
+ </p>
252
+ <pre><code class="language-json">{
253
+ "clientContext": {
254
+ "recaptchaToken": "[CAPTCHA_TOKEN]",
255
+ "userPaygateTier": "PAYGATE_TIER_TWO",
256
+ "tool": "PINHOLE"
257
+ }
258
+ }</code></pre>
259
+
260
+ <h3><span class="step-badge">3</span> Đồng bộ User-Agent</h3>
261
+ <p>
262
+ <strong>Quan trọng:</strong> Bạn phải sử dụng đúng User-Agent dưới đây để đảm bảo Token được chấp nhận:
263
+ </p>
264
+ <pre><code class="language-text">Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36</code></pre>
265
+ </section>
266
+
267
+ <section id="nodejs">
268
+ <h2>🟩 Code mẫu: Node.js (Built-in)</h2>
269
+ <pre><code class="language-javascript">const http = require('http');
270
+ const https = require('https');
271
+
272
+ /**
273
+ * Script kiểm tra API giải captcha với Proxy
274
+ * Hỗ trợ cả HTTP và HTTPS, tự động chọn module phù hợp
275
+ */
276
+ async function testCaptchaWithProxy() {
277
+ const API_URL = 'https://kinxsoftware.online/api/solve';
278
+ const API_KEY = 'YOUR_API_KEY_HERE';
279
+ const PROXY = 'http://user:pass@ip:port';
280
+
281
+ const postData = JSON.stringify({
282
+ action: 'FLOW_GENERATION',
283
+ proxy: PROXY
284
+ });
285
+
286
+ const url = new URL(API_URL);
287
+ const isHttps = url.protocol === 'https:';
288
+ const transport = isHttps ? https : http;
289
+
290
+ const options = {
291
+ hostname: url.hostname,
292
+ port: url.port || (isHttps ? 443 : 80),
293
+ path: url.pathname,
294
+ method: 'POST',
295
+ headers: {
296
+ 'Content-Type': 'application/json',
297
+ 'x-api-key': API_KEY,
298
+ 'Content-Length': Buffer.byteLength(postData)
299
+ },
300
+ // Bỏ qua kiểm tra SSL nếu là localhost hoặc server tự cấp chứng chỉ
301
+ rejectUnauthorized: false
302
+ };
303
+
304
+ console.log('--- Đang gửi yêu cầu giải captcha với Proxy ---');
305
+ console.log('URL:', API_URL);
306
+ console.log('Proxy:', PROXY);
307
+
308
+ const req = transport.request(options, (res) => {
309
+ let data = '';
310
+
311
+ res.on('data', (chunk) => {
312
+ data += chunk;
313
+ });
314
+
315
+ res.on('end', () => {
316
+ console.log('Phản hồi từ Server:', res.statusCode);
317
+ try {
318
+ const jsonResponse = JSON.parse(data);
319
+ console.log('Kết quả:', JSON.stringify(jsonResponse, null, 2));
320
+ } catch (e) {
321
+ console.log('Phản hồi không phải JSON:', data);
322
+ }
323
+ });
324
+ });
325
+
326
+ req.on('error', (e) => {
327
+ console.error('Lỗi kết nối:', e.message);
328
+ });
329
+
330
+ req.write(postData);
331
+ req.end();
332
+ }
333
+
334
+ testCaptchaWithProxy();</code></pre>
335
+ </section>
336
+
337
+ <section id="python">
338
+ <h2>🐍 Code mẫu: Python (Requests)</h2>
339
+ <pre><code class="language-python">import requests
340
+
341
+ def solve_and_run():
342
+ # BƯỚC 1: Lấy Token từ API
343
+ url = "https://kinxsoftware.online/api/solve"
344
+ payload = {
345
+ "action": "FLOW_GENERATION",
346
+ "proxy": "http://user:pass@ip:port" # <-- Thêm proxy tại đây
347
+ }
348
+ headers = {"x-api-key": "YOUR_API_KEY_HERE"}
349
+
350
+ res = requests.post(url, json=payload, headers=headers, verify=False)
351
+ data = res.json()
352
+ token = data.get("token")
353
+
354
+ print(f"Token: {token}")
355
+
356
+ # BƯỚC 2: Tích hợp vào request mục tiêu (giữ nguyên User-Agent)
357
+ # ... code tích hợp tiếp theo
358
+ </code></pre>
359
+ </section>
360
+
361
+ <section id="csharp">
362
+ <h2>🔷 Code mẫu: C# (HttpClient)</h2>
363
+ <pre><code class="language-csharp">using System.Net.Http.Json;
364
+
365
+ // BƯỚC 1: Lấy Token
366
+ var client = new HttpClient();
367
+ client.DefaultRequestHeaders.Add("x-api-key", "YOUR_API_KEY_HERE");
368
+
369
+ var payloadSolver = new {
370
+ action = "FLOW_GENERATION",
371
+ proxy = "http://user:pass@ip:port" // <-- Thêm proxy tại đây
372
+ };
373
+
374
+ // Gửi yêu cầu giải captcha
375
+ var solverRes = await client.PostAsJsonAsync("https://kinxsoftware.online/api/solve", payloadSolver);
376
+ var solverData = await solverRes.Content.ReadFromJsonAsync&lt;dynamic&gt;();
377
+ string token = solverData.token;
378
+
379
+ Console.WriteLine($"Token: {token}");
380
+ </code></pre>
381
+ </section>
382
+
383
+ <footer class="mt-5 text-center text-muted border-top pt-4">
384
+ <p>&copy; 2024 Captcha Solver Professional Service.</p>
385
+ </footer>
386
+ </main>
387
+
388
+ <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
389
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.9.0/highlight.min.js"></script>
390
+ <script>
391
+ hljs.highlightAll();
392
+ </script>
393
+ </body>
394
+
395
+ </html>
public/index.html ADDED
@@ -0,0 +1,960 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="vi">
3
+
4
+ <head>
5
+ <meta charset="UTF-8" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <title>Pro Captcha Solver - Dashboard</title>
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="/socket.io/socket.io.js"></script>
10
+ <!-- Font Inter for modern look -->
11
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet" />
12
+ <style>
13
+ body {
14
+ font-family: "Inter", sans-serif;
15
+ }
16
+
17
+ .modal-enter {
18
+ animation: fadeIn 0.3s ease-out;
19
+ }
20
+
21
+ @keyframes fadeIn {
22
+ from {
23
+ opacity: 0;
24
+ transform: scale(0.95);
25
+ }
26
+
27
+ to {
28
+ opacity: 1;
29
+ transform: scale(1);
30
+ }
31
+ }
32
+
33
+ .scrollbar-hide::-webkit-scrollbar {
34
+ display: none;
35
+ }
36
+
37
+ .scrollbar-hide {
38
+ -ms-overflow-style: none;
39
+ scrollbar-width: none;
40
+ }
41
+
42
+ /* Sidebar active link style */
43
+ .nav-item.active {
44
+ background-color: #f3f4f6;
45
+ color: #4f46e5;
46
+ border-right: 3px solid #4f46e5;
47
+ }
48
+ </style>
49
+ </head>
50
+
51
+ <body class="bg-gray-50 text-gray-800 h-screen overflow-hidden">
52
+ <!-- AUTH SCREEN (Keep Centered) -->
53
+ <div id="auth-screen" class="fixed inset-0 z-50 bg-gray-50 flex justify-center items-center">
54
+ <div class="bg-white p-8 rounded-2xl shadow-xl w-full max-w-md border border-gray-100">
55
+ <h4 class="text-3xl font-bold text-center mb-2 text-indigo-600">
56
+ 🚀 CaptchaPro
57
+ </h4>
58
+ <p class="text-center text-gray-400 mb-8 text-sm">
59
+ Automated Solver Service
60
+ </p>
61
+
62
+ <div class="flex border-b border-gray-100 mb-6">
63
+ <button onclick="switchAuthTab('login')" id="tab-login"
64
+ class="w-1/2 pb-3 text-center font-bold border-b-2 border-indigo-600 text-indigo-600 transition">
65
+ Đăng nhập
66
+ </button>
67
+ <button onclick="switchAuthTab('register')" id="tab-register"
68
+ class="w-1/2 pb-3 text-center font-bold text-gray-400 border-b-2 border-transparent hover:text-indigo-500 transition">
69
+ Đăng ký
70
+ </button>
71
+ </div>
72
+
73
+ <div id="login-form">
74
+ <input type="text" id="l_user"
75
+ class="w-full p-3 mb-3 bg-gray-50 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 transition"
76
+ placeholder="Username" />
77
+ <input type="password" id="l_pass"
78
+ class="w-full p-3 mb-6 bg-gray-50 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 transition"
79
+ placeholder="Password" />
80
+ <button onclick="login()"
81
+ class="w-full bg-indigo-600 text-white py-3 rounded-lg font-bold hover:bg-indigo-700 transition shadow-lg shadow-indigo-200">
82
+ ĐĂNG NHẬP
83
+ </button>
84
+ </div>
85
+
86
+ <div id="register-form" class="hidden">
87
+ <input type="text" id="r_user"
88
+ class="w-full p-3 mb-3 bg-gray-50 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 transition"
89
+ placeholder="New Username" />
90
+ <input type="password" id="r_pass"
91
+ class="w-full p-3 mb-6 bg-gray-50 border border-gray-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-green-500 transition"
92
+ placeholder="New Password" />
93
+ <button onclick="register()"
94
+ class="w-full bg-green-500 text-white py-3 rounded-lg font-bold hover:bg-green-600 transition shadow-lg shadow-green-200">
95
+ ĐĂNG KÝ NGAY
96
+ </button>
97
+ </div>
98
+ <p id="auth-msg" class="text-red-500 text-center mt-4 text-sm font-medium h-5"></p>
99
+ </div>
100
+ </div>
101
+
102
+ <!-- MAIN DASHBOARD LAYOUT -->
103
+ <div id="dashboard" class="flex h-screen hidden">
104
+ <!-- SIDEBAR -->
105
+ <aside class="w-64 bg-white border-r border-gray-200 flex flex-col shadow-sm z-20">
106
+ <div class="h-16 flex items-center px-6 border-b border-gray-100">
107
+ <span class="text-2xl font-bold text-indigo-600 tracking-tight">🚀 CaptchaPro</span>
108
+ </div>
109
+
110
+ <div class="p-4">
111
+ <div class="flex items-center gap-3 p-3 bg-indigo-50 rounded-xl mb-4">
112
+ <div
113
+ class="w-10 h-10 rounded-full bg-indigo-200 flex items-center justify-center text-indigo-700 font-bold text-lg">
114
+ <span id="avatar-char">U</span>
115
+ </div>
116
+ <div class="overflow-hidden">
117
+ <div id="display-name" class="font-bold text-gray-800 truncate">
118
+ User
119
+ </div>
120
+ <div id="user-role" class="text-xs text-indigo-600 font-medium">
121
+ Member
122
+ </div>
123
+ </div>
124
+ </div>
125
+ </div>
126
+
127
+ <nav class="flex-1 px-4 space-y-1">
128
+ <button onclick="showPage('dashboard')" id="nav-dashboard"
129
+ class="nav-item active w-full flex items-center gap-3 px-3 py-3 text-sm font-medium text-gray-600 rounded-lg hover:bg-gray-50 transition">
130
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
131
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
132
+ d="M4 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2V6zM14 6a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2V6zM4 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2H6a2 2 0 01-2-2v-2zM14 16a2 2 0 012-2h2a2 2 0 012 2v2a2 2 0 01-2 2h-2a2 2 0 01-2-2v-2z">
133
+ </path>
134
+ </svg>
135
+ Dashboard
136
+ </button>
137
+
138
+ <button onclick="showPage('history')" id="nav-history"
139
+ class="nav-item w-full flex items-center gap-3 px-3 py-3 text-sm font-medium text-gray-600 rounded-lg hover:bg-gray-50 transition">
140
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
141
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
142
+ d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z"></path>
143
+ </svg>
144
+ Lịch sử
145
+ </button>
146
+
147
+ <a href="/docs.html" target="_blank"
148
+ class="nav-item w-full flex items-center gap-3 px-3 py-3 text-sm font-medium text-gray-600 rounded-lg hover:bg-gray-50 transition">
149
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
150
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
151
+ d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 4.168 6.253v13C4.168 19.977 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 19.977 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253">
152
+ </path>
153
+ </svg>
154
+ Tài liệu API
155
+ </a>
156
+ </nav>
157
+
158
+ <div id="sidebar-admin" class="px-4 py-2 hidden mt-2">
159
+ <div class="text-xs font-bold text-gray-400 uppercase px-2 mb-2 tracking-wider">
160
+ Admin Control
161
+ </div>
162
+ <button onclick="showPage('admin')" id="nav-admin"
163
+ class="nav-item w-full flex items-center gap-3 px-3 py-3 text-sm font-medium text-red-600 rounded-lg hover:bg-red-50 transition">
164
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
165
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
166
+ d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z">
167
+ </path>
168
+ </svg>
169
+ Quản lý hệ thống
170
+ </button>
171
+ </div>
172
+
173
+ <div class="p-4 border-t border-gray-100">
174
+ <button onclick="logout()"
175
+ class="w-full flex items-center justify-center gap-2 px-4 py-2 text-sm font-bold text-red-500 border border-red-200 rounded-lg hover:bg-red-50 transition">
176
+ <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
177
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
178
+ d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
179
+ </svg>
180
+ Đăng xuất
181
+ </button>
182
+ </div>
183
+ </aside>
184
+
185
+ <!-- MAIN CONTENT -->
186
+ <main class="flex-1 overflow-y-auto bg-gray-50 p-8">
187
+ <!-- PAGE: MAIN DASHBOARD -->
188
+ <div id="page-dashboard" class="animate-fade-in space-y-6">
189
+ <!-- Header Mobile (only text) -->
190
+ <h1 class="text-2xl font-bold text-gray-800 mb-6">Tổng quan</h1>
191
+
192
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
193
+ <!-- Left Column -->
194
+ <div class="md:col-span-1 space-y-6">
195
+ <!-- Credit Card -->
196
+ <div
197
+ class="bg-gradient-to-br from-indigo-600 to-violet-600 text-white rounded-2xl p-6 shadow-lg shadow-indigo-100 relative overflow-hidden">
198
+ <div class="absolute top-0 right-0 -mt-4 -mr-4 w-24 h-24 bg-white opacity-10 rounded-full blur-xl"></div>
199
+ <div class="relative z-10">
200
+ <p class="text-indigo-100 text-sm font-medium mb-1">
201
+ Số dư khả dụng
202
+ </p>
203
+ <h1 class="text-5xl font-bold tracking-tight" id="credits">
204
+ 0
205
+ </h1>
206
+ <div class="mt-4 flex items-center gap-2 text-xs font-mono bg-black/20 p-2 rounded-lg truncate">
207
+ <span class="text-gray-300">KEY:</span>
208
+ <span id="api-key" class="text-white select-all">...</span>
209
+ </div>
210
+ </div>
211
+ </div>
212
+
213
+ <!-- Deposit Options -->
214
+ <div class="bg-white p-6 rounded-2xl shadow-sm border border-gray-100">
215
+ <h5 class="font-bold text-gray-700 mb-4 flex items-center gap-2">
216
+ <span class="bg-green-100 text-green-600 p-1.5 rounded-lg text-xs">💎</span>
217
+ Nạp thêm Credits
218
+ </h5>
219
+ <div class="space-y-3">
220
+ <button onclick="openDepositModal(1)"
221
+ class="w-full flex justify-between items-center p-3 border border-gray-100 rounded-xl hover:border-blue-500 hover:bg-blue-50 transition group">
222
+ <span class="text-gray-600 font-medium group-hover:text-blue-600">Gói $1</span>
223
+ <span
224
+ class="bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded font-bold group-hover:bg-blue-200 group-hover:text-blue-800">1,000
225
+ Cre</span>
226
+ </button>
227
+ <button onclick="openDepositModal(5)"
228
+ class="w-full flex justify-between items-center p-3 border border-gray-100 rounded-xl hover:border-green-500 hover:bg-green-50 transition group">
229
+ <span class="text-gray-600 font-medium group-hover:text-green-600">Gói $5</span>
230
+ <span
231
+ class="bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded font-bold group-hover:bg-green-200 group-hover:text-green-800">5,000
232
+ Cre</span>
233
+ </button>
234
+ <button onclick="openDepositModal(10)"
235
+ class="w-full flex justify-between items-center p-3 border border-gray-100 rounded-xl hover:border-yellow-500 hover:bg-yellow-50 transition group">
236
+ <span class="text-gray-600 font-medium group-hover:text-yellow-600">Gói $10</span>
237
+ <span
238
+ class="bg-gray-100 text-gray-800 text-xs px-2 py-1 rounded font-bold group-hover:bg-yellow-200 group-hover:text-yellow-800">10,000
239
+ Cre</span>
240
+ </button>
241
+ </div>
242
+ </div>
243
+ </div>
244
+
245
+ <!-- Right Column: Test API -->
246
+ <div class="md:col-span-2">
247
+ <div class="bg-white p-6 rounded-2xl shadow-sm border border-gray-100 h-full flex flex-col">
248
+ <h5 class="font-bold text-gray-700 mb-4 flex items-center gap-2">
249
+ <span class="bg-indigo-100 text-indigo-600 p-1.5 rounded-lg text-xs">⚡</span>
250
+ Test Solver API
251
+ </h5>
252
+
253
+ <div class="space-y-4 flex-1">
254
+ <div class="bg-indigo-50/50 rounded-2xl p-6 border border-indigo-100/50">
255
+ <div class="flex items-center gap-4 mb-5">
256
+ <div class="w-12 h-12 bg-white rounded-xl shadow-sm flex items-center justify-center text-2xl">
257
+ 🌐
258
+ </div>
259
+ <div>
260
+ <h6 class="font-bold text-gray-800">
261
+ Cấu hình tự động
262
+ </h6>
263
+ <p class="text-xs text-gray-500">
264
+ Hệ thống đã tối ưu cho môi trường Google Labs
265
+ </p>
266
+ </div>
267
+ </div>
268
+
269
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-4">
270
+ <div class="bg-white/80 p-3 rounded-xl border border-indigo-100/30">
271
+ <p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mb-1">
272
+ Dịch vụ
273
+ </p>
274
+ <p class="text-xs font-bold text-indigo-600 truncate">
275
+ Sẵn sàng (High Trust)
276
+ </p>
277
+ </div>
278
+ <div class="bg-white/80 p-3 rounded-xl border border-indigo-100/30 text-right">
279
+ <p class="text-[10px] font-bold text-gray-400 uppercase tracking-wider mb-1 text-right">
280
+ Trạng thái cổng
281
+ </p>
282
+ <div class="flex items-center justify-end gap-1.5">
283
+ <span
284
+ class="w-1.5 h-1.5 bg-green-500 rounded-full animate-pulse shadow-[0_0_8px_rgba(34,197,94,0.6)]"></span>
285
+ <span class="text-xs font-bold text-gray-700">HTTPS (443)</span>
286
+ </div>
287
+ </div>
288
+ </div>
289
+ </div>
290
+
291
+ <div class="flex items-start gap-2 px-2">
292
+ <span class="text-indigo-400 text-xs">🛡️</span>
293
+ <p class="text-[11px] text-gray-400 leading-relaxed italic">
294
+ Hệ thống đã được thiết lập bảo mật đa lớp. Mọi tham số cấu
295
+ hình đã được mã hóa và cố định trong lõi xử lý.
296
+ </p>
297
+ </div>
298
+ </div>
299
+
300
+ <button onclick="solve()" id="btn-solve"
301
+ class="w-full mt-6 bg-gray-900 text-white py-3 rounded-xl font-bold hover:bg-black transition shadow-lg hover:shadow-xl flex justify-center items-center gap-2">
302
+ <span>🚀</span> Giải Captcha (-1 Credit)
303
+ </button>
304
+
305
+ <div class="mt-6">
306
+ <label class="block text-xs font-bold text-gray-400 mb-1 uppercase">Terminal Output</label>
307
+ <textarea id="result-log"
308
+ class="w-full bg-gray-900 text-green-400 font-mono text-xs p-3 rounded-xl h-32 focus:outline-none resize-none"
309
+ readonly placeholder="Waiting for command..."></textarea>
310
+ </div>
311
+ </div>
312
+ </div>
313
+ </div>
314
+ </div>
315
+
316
+ <!-- PAGE: HISTORY -->
317
+ <div id="page-history" class="hidden animate-fade-in">
318
+ <div class="flex justify-between items-center mb-6">
319
+ <h1 class="text-2xl font-bold text-gray-800">Lịch sử hoạt động</h1>
320
+ </div>
321
+
322
+ <div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden">
323
+ <div class="px-6 py-4 border-b border-gray-100 flex gap-6">
324
+ <button onclick="switchHistoryTab('usage')" id="tab-hist-usage"
325
+ class="pb-2 font-bold text-sm text-indigo-600 border-b-2 border-indigo-600 transition">
326
+ ⚡ Captcha Logs
327
+ </button>
328
+ <button onclick="switchHistoryTab('deposit')" id="tab-hist-deposit"
329
+ class="pb-2 font-bold text-sm text-gray-400 hover:text-gray-600 transition">
330
+ 💰 Transaction Logs
331
+ </button>
332
+ </div>
333
+
334
+ <div class="p-0">
335
+ <div id="hist-usage-content">
336
+ <div class="overflow-auto max-h-[600px] scrollbar-hide">
337
+ <table class="w-full text-sm text-left">
338
+ <thead class="bg-gray-50 text-gray-500 font-medium">
339
+ <tr>
340
+ <th class="p-4 pl-6">Thời gian</th>
341
+ <th class="p-4">Hành động</th>
342
+ <th class="p-4">Chi tiết</th>
343
+ </tr>
344
+ </thead>
345
+ <tbody id="table-usage-body" class="divide-y divide-gray-50"></tbody>
346
+ </table>
347
+ </div>
348
+ </div>
349
+ <div id="hist-deposit-content" class="hidden">
350
+ <div class="overflow-auto max-h-[600px] scrollbar-hide">
351
+ <table class="w-full text-sm text-left">
352
+ <thead class="bg-gray-50 text-gray-500 font-medium">
353
+ <tr>
354
+ <th class="p-4 pl-6">Thời gian</th>
355
+ <th class="p-4">Số tiền</th>
356
+ <th class="p-4">Phương thức</th>
357
+ <th class="p-4">Trạng thái</th>
358
+ </tr>
359
+ </thead>
360
+ <tbody id="table-deposit-body" class="divide-y divide-gray-50"></tbody>
361
+ </table>
362
+ </div>
363
+ </div>
364
+ </div>
365
+ </div>
366
+ </div>
367
+
368
+ <!-- PAGE: ADMIN -->
369
+ <div id="page-admin" class="hidden animate-fade-in">
370
+ <div class="flex justify-between items-center mb-6">
371
+ <h1 class="text-2xl font-bold text-red-600 flex items-center gap-2">
372
+ 🛡️ Admin Dashboard
373
+ </h1>
374
+ <button onclick="loadAdminData()"
375
+ class="text-sm bg-white border border-gray-200 text-gray-600 px-3 py-1.5 rounded-lg hover:bg-gray-50 font-medium shadow-sm">
376
+ Làm mới dữ liệu
377
+ </button>
378
+ </div>
379
+
380
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6">
381
+ <!-- Transactions -->
382
+ <div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden flex flex-col h-[500px]">
383
+ <div class="p-4 border-b border-gray-100 bg-yellow-50/50">
384
+ <h3 class="font-bold text-yellow-700">
385
+ ⌛ Yêu cầu nạp chờ duyệt
386
+ </h3>
387
+ </div>
388
+ <div class="overflow-auto flex-1 scrollbar-hide">
389
+ <table class="w-full text-sm text-left">
390
+ <thead class="bg-gray-50 text-gray-500 text-xs uppercase">
391
+ <tr>
392
+ <th class="p-3 pl-4">User</th>
393
+ <th class="p-3">Amount</th>
394
+ <th class="p-3">Method</th>
395
+ <th class="p-3 text-right pr-4">Action</th>
396
+ </tr>
397
+ </thead>
398
+ <tbody id="admin-trans-list" class="divide-y divide-gray-50"></tbody>
399
+ </table>
400
+ </div>
401
+ </div>
402
+
403
+ <!-- Users -->
404
+ <div class="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden flex flex-col h-[500px]">
405
+ <div class="p-4 border-b border-gray-100 bg-indigo-50/50">
406
+ <h3 class="font-bold text-indigo-700">
407
+ 👥 Danh sách người dùng
408
+ </h3>
409
+ </div>
410
+ <div class="overflow-auto flex-1 scrollbar-hide">
411
+ <table class="w-full text-sm text-left">
412
+ <thead class="bg-gray-50 text-gray-500 text-xs uppercase">
413
+ <tr>
414
+ <th class="p-3 pl-4">User</th>
415
+ <th class="p-3">Credits</th>
416
+ <th class="p-3">Role</th>
417
+ <th class="p-3 text-right pr-4">Hành động</th>
418
+ </tr>
419
+ </thead>
420
+ <tbody id="admin-user-list" class="divide-y divide-gray-50"></tbody>
421
+ </table>
422
+ </div>
423
+ </div>
424
+ </div>
425
+ </div>
426
+ </main>
427
+ </div>
428
+
429
+ <!-- MODALS (Deposit & Success) - SAME LOGIC, UPDATED STYLES -->
430
+ <div id="depositModal"
431
+ class="fixed inset-0 bg-gray-900/50 z-50 hidden flex items-center justify-center backdrop-blur-sm transition-all">
432
+ <div class="bg-white w-full max-w-md rounded-2xl shadow-2xl transform scale-100 modal-enter mx-4 overflow-hidden">
433
+ <div class="bg-gray-50 px-6 py-4 border-b border-gray-100 flex justify-between items-center">
434
+ <h5 class="font-bold text-lg text-gray-800">
435
+ Thanh toán
436
+ <span id="modal-amount-display" class="text-indigo-600">$0</span>
437
+ </h5>
438
+ <button onclick="closeModal('depositModal')"
439
+ class="text-gray-400 hover:text-gray-600 bg-gray-200 rounded-full w-8 h-8 flex items-center justify-center font-bold transition">
440
+ &times;
441
+ </button>
442
+ </div>
443
+ <div class="p-6">
444
+ <div class="flex p-1 bg-gray-100 rounded-xl mb-6">
445
+ <button onclick="switchPaymentMethod('mb')" id="btn-tab-mb"
446
+ class="flex-1 py-2 rounded-lg text-sm font-bold bg-white text-indigo-600 shadow-sm transition">
447
+ MBBank
448
+ </button>
449
+ <button onclick="switchPaymentMethod('binance')" id="btn-tab-binance"
450
+ class="flex-1 py-2 rounded-lg text-sm font-bold text-gray-500 hover:text-gray-700 transition">
451
+ Binance
452
+ </button>
453
+ </div>
454
+
455
+ <!-- Content logic remains similar -->
456
+ <div id="content-mb" class="text-center">
457
+ <img id="mb-qr-img" src="" class="w-48 h-auto mx-auto border-4 border-white shadow-lg rounded-xl mb-4" />
458
+ <div class="bg-indigo-50 p-4 rounded-xl text-sm space-y-2 border border-indigo-100 text-left">
459
+ <div class="flex justify-between">
460
+ <span class="text-gray-500">Bank</span>
461
+ <span class="font-bold text-gray-800">MBBank</span>
462
+ </div>
463
+ <div class="flex justify-between">
464
+ <span class="text-gray-500">Account</span>
465
+ <span class="font-bold text-indigo-600 select-all">0972121082000</span>
466
+ </div>
467
+ <div class="flex justify-between">
468
+ <span class="text-gray-500">Name</span>
469
+ <span class="font-bold text-gray-800">NGUYEN NGOC NEN</span>
470
+ </div>
471
+ <div class="pt-2 border-t border-indigo-200 flex justify-between">
472
+ <span class="text-gray-500">Memo</span>
473
+ <span id="mb-memo" class="font-bold text-red-500 select-all">...</span>
474
+ </div>
475
+ </div>
476
+ </div>
477
+ <div id="content-binance" class="hidden text-center py-6">
478
+ <img id="binance-qr-img"
479
+ src="https://img.upanh.moe/svG8VPr7/z7378722800950-37af8ee1f0cd5dafae8366cb441490ed-jpg.jpg"
480
+ class="w-48 h-auto mx-auto border-4 border-white shadow-lg rounded-xl mb-4" />
481
+ <p class="text-sm text-gray-500 mb-2">Binance Pay / Wallet</p>
482
+ <div class="bg-yellow-50 text-yellow-800 p-3 rounded-xl text-sm font-medium border border-yellow-100">
483
+ Memo: <span id="bn-memo" class="font-bold">...</span>
484
+ </div>
485
+ </div>
486
+ </div>
487
+ <div class="p-4 bg-gray-50 border-t border-gray-100 flex justify-end gap-3">
488
+ <button onclick="closeModal('depositModal')"
489
+ class="px-4 py-2 text-gray-500 font-bold hover:bg-gray-200 rounded-lg transition">
490
+ Hủy
491
+ </button>
492
+ <button onclick="confirmDeposit()" id="btn-confirm-deposit"
493
+ class="px-6 py-2 bg-indigo-600 text-white font-bold rounded-lg hover:bg-indigo-700 shadow-lg shadow-indigo-200 transition">
494
+ ✅ Đã chuyển tiền
495
+ </button>
496
+ </div>
497
+ </div>
498
+ </div>
499
+
500
+ <!-- Success Modal -->
501
+ <div id="successModal"
502
+ class="fixed inset-0 bg-gray-900/60 z-60 hidden flex items-center justify-center backdrop-blur-sm">
503
+ <div class="bg-white rounded-3xl shadow-2xl p-8 w-full max-w-sm text-center modal-enter mx-4">
504
+ <div class="w-16 h-16 bg-green-100 text-green-600 rounded-full flex items-center justify-center mx-auto mb-4">
505
+ <svg class="w-8 h-8" fill="none" stroke="currentColor" viewBox="0 0 24 24">
506
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path>
507
+ </svg>
508
+ </div>
509
+ <h2 class="text-2xl font-bold text-gray-800 mb-2">Thành công!</h2>
510
+ <p id="success-msg" class="text-gray-500 mb-6">Yêu cầu đã được gửi.</p>
511
+ <button onclick="closeModal('successModal')"
512
+ class="w-full py-3 bg-green-600 text-white rounded-xl font-bold hover:bg-green-700 transition">
513
+ OK
514
+ </button>
515
+ </div>
516
+ </div>
517
+
518
+ <script>
519
+ const API = "/api";
520
+ const socket = io();
521
+ let userKey = localStorage.getItem("apiKey");
522
+ let currentAmount = 0;
523
+ let currentMethod = "MBBank";
524
+
525
+ // --- HWID / FINGERPRINT ---
526
+ function getHWID() {
527
+ let id = localStorage.getItem("hwid");
528
+ if (!id) {
529
+ id =
530
+ "hw-" + Math.random().toString(36).substr(2, 9) + "-" + Date.now();
531
+ localStorage.setItem("hwid", id);
532
+ }
533
+ return id;
534
+ }
535
+
536
+ // --- AUTH & INIT ---
537
+ if (userKey) {
538
+ refreshMe();
539
+ } else {
540
+ document.getElementById("auth-screen").classList.remove("hidden");
541
+ }
542
+
543
+ function switchAuthTab(tab) {
544
+ const login = document.getElementById("login-form");
545
+ const reg = document.getElementById("register-form");
546
+ const tLog = document.getElementById("tab-login");
547
+ const tReg = document.getElementById("tab-register");
548
+
549
+ if (tab === "login") {
550
+ login.classList.remove("hidden");
551
+ reg.classList.add("hidden");
552
+ tLog.className =
553
+ "w-1/2 pb-3 text-center font-bold border-b-2 border-indigo-600 text-indigo-600 transition";
554
+ tReg.className =
555
+ "w-1/2 pb-3 text-center font-bold text-gray-400 border-b-2 border-transparent hover:text-indigo-500 transition";
556
+ } else {
557
+ login.classList.add("hidden");
558
+ reg.classList.remove("hidden");
559
+ tReg.className =
560
+ "w-1/2 pb-3 text-center font-bold border-b-2 border-green-500 text-green-500 transition";
561
+ tLog.className =
562
+ "w-1/2 pb-3 text-center font-bold text-gray-400 border-b-2 border-transparent hover:text-indigo-500 transition";
563
+ }
564
+ }
565
+
566
+ async function login() {
567
+ const u = document.getElementById("l_user").value;
568
+ const p = document.getElementById("l_pass").value;
569
+ try {
570
+ const res = await fetch(`${API}/login`, {
571
+ method: "POST",
572
+ headers: { "Content-Type": "application/json" },
573
+ body: JSON.stringify({ username: u, password: p }),
574
+ });
575
+ const data = await res.json();
576
+ if (data.success) {
577
+ localStorage.setItem("apiKey", data.user.api_key);
578
+ userKey = data.user.api_key;
579
+ loadUser(data.user);
580
+ } else document.getElementById("auth-msg").innerText = data.error;
581
+ } catch (e) {
582
+ console.error(e);
583
+ }
584
+ }
585
+
586
+ async function register() {
587
+ const u = document.getElementById("r_user").value;
588
+ const p = document.getElementById("r_pass").value;
589
+ const hwid = getHWID();
590
+ const res = await fetch(`${API}/register`, {
591
+ method: "POST",
592
+ headers: { "Content-Type": "application/json" },
593
+ body: JSON.stringify({ username: u, password: p, hwid: hwid }),
594
+ });
595
+ const data = await res.json();
596
+ if (data.success) {
597
+ alert(data.message);
598
+ switchAuthTab("login");
599
+ } else alert(data.error);
600
+ }
601
+
602
+ function loadUser(user) {
603
+ document.getElementById("auth-screen").classList.add("hidden");
604
+ document.getElementById("dashboard").classList.remove("hidden");
605
+
606
+ document.getElementById("display-name").innerText = user.username;
607
+ document.getElementById("avatar-char").innerText = user.username
608
+ .charAt(0)
609
+ .toUpperCase(); // New Avatar logic
610
+ document.getElementById("credits").innerText = user.credits;
611
+ document.getElementById("api-key").innerText = user.api_key;
612
+ document.getElementById("user-role").innerText =
613
+ user.role === "admin" ? "Administrator" : "Member";
614
+
615
+ if (user.role === "admin") {
616
+ document.getElementById("sidebar-admin").classList.remove("hidden");
617
+ } else {
618
+ document.getElementById("sidebar-admin").classList.add("hidden");
619
+ }
620
+
621
+ // Default View
622
+ showPage("dashboard");
623
+ }
624
+
625
+ function logout() {
626
+ localStorage.removeItem("apiKey");
627
+ location.reload();
628
+ }
629
+
630
+ // --- NAVIGATION ---
631
+ function showPage(pageId) {
632
+ // Hide all
633
+ document.getElementById("page-dashboard").classList.add("hidden");
634
+ document.getElementById("page-history").classList.add("hidden");
635
+ document.getElementById("page-admin").classList.add("hidden");
636
+
637
+ // Remove active styles
638
+ document
639
+ .querySelectorAll(".nav-item")
640
+ .forEach((el) =>
641
+ el.classList.remove(
642
+ "active",
643
+ "bg-indigo-50",
644
+ "text-indigo-600",
645
+ "border-r-4",
646
+ "border-indigo-600"
647
+ )
648
+ );
649
+
650
+ // Show Target
651
+ const target = document.getElementById(`page-${pageId}`);
652
+ if (target) target.classList.remove("hidden");
653
+
654
+ // Add active style to nav
655
+ const nav = document.getElementById(`nav-${pageId}`);
656
+ if (nav) {
657
+ nav.classList.add("active", "bg-indigo-50", "text-indigo-600");
658
+ }
659
+
660
+ if (pageId === "history") loadHistoryData();
661
+ if (pageId === "admin") loadAdminData();
662
+ }
663
+
664
+ // --- DASHBOARD ACTIONS ---
665
+ async function solve() {
666
+ const btn = document.getElementById("btn-solve");
667
+ const log = document.getElementById("result-log");
668
+
669
+ btn.disabled = true;
670
+ btn.innerHTML = `<svg class="animate-spin h-5 w-5 text-white" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg> Processing...`;
671
+
672
+ try {
673
+ const res = await fetch(`${API}/solve`, {
674
+ method: "POST",
675
+ headers: {
676
+ "Content-Type": "application/json",
677
+ "x-api-key": userKey,
678
+ },
679
+ body: JSON.stringify({ action: "default" }),
680
+ });
681
+ const data = await res.json();
682
+ if (data.success) {
683
+ log.value = `[SUCCESS] Token received:\n${data.token}`;
684
+ refreshMe();
685
+ } else {
686
+ log.value = `[FAILED] Error: ${data.error}`;
687
+ }
688
+ } catch (e) {
689
+ log.value = `[ERROR] Connection failed: ${e}`;
690
+ }
691
+
692
+ btn.disabled = false;
693
+ btn.innerHTML = `<span>🚀</span> Giải Captcha (-1 Credit)`;
694
+ }
695
+
696
+ // --- DEPOSIT ---
697
+ function openDepositModal(amount) {
698
+ currentAmount = amount;
699
+ const user = document.getElementById("display-name").innerText;
700
+ const memo = `NAP ${user}`.toUpperCase();
701
+ const vnd = amount * 25000;
702
+
703
+ document.getElementById(
704
+ "modal-amount-display"
705
+ ).innerText = `$${amount}`;
706
+ document.getElementById(
707
+ "mb-qr-img"
708
+ ).src = `https://img.vietqr.io/image/MB-0972121082000-compact2.jpg?amount=${vnd}&addInfo=${encodeURIComponent(
709
+ memo
710
+ )}`;
711
+ document.getElementById("mb-memo").innerText = memo;
712
+ document.getElementById("bn-memo").innerText = memo;
713
+
714
+ switchPaymentMethod("mb");
715
+ openModal("depositModal");
716
+ }
717
+
718
+ function switchPaymentMethod(method) {
719
+ currentMethod = method === "mb" ? "MBBank" : "Binance";
720
+ if (method === "mb") {
721
+ document.getElementById("content-mb").classList.remove("hidden");
722
+ document.getElementById("content-binance").classList.add("hidden");
723
+ document.getElementById("btn-tab-mb").className =
724
+ "flex-1 py-2 rounded-lg text-sm font-bold bg-white text-indigo-600 shadow-sm transition";
725
+ document.getElementById("btn-tab-binance").className =
726
+ "flex-1 py-2 rounded-lg text-sm font-bold text-gray-500 hover:text-gray-700 transition";
727
+ } else {
728
+ document.getElementById("content-mb").classList.add("hidden");
729
+ document.getElementById("content-binance").classList.remove("hidden");
730
+ document.getElementById("btn-tab-binance").className =
731
+ "flex-1 py-2 rounded-lg text-sm font-bold bg-white text-yellow-600 shadow-sm transition";
732
+ document.getElementById("btn-tab-mb").className =
733
+ "flex-1 py-2 rounded-lg text-sm font-bold text-gray-500 hover:text-gray-700 transition";
734
+ }
735
+ }
736
+
737
+ async function confirmDeposit() {
738
+ const btn = document.getElementById("btn-confirm-deposit");
739
+ const old = btn.innerText;
740
+ btn.disabled = true;
741
+ btn.innerText = "...";
742
+ try {
743
+ const res = await fetch(`${API}/deposit`, {
744
+ method: "POST",
745
+ headers: {
746
+ "Content-Type": "application/json",
747
+ "x-api-key": userKey,
748
+ },
749
+ body: JSON.stringify({
750
+ amount: currentAmount,
751
+ method: currentMethod,
752
+ }),
753
+ });
754
+ const data = await res.json();
755
+ if (data.success) {
756
+ closeModal("depositModal");
757
+ openModal("successModal");
758
+ } else alert(data.error);
759
+ } catch (e) {
760
+ alert(e.message);
761
+ }
762
+ btn.disabled = false;
763
+ btn.innerText = old;
764
+ }
765
+
766
+ // --- HISTORY UI ---
767
+ async function loadHistoryData() {
768
+ const resU = await fetch(`${API}/history/usage`, {
769
+ headers: { "x-api-key": userKey },
770
+ });
771
+ const dataU = await resU.json();
772
+ document.getElementById("table-usage-body").innerHTML =
773
+ dataU
774
+ .map(
775
+ (u) => `
776
+ <tr class="bg-white hover:bg-gray-50 transition border-b border-gray-50/50">
777
+ <td class="p-4 pl-6 text-xs text-gray-400 font-mono">${new Date(
778
+ u.timestamp
779
+ ).toLocaleString()}</td>
780
+ <td class="p-4 font-bold text-gray-700 text-sm">${u.action}</td>
781
+ <td class="p-4 text-xs text-gray-400 font-mono truncate max-w-xs" title="${u.details
782
+ }">${u.details.includes("labs.google") ? "[HIDDEN]" : u.details
783
+ }</td>
784
+ </tr>
785
+ `
786
+ )
787
+ .join("") ||
788
+ '<tr><td colspan="3" class="p-4 text-center text-gray-400 text-sm">Chưa có dữ liệu</td></tr>';
789
+
790
+ const resT = await fetch(`${API}/history/transactions`, {
791
+ headers: { "x-api-key": userKey },
792
+ });
793
+ const dataT = await resT.json();
794
+ document.getElementById("table-deposit-body").innerHTML =
795
+ dataT
796
+ .map((t) => {
797
+ let color =
798
+ t.status === "approved"
799
+ ? "text-green-600 bg-green-50"
800
+ : t.status === "rejected"
801
+ ? "text-red-600 bg-red-50"
802
+ : "text-yellow-600 bg-yellow-50";
803
+ return `
804
+ <tr class="bg-white hover:bg-gray-50 transition border-b border-gray-50/50">
805
+ <td class="p-4 pl-6 text-xs text-gray-400 font-mono">${new Date(
806
+ t.created_at
807
+ ).toLocaleString()}</td>
808
+ <td class="p-4 font-bold text-gray-800">$${t.amount}</td>
809
+ <td class="p-4 text-sm text-gray-500">${t.method}</td>
810
+ <td class="p-4"><span class="px-2.5 py-1 rounded-lg text-xs font-bold uppercase ${color}">${t.status
811
+ }</span></td>
812
+ </tr>
813
+ `;
814
+ })
815
+ .join("") ||
816
+ '<tr><td colspan="4" class="p-4 text-center text-gray-400 text-sm">Chưa có dữ liệu</td></tr>';
817
+ }
818
+
819
+ function switchHistoryTab(tab) {
820
+ if (tab === "usage") {
821
+ document
822
+ .getElementById("hist-usage-content")
823
+ .classList.remove("hidden");
824
+ document
825
+ .getElementById("hist-deposit-content")
826
+ .classList.add("hidden");
827
+ document.getElementById("tab-hist-usage").className =
828
+ "pb-2 font-bold text-sm text-indigo-600 border-b-2 border-indigo-600 transition";
829
+ document.getElementById("tab-hist-deposit").className =
830
+ "pb-2 font-bold text-sm text-gray-400 hover:text-gray-600 transition";
831
+ } else {
832
+ document.getElementById("hist-usage-content").classList.add("hidden");
833
+ document
834
+ .getElementById("hist-deposit-content")
835
+ .classList.remove("hidden");
836
+ document.getElementById("tab-hist-deposit").className =
837
+ "pb-2 font-bold text-sm text-indigo-600 border-b-2 border-indigo-600 transition";
838
+ document.getElementById("tab-hist-usage").className =
839
+ "pb-2 font-bold text-sm text-gray-400 hover:text-gray-600 transition";
840
+ }
841
+ }
842
+
843
+ // --- ADMIN ---
844
+ async function loadAdminData() {
845
+ const resT = await fetch(`${API}/admin/transactions`, {
846
+ headers: { "x-api-key": userKey },
847
+ });
848
+ const dataT = await resT.json();
849
+ document.getElementById("admin-trans-list").innerHTML =
850
+ dataT
851
+ .filter((t) => t.status === "pending")
852
+ .map(
853
+ (t) => `
854
+ <tr class="bg-white hover:bg-yellow-50/30 transition border-b border-gray-50">
855
+ <td class="p-3 pl-4 font-bold text-gray-700">${t.username}</td>
856
+ <td class="p-3 font-mono text-indigo-600">$${t.amount}</td>
857
+ <td class="p-3 text-xs text-gray-400">${t.method}</td>
858
+ <td class="p-3 pr-4 text-right flex justify-end gap-1">
859
+ <button onclick="processTrans(${t.id}, 'approve')" class="p-1 px-2 rounded bg-green-100 text-green-600 hover:bg-green-200 font-bold text-xs">✔</button>
860
+ <button onclick="processTrans(${t.id}, 'reject')" class="p-1 px-2 rounded bg-red-100 text-red-600 hover:bg-red-200 font-bold text-xs">✘</button>
861
+ </td>
862
+ </tr>
863
+ `
864
+ )
865
+ .join("") ||
866
+ '<tr><td colspan="4" class="p-6 text-center text-gray-300 text-sm italic">Không có yêu cầu nào</td></tr>';
867
+
868
+ const resU = await fetch(`${API}/admin/users`, {
869
+ headers: { "x-api-key": userKey },
870
+ });
871
+ const dataU = await resU.json();
872
+ document.getElementById("admin-user-list").innerHTML = dataU
873
+ .map(
874
+ (u) => `
875
+ <tr class="bg-white hover:bg-indigo-50/30 transition border-b border-gray-50">
876
+ <td class="p-3 pl-4 font-medium text-gray-700">${u.username
877
+ }</td>
878
+ <td class="p-3 font-bold text-gray-800">${u.credits}</td>
879
+ <td class="p-3"><span class="px-2 py-0.5 rounded text-[10px] font-bold uppercase ${u.role === "admin"
880
+ ? "bg-red-100 text-red-600"
881
+ : "bg-gray-100 text-gray-500"
882
+ }">${u.role}</span></td>
883
+ <td class="p-3 pr-4 text-right">
884
+ <button onclick="deleteUser(${u.id}, '${u.username
885
+ }')" class="text-red-400 hover:text-red-600 transition p-1">
886
+ <svg class="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
887
+ <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"></path>
888
+ </svg>
889
+ </button>
890
+ </td>
891
+ </tr>
892
+ `
893
+ )
894
+ .join("");
895
+ }
896
+
897
+ async function processTrans(id, act) {
898
+ if (!confirm(`Xác nhận ${act}?`)) return;
899
+ await fetch(`${API}/admin/approve`, {
900
+ method: "POST",
901
+ headers: { "Content-Type": "application/json", "x-api-key": userKey },
902
+ body: JSON.stringify({ transId: id, action: act }),
903
+ });
904
+ loadAdminData();
905
+ refreshMe();
906
+ }
907
+
908
+ async function deleteUser(id, name) {
909
+ if (
910
+ !confirm(
911
+ `Bạn có chắc chắn muốn xóa user "${name}"? Thao tác này không thể hoàn tác.`
912
+ )
913
+ )
914
+ return;
915
+ try {
916
+ const res = await fetch(`${API}/admin/delete-user`, {
917
+ method: "POST",
918
+ headers: {
919
+ "Content-Type": "application/json",
920
+ "x-api-key": userKey,
921
+ },
922
+ body: JSON.stringify({ userId: id }),
923
+ });
924
+ const data = await res.json();
925
+ if (data.success) {
926
+ loadAdminData();
927
+ } else alert(data.error);
928
+ } catch (e) {
929
+ alert(e.message);
930
+ }
931
+ }
932
+
933
+ async function refreshMe() {
934
+ const res = await fetch(`${API}/me`, {
935
+ headers: { "x-api-key": userKey },
936
+ });
937
+ const data = await res.json();
938
+ if (data.username) loadUser(data);
939
+ else {
940
+ localStorage.removeItem("apiKey");
941
+ location.reload();
942
+ }
943
+ }
944
+
945
+ // --- HELPERS ---
946
+ function openModal(id) {
947
+ document.getElementById(id).classList.remove("hidden");
948
+ }
949
+ function closeModal(id) {
950
+ document.getElementById(id).classList.add("hidden");
951
+ }
952
+
953
+ // Socket
954
+ socket.on("user:credit-update", () => refreshMe());
955
+ socket.on("transaction-updated", () => loadAdminData());
956
+ socket.on("admin:new-transaction", () => loadAdminData());
957
+ </script>
958
+ </body>
959
+
960
+ </html>