Upload 11 files
Browse files- .gitattributes +1 -0
- cert.pem +28 -0
- check_proxy_alive.js +73 -0
- database.sqlite +3 -0
- key.pem +28 -0
- logger.js +51 -0
- package-lock.json +0 -0
- package.json +18 -0
- server.js +399 -0
- solver.js +197 -0
- stress_test_queue.js +35 -0
- test_proxy.js +66 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
database.sqlite filter=lfs diff=lfs merge=lfs -text
|
cert.pem
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-----BEGIN CERTIFICATE-----
|
| 2 |
+
MIIEsjCCA5qgAwIBAgIUWjp7uG3wH7CzK8BZ2dGz3qMF4l8wDQYJKoZIhvcNAQEL
|
| 3 |
+
BQAwgYsxCzAJBgNVBAYTAlVTMRkwFwYDVQQKExBDbG91ZEZsYXJlLCBJbmMuMTQw
|
| 4 |
+
MgYDVQQLEytDbG91ZEZsYXJlIE9yaWdpbiBTU0wgQ2VydGlmaWNhdGUgQXV0aG9y
|
| 5 |
+
aXR5MRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRMwEQYDVQQIEwpDYWxpZm9ybmlh
|
| 6 |
+
MB4XDTI1MTIyOTA1NTMwMFoXDTQwMTIyNTA1NTMwMFowYjEZMBcGA1UEChMQQ2xv
|
| 7 |
+
dWRGbGFyZSwgSW5jLjEdMBsGA1UECxMUQ2xvdWRGbGFyZSBPcmlnaW4gQ0ExJjAk
|
| 8 |
+
BgNVBAMTHUNsb3VkRmxhcmUgT3JpZ2luIENlcnRpZmljYXRlMIIBIjANBgkqhkiG
|
| 9 |
+
9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqpUOrPW40xlB4F6MxiQmKgIHeSqJQBAzXkMq
|
| 10 |
+
Ec3Juhb1zT6FP5l2gKSUcX2WWriQBm82MRPca70ni22vEjNKfD76MC8yYun8E5mR
|
| 11 |
+
2hCOBfhn6ZBv5OCLDljfHg6p1umAhamGty3ACriI/j3WOMw0rhUJwqsIuQ5i3DK4
|
| 12 |
+
dbuqpNnqkBGcxy9Tzd+PYCbtl5z1QAKlVc1fvGJ4MJ/1m8yflyoYEcZ8aVdrBDwx
|
| 13 |
+
KV3Kpl6EpRS+NN0peFrPViOcaWunQPRIiYS/UyjLihoXVFrFFiDJL3DtwxPe7C+t
|
| 14 |
+
FHVW2I5UFNm1qRX4bSNZ6f9KEnNwtXZ+rriYXIkd23ENizpniQIDAQABo4IBNDCC
|
| 15 |
+
ATAwDgYDVR0PAQH/BAQDAgWgMB0GA1UdJQQWMBQGCCsGAQUFBwMCBggrBgEFBQcD
|
| 16 |
+
ATAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBT8P5IcgCbSZeu7AHjm/6ry0kw3KjAf
|
| 17 |
+
BgNVHSMEGDAWgBQk6FNXXXw0QIep65TbuuEWePwppDBABggrBgEFBQcBAQQ0MDIw
|
| 18 |
+
MAYIKwYBBQUHMAGGJGh0dHA6Ly9vY3NwLmNsb3VkZmxhcmUuY29tL29yaWdpbl9j
|
| 19 |
+
YTA1BgNVHREELjAsghUqLmtpbnhzb2Z0d2FyZS5vbmxpbmWCE2tpbnhzb2Z0d2Fy
|
| 20 |
+
ZS5vbmxpbmUwOAYDVR0fBDEwLzAtoCugKYYnaHR0cDovL2NybC5jbG91ZGZsYXJl
|
| 21 |
+
LmNvbS9vcmlnaW5fY2EuY3JsMA0GCSqGSIb3DQEBCwUAA4IBAQBQMhFjPAOP398v
|
| 22 |
+
owmv7zOxYerdHwkZ9wp5TMDUV2dO8DuExmPzzuPmz34bIjVzFUS5B3vwelkzU4z4
|
| 23 |
+
MhpxWqeXB5iQWMYo3DzW8UQgGq8qKtZl/LHIuyAkNCWXdlepdsdo4625ehcddTE9
|
| 24 |
+
lG4qRuxV+9KavzFZhu14kqqZkQ+e7jqELF74dqx2tBGayQgP//0Z0AXgaLihvzAx
|
| 25 |
+
yc4suJp5tN3UCfyiFBhWyoD0cPhNNlyfM2O5g3mgo0nEvPSIyNQFfZF67Pcj/Gag
|
| 26 |
+
VylmV9p3XvAa8Nn+oHrnxck5YUhkdltExoSi9se0k8uP07Nzc6F/n51Mq9LO9HFS
|
| 27 |
+
YzcQo2vT
|
| 28 |
+
-----END CERTIFICATE-----
|
check_proxy_alive.js
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const http = require('http');
|
| 2 |
+
const https = require('https');
|
| 3 |
+
|
| 4 |
+
async function testProxy(protocol, targetHost, targetPath) {
|
| 5 |
+
const proxy = 'http://zlet9i3y:oZJQ0o4V@157.10.49.215:44128';
|
| 6 |
+
const targetUrl = `${protocol}://${targetHost}${targetPath}`;
|
| 7 |
+
|
| 8 |
+
console.log(`Testing Proxy: ${proxy}`);
|
| 9 |
+
console.log(`Target: ${targetUrl}`);
|
| 10 |
+
|
| 11 |
+
const url = new URL(proxy);
|
| 12 |
+
const auth = 'Basic ' + Buffer.from(url.username + ':' + url.password).toString('base64');
|
| 13 |
+
|
| 14 |
+
if (protocol === 'http') {
|
| 15 |
+
return new Promise((resolve) => {
|
| 16 |
+
const options = {
|
| 17 |
+
host: url.hostname,
|
| 18 |
+
port: url.port,
|
| 19 |
+
path: targetUrl,
|
| 20 |
+
headers: {
|
| 21 |
+
'Proxy-Authorization': auth,
|
| 22 |
+
'Host': targetHost
|
| 23 |
+
}
|
| 24 |
+
};
|
| 25 |
+
http.get(options, (res) => {
|
| 26 |
+
let data = '';
|
| 27 |
+
res.on('data', chunk => data += chunk);
|
| 28 |
+
res.on('end', () => {
|
| 29 |
+
console.log(`HTTP Status: ${res.statusCode}`);
|
| 30 |
+
resolve(true);
|
| 31 |
+
});
|
| 32 |
+
}).on('error', e => {
|
| 33 |
+
console.error(`HTTP Proxy Error: ${e.message}`);
|
| 34 |
+
resolve(false);
|
| 35 |
+
});
|
| 36 |
+
});
|
| 37 |
+
} else {
|
| 38 |
+
// HTTPS CONNECT
|
| 39 |
+
return new Promise((resolve) => {
|
| 40 |
+
const req = http.request({
|
| 41 |
+
host: url.hostname,
|
| 42 |
+
port: url.port,
|
| 43 |
+
method: 'CONNECT',
|
| 44 |
+
path: `${targetHost}:443`,
|
| 45 |
+
headers: {
|
| 46 |
+
'Proxy-Authorization': auth
|
| 47 |
+
}
|
| 48 |
+
});
|
| 49 |
+
|
| 50 |
+
req.on('connect', (res, socket, head) => {
|
| 51 |
+
console.log('HTTPS CONNECT established');
|
| 52 |
+
socket.destroy();
|
| 53 |
+
resolve(true);
|
| 54 |
+
});
|
| 55 |
+
|
| 56 |
+
req.on('error', e => {
|
| 57 |
+
console.error(`HTTPS Proxy Error (CONNECT): ${e.message}`);
|
| 58 |
+
resolve(false);
|
| 59 |
+
});
|
| 60 |
+
|
| 61 |
+
req.end();
|
| 62 |
+
});
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
async function run() {
|
| 67 |
+
console.log('--- Step 1: HTTP Test ---');
|
| 68 |
+
await testProxy('http', 'httpbin.org', '/ip');
|
| 69 |
+
console.log('\n--- Step 2: HTTPS Test ---');
|
| 70 |
+
await testProxy('https', 'labs.google', '/');
|
| 71 |
+
}
|
| 72 |
+
|
| 73 |
+
run();
|
database.sqlite
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:0b0e4ce04c7967f0e0b3b7e1d0daaefab740e8c7d7af0aa9275d9d66f8465cf5
|
| 3 |
+
size 184320
|
key.pem
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
-----BEGIN PRIVATE KEY-----
|
| 2 |
+
MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCqlQ6s9bjTGUHg
|
| 3 |
+
XozGJCYqAgd5KolAEDNeQyoRzcm6FvXNPoU/mXaApJRxfZZauJAGbzYxE9xrvSeL
|
| 4 |
+
ba8SM0p8PvowLzJi6fwTmZHaEI4F+GfpkG/k4IsOWN8eDqnW6YCFqYa3LcAKuIj+
|
| 5 |
+
PdY4zDSuFQnCqwi5DmLcMrh1u6qk2eqQEZzHL1PN349gJu2XnPVAAqVVzV+8Yngw
|
| 6 |
+
n/WbzJ+XKhgRxnxpV2sEPDEpXcqmXoSlFL403Sl4Ws9WI5xpa6dA9EiJhL9TKMuK
|
| 7 |
+
GhdUWsUWIMkvcO3DE97sL60UdVbYjlQU2bWpFfhtI1np/0oSc3C1dn6uuJhciR3b
|
| 8 |
+
cQ2LOmeJAgMBAAECggEAFKR4k+wha2RPUUJH2VN3DdbUhjBkTw0W1kWc8e4cBwmv
|
| 9 |
+
waBa+h0NVthzwT8gIobaoPi0z2AWqLAJeMkzaimsThhF6O5RM+CaWVnYaVHfCfBr
|
| 10 |
+
TLummsUkdr5A88909JWpsKMY8xJVfsahh1i+0308s8NGfNj7M3++7WQy58Znyk6E
|
| 11 |
+
zN+yRa7vSUZIvCoElzfZwGgcmTGZL3GomRCCOFqHP/+/iahm1N+8uLRa680mjnQ2
|
| 12 |
+
8twYjF2D5agOwmpKG9YOmt2rtUlTq+k0bEt+s1XOu5Lln7DC8z6d5LxMJqH6IYo2
|
| 13 |
+
HDnNo25EFK7Pg5Gi1iLrRJdW+mcePp/5JpJObyYl4QKBgQDm3iBKeYkToFvPTbBV
|
| 14 |
+
YiSc8t/8hnXTLV8HZtflr51nXVTALEUAg7NnWhWOzK60Ust1aCvqco3vgXzNXtEx
|
| 15 |
+
MS9Pp/Nb7l51W9uazocQmOZlUZut3wyMl6152Zk5rtjBmZ8RD1gP8/UVOwMkicDe
|
| 16 |
+
KgiCIse+PfyP990IC2Nu14oeGQKBgQC9JuIOgyl5v9v0F5JGJZ9vsADrT9LopHO3
|
| 17 |
+
Ecb838XnmV9YQS5pyZZwD5IEBsUbOsRchUEZJRcsPXKXse+N5Eu5qC6jOF2HR/rX
|
| 18 |
+
xUj2GQIWLKYvzPIpkWq7YFvZ5PdJp6uXWoA9TN544pvlmewcr3XixDZAGw/TTUls
|
| 19 |
+
HkzGQPLi8QKBgQCdZ7cmb+S43+2Y0sQ2AtspsyJqHxb4a4fOGR18OZcdIXImMEi/
|
| 20 |
+
iAvqnG30VbOLuKOFogfHKmGBeYyozUvduUYjjkQLWuNGKuH4k3VdKJJ1ZAW7r0b0
|
| 21 |
+
GHoriOPCg2a/4MimLgsBnXhtCDYlVFKOZqQ0bhg3qjWnxvwJfxJFQWmnCQKBgQCq
|
| 22 |
+
pI2D8ihUwhW43BsGVaLEq8/oehcX8mTCX7NZJrfv/JnbkNJCda7WYfWk1Ea2Dgpg
|
| 23 |
+
PYdBHAkPzE+5MrmfFLqMP4HzlkIqbXGLf1wXSPBs+NLkhMxjW+EiMggrRzkgpbdT
|
| 24 |
+
0D2pD9QU0rs6I+ay6kg70C/xHXjlc/AtLhitMqLswQKBgQDhz8aTa2B8jslQ8R/b
|
| 25 |
+
h1YXmrgkVjHimTYTeLXeE45sMna9HplIWDDUAeoD4GipbIbRVixwS7NTNW0lTGzk
|
| 26 |
+
ZeKiRUzz9og3W3KtXm9Sglfjk9IeOPGUH3VEah0D79LgsB0yi8IOzBCNXluFcI8p
|
| 27 |
+
q5kyqlfB1fRhzkZ6yqq43zD6EQ==
|
| 28 |
+
-----END PRIVATE KEY-----
|
logger.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const fs = require('fs');
|
| 2 |
+
const path = require('path');
|
| 3 |
+
|
| 4 |
+
const logFile = path.join(__dirname, 'app.log');
|
| 5 |
+
const logStream = fs.createWriteStream(logFile, { flags: 'a' });
|
| 6 |
+
|
| 7 |
+
class Logger {
|
| 8 |
+
constructor() {
|
| 9 |
+
this.queueCount = 0;
|
| 10 |
+
this.activeCount = 0;
|
| 11 |
+
this.lastStatusTime = 0;
|
| 12 |
+
}
|
| 13 |
+
|
| 14 |
+
info(msg) {
|
| 15 |
+
this.writeToFile(`[INFO] ${msg}`);
|
| 16 |
+
console.log(`[${new Date().toLocaleTimeString()}] INFO: ${msg}`);
|
| 17 |
+
}
|
| 18 |
+
|
| 19 |
+
warn(msg) {
|
| 20 |
+
this.writeToFile(`[WARN] ${msg}`);
|
| 21 |
+
console.warn(`[${new Date().toLocaleTimeString()}] WARN: ${msg}`);
|
| 22 |
+
}
|
| 23 |
+
|
| 24 |
+
error(msg) {
|
| 25 |
+
this.writeToFile(`[ERROR] ${msg}`);
|
| 26 |
+
console.error(`[${new Date().toLocaleTimeString()}] ERROR: ${msg}`);
|
| 27 |
+
}
|
| 28 |
+
|
| 29 |
+
debug(msg) {
|
| 30 |
+
this.writeToFile(`[DEBUG] ${msg}`);
|
| 31 |
+
}
|
| 32 |
+
|
| 33 |
+
writeToFile(msg) {
|
| 34 |
+
const timestamp = new Date().toISOString();
|
| 35 |
+
logStream.write(`[${timestamp}] ${msg}\n`);
|
| 36 |
+
}
|
| 37 |
+
|
| 38 |
+
// Specialized throttled stats for the console
|
| 39 |
+
updateStats(active, queue) {
|
| 40 |
+
this.activeCount = active;
|
| 41 |
+
this.queueCount = queue;
|
| 42 |
+
|
| 43 |
+
const now = Date.now();
|
| 44 |
+
if (now - this.lastStatusTime > 3000) { // Update console every 3 seconds
|
| 45 |
+
this.lastStatusTime = now;
|
| 46 |
+
process.stdout.write(`\r[Status] Active: ${active} | Queued: ${queue} | Logs: See app.log `);
|
| 47 |
+
}
|
| 48 |
+
}
|
| 49 |
+
}
|
| 50 |
+
|
| 51 |
+
module.exports = new Logger();
|
package-lock.json
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
package.json
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"name": "captcha-service-pro",
|
| 3 |
+
"version": "2.0.0",
|
| 4 |
+
"main": "server.js",
|
| 5 |
+
"scripts": {
|
| 6 |
+
"start": "electron ."
|
| 7 |
+
},
|
| 8 |
+
"dependencies": {
|
| 9 |
+
"body-parser": "^1.20.2",
|
| 10 |
+
"cors": "^2.8.5",
|
| 11 |
+
"electron": "^28.0.0",
|
| 12 |
+
"express": "^4.18.2",
|
| 13 |
+
"puppeteer": "^24.34.0",
|
| 14 |
+
"socket.io": "^4.8.3",
|
| 15 |
+
"sqlite3": "^5.1.7",
|
| 16 |
+
"uuid": "^9.0.1"
|
| 17 |
+
}
|
| 18 |
+
}
|
server.js
ADDED
|
@@ -0,0 +1,399 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const { app: electronApp, session } = require("electron");
|
| 2 |
+
|
| 3 |
+
// Global Proxy Auth Handler
|
| 4 |
+
electronApp.on("login", (event, webContents, details, authInfo, callback) => {
|
| 5 |
+
if (authInfo.isProxy) {
|
| 6 |
+
const ses = webContents.session;
|
| 7 |
+
if (ses.proxyCredentials) {
|
| 8 |
+
logger.debug(`[Main] Providing credentials for proxy via app.on('login')`);
|
| 9 |
+
event.preventDefault();
|
| 10 |
+
callback(ses.proxyCredentials.username, ses.proxyCredentials.password);
|
| 11 |
+
}
|
| 12 |
+
}
|
| 13 |
+
});
|
| 14 |
+
|
| 15 |
+
const express = require("express");
|
| 16 |
+
const bodyParser = require("body-parser");
|
| 17 |
+
const cors = require("cors");
|
| 18 |
+
const sqlite3 = require("sqlite3");
|
| 19 |
+
const { v4: uuidv4 } = require("uuid");
|
| 20 |
+
const path = require("path");
|
| 21 |
+
const RecaptchaSolver = require("./solver");
|
| 22 |
+
const http = require("http");
|
| 23 |
+
const { Server } = require("socket.io");
|
| 24 |
+
const logger = require("./logger");
|
| 25 |
+
|
| 26 |
+
const PORT = 3000;
|
| 27 |
+
const app = express();
|
| 28 |
+
const server = http.createServer(app);
|
| 29 |
+
const io = new Server(server, {
|
| 30 |
+
cors: { origin: "*" },
|
| 31 |
+
});
|
| 32 |
+
|
| 33 |
+
const db = new sqlite3.Database("./database.sqlite");
|
| 34 |
+
|
| 35 |
+
// --- INIT DB ---
|
| 36 |
+
db.serialize(() => {
|
| 37 |
+
// Thêm cột role để phân quyền (admin/user)
|
| 38 |
+
db.run(`CREATE TABLE IF NOT EXISTS users (
|
| 39 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 40 |
+
username TEXT UNIQUE,
|
| 41 |
+
password TEXT,
|
| 42 |
+
api_key TEXT,
|
| 43 |
+
credits INTEGER DEFAULT 0,
|
| 44 |
+
role TEXT DEFAULT 'user',
|
| 45 |
+
hwid TEXT
|
| 46 |
+
)`);
|
| 47 |
+
db.run("ALTER TABLE users ADD COLUMN hwid TEXT", (err) => {
|
| 48 |
+
if (err) {
|
| 49 |
+
if (!err.message.includes("duplicate column name")) {
|
| 50 |
+
console.error("Migration error (hwid):", err.message);
|
| 51 |
+
}
|
| 52 |
+
}
|
| 53 |
+
});
|
| 54 |
+
db.run("UPDATE users SET role = 'admin' WHERE username = 'admin'");
|
| 55 |
+
|
| 56 |
+
// Bảng lưu lịch sử nạp tiền chờ duyệt
|
| 57 |
+
db.run(`CREATE TABLE IF NOT EXISTS transactions (
|
| 58 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 59 |
+
user_id INTEGER,
|
| 60 |
+
username TEXT,
|
| 61 |
+
amount INTEGER,
|
| 62 |
+
method TEXT,
|
| 63 |
+
status TEXT DEFAULT 'pending', -- pending, approved, rejected
|
| 64 |
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
| 65 |
+
)`);
|
| 66 |
+
|
| 67 |
+
// Bảng log lịch sử sử dụng
|
| 68 |
+
db.run(`CREATE TABLE IF NOT EXISTS usage_logs (
|
| 69 |
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
| 70 |
+
user_id INTEGER,
|
| 71 |
+
action TEXT,
|
| 72 |
+
details TEXT,
|
| 73 |
+
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
|
| 74 |
+
)`);
|
| 75 |
+
});
|
| 76 |
+
|
| 77 |
+
app.use(cors());
|
| 78 |
+
app.use(bodyParser.json());
|
| 79 |
+
app.use(express.static(path.join(__dirname, "public")));
|
| 80 |
+
|
| 81 |
+
// --- MIDDLEWARE CHECK ADMIN ---
|
| 82 |
+
const requireAdmin = (req, res, next) => {
|
| 83 |
+
const apiKey = req.headers["x-api-key"];
|
| 84 |
+
db.get("SELECT role FROM users WHERE api_key = ?", [apiKey], (err, row) => {
|
| 85 |
+
if (row && row.role === "admin") next();
|
| 86 |
+
else res.status(403).json({ error: "Bạn không phải Admin!" });
|
| 87 |
+
});
|
| 88 |
+
};
|
| 89 |
+
|
| 90 |
+
// --- AUTH ---
|
| 91 |
+
app.post("/api/register", (req, res) => {
|
| 92 |
+
const { username, password, hwid } = req.body;
|
| 93 |
+
const apiKey = uuidv4();
|
| 94 |
+
|
| 95 |
+
// Kiểm tra xem HWID này đã được cộng tiền lần nào chưa
|
| 96 |
+
db.get("SELECT id FROM users WHERE hwid = ?", [hwid], (err, row) => {
|
| 97 |
+
const initialCredits = row ? 0 : 100; // Nếu đã tồn tại HWID thì 0, mới thì 10
|
| 98 |
+
const message = row
|
| 99 |
+
? "Đăng ký thành công! (Máy này đã nhận bonus trước đó nên không được cộng thêm)"
|
| 100 |
+
: "Đăng ký thành công! Bạn nhận được +100 Credits bonus.";
|
| 101 |
+
|
| 102 |
+
const stmt = db.prepare(
|
| 103 |
+
"INSERT INTO users (username, password, api_key, credits, role, hwid) VALUES (?, ?, ?, ?, 'user', ?)"
|
| 104 |
+
);
|
| 105 |
+
stmt.run(username, password, apiKey, initialCredits, hwid, function (err) {
|
| 106 |
+
if (err) return res.status(400).json({ error: "User đã tồn tại" });
|
| 107 |
+
res.json({ success: true, message: message });
|
| 108 |
+
io.emit("admin:new-user");
|
| 109 |
+
});
|
| 110 |
+
stmt.finalize();
|
| 111 |
+
});
|
| 112 |
+
});
|
| 113 |
+
|
| 114 |
+
app.post("/api/login", (req, res) => {
|
| 115 |
+
const { username, password } = req.body;
|
| 116 |
+
db.get(
|
| 117 |
+
"SELECT * FROM users WHERE username = ? AND password = ?",
|
| 118 |
+
[username, password],
|
| 119 |
+
(err, row) => {
|
| 120 |
+
if (!row) return res.status(401).json({ error: "Sai thông tin" });
|
| 121 |
+
res.json({ success: true, user: row });
|
| 122 |
+
}
|
| 123 |
+
);
|
| 124 |
+
});
|
| 125 |
+
|
| 126 |
+
app.get("/api/me", (req, res) => {
|
| 127 |
+
const apiKey = req.headers["x-api-key"];
|
| 128 |
+
db.get(
|
| 129 |
+
"SELECT username, credits, api_key, role FROM users WHERE api_key = ?",
|
| 130 |
+
[apiKey],
|
| 131 |
+
(err, row) => {
|
| 132 |
+
if (row) res.json(row);
|
| 133 |
+
else res.status(401).json({ error: "Key lỗi" });
|
| 134 |
+
}
|
| 135 |
+
);
|
| 136 |
+
});
|
| 137 |
+
|
| 138 |
+
// --- HISTORY API ---
|
| 139 |
+
app.get("/api/history/usage", (req, res) => {
|
| 140 |
+
const apiKey = req.headers["x-api-key"];
|
| 141 |
+
db.get("SELECT id FROM users WHERE api_key = ?", [apiKey], (err, user) => {
|
| 142 |
+
if (!user) return res.status(401).json({ error: "Auth failed" });
|
| 143 |
+
db.all(
|
| 144 |
+
"SELECT * FROM usage_logs WHERE user_id = ? ORDER BY id DESC LIMIT 50",
|
| 145 |
+
[user.id],
|
| 146 |
+
(err, rows) => {
|
| 147 |
+
res.json(rows);
|
| 148 |
+
}
|
| 149 |
+
);
|
| 150 |
+
});
|
| 151 |
+
});
|
| 152 |
+
|
| 153 |
+
app.get("/api/history/transactions", (req, res) => {
|
| 154 |
+
const apiKey = req.headers["x-api-key"];
|
| 155 |
+
db.get("SELECT id FROM users WHERE api_key = ?", [apiKey], (err, user) => {
|
| 156 |
+
if (!user) return res.status(401).json({ error: "Auth failed" });
|
| 157 |
+
db.all(
|
| 158 |
+
"SELECT * FROM transactions WHERE user_id = ? ORDER BY id DESC",
|
| 159 |
+
[user.id],
|
| 160 |
+
(err, rows) => {
|
| 161 |
+
res.json(rows);
|
| 162 |
+
}
|
| 163 |
+
);
|
| 164 |
+
});
|
| 165 |
+
});
|
| 166 |
+
|
| 167 |
+
// --- USER: TẠO YÊU CẦU NẠP TIỀN ---
|
| 168 |
+
app.post("/api/deposit", (req, res) => {
|
| 169 |
+
const apiKey = req.headers["x-api-key"];
|
| 170 |
+
const { amount, method } = req.body; // amount tính bằng USD (1, 5, 10)
|
| 171 |
+
|
| 172 |
+
db.get(
|
| 173 |
+
"SELECT id, username FROM users WHERE api_key = ?",
|
| 174 |
+
[apiKey],
|
| 175 |
+
(err, user) => {
|
| 176 |
+
if (!user) return res.status(401).json({ error: "User không tồn tại" });
|
| 177 |
+
|
| 178 |
+
const stmt = db.prepare(
|
| 179 |
+
"INSERT INTO transactions (user_id, username, amount, method) VALUES (?, ?, ?, ?)"
|
| 180 |
+
);
|
| 181 |
+
stmt.run(user.id, user.username, amount, method, function (err) {
|
| 182 |
+
if (err) return res.status(500).json({ error: "Lỗi tạo giao dịch" });
|
| 183 |
+
res.json({
|
| 184 |
+
success: true,
|
| 185 |
+
message: "Đã gửi yêu cầu! Vui lòng đợi Admin duyệt.",
|
| 186 |
+
});
|
| 187 |
+
io.emit("admin:new-transaction"); // Notify admin
|
| 188 |
+
});
|
| 189 |
+
stmt.finalize();
|
| 190 |
+
}
|
| 191 |
+
);
|
| 192 |
+
});
|
| 193 |
+
|
| 194 |
+
// --- ADMIN API ---
|
| 195 |
+
|
| 196 |
+
// 1. Lấy danh sách user
|
| 197 |
+
app.get("/api/admin/users", requireAdmin, (req, res) => {
|
| 198 |
+
db.all(
|
| 199 |
+
"SELECT id, username, credits, role, api_key FROM users",
|
| 200 |
+
[],
|
| 201 |
+
(err, rows) => {
|
| 202 |
+
res.json(rows);
|
| 203 |
+
}
|
| 204 |
+
);
|
| 205 |
+
});
|
| 206 |
+
|
| 207 |
+
// 2. Lấy danh sách giao dịch nạp tiền
|
| 208 |
+
app.get("/api/admin/transactions", requireAdmin, (req, res) => {
|
| 209 |
+
db.all("SELECT * FROM transactions ORDER BY id DESC", [], (err, rows) => {
|
| 210 |
+
res.json(rows);
|
| 211 |
+
});
|
| 212 |
+
});
|
| 213 |
+
|
| 214 |
+
// 3. Duyệt hoặc Từ chối nạp tiền
|
| 215 |
+
app.post("/api/admin/approve", requireAdmin, (req, res) => {
|
| 216 |
+
const { transId, action } = req.body; // action: 'approve' | 'reject'
|
| 217 |
+
|
| 218 |
+
db.get("SELECT * FROM transactions WHERE id = ?", [transId], (err, trans) => {
|
| 219 |
+
if (!trans || trans.status !== "pending")
|
| 220 |
+
return res.status(400).json({ error: "Giao dịch không hợp lệ" });
|
| 221 |
+
|
| 222 |
+
if (action === "reject") {
|
| 223 |
+
db.run("UPDATE transactions SET status = 'rejected' WHERE id = ?", [
|
| 224 |
+
transId,
|
| 225 |
+
]);
|
| 226 |
+
io.emit("transaction-updated");
|
| 227 |
+
return res.json({ success: true, message: "Đã từ chối giao dịch" });
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
if (action === "approve") {
|
| 231 |
+
const creditsToAdd = trans.amount * 1000; // $1 = 1000 credits
|
| 232 |
+
db.serialize(() => {
|
| 233 |
+
db.run("UPDATE transactions SET status = 'approved' WHERE id = ?", [
|
| 234 |
+
transId,
|
| 235 |
+
]);
|
| 236 |
+
db.run("UPDATE users SET credits = credits + ? WHERE id = ?", [
|
| 237 |
+
creditsToAdd,
|
| 238 |
+
trans.user_id,
|
| 239 |
+
]);
|
| 240 |
+
});
|
| 241 |
+
return res.json({
|
| 242 |
+
success: true,
|
| 243 |
+
message: `Đã duyệt! Cộng ${creditsToAdd} credits cho ${trans.username}`,
|
| 244 |
+
});
|
| 245 |
+
io.emit("transaction-updated");
|
| 246 |
+
// Notify specific user to update credits (client filters by checking if it's their update)
|
| 247 |
+
io.emit("user:credit-update");
|
| 248 |
+
}
|
| 249 |
+
});
|
| 250 |
+
});
|
| 251 |
+
|
| 252 |
+
// 5. Xóa user
|
| 253 |
+
app.post("/api/admin/delete-user", requireAdmin, (req, res) => {
|
| 254 |
+
const { userId } = req.body;
|
| 255 |
+
db.run("DELETE FROM users WHERE id = ?", [userId], function (err) {
|
| 256 |
+
if (err) return res.status(500).json({ error: "Lỗi xóa user" });
|
| 257 |
+
res.json({ success: true, message: "Đã xóa người dùng thành công." });
|
| 258 |
+
io.emit("admin:user-deleted"); // Notify admin to refresh list
|
| 259 |
+
});
|
| 260 |
+
});
|
| 261 |
+
|
| 262 |
+
// 4. Cộng/Trừ tiền thủ công cho user
|
| 263 |
+
app.post("/api/admin/update-credits", requireAdmin, (req, res) => {
|
| 264 |
+
const { userId, amount } = req.body; // amount có thể là số âm để trừ
|
| 265 |
+
db.run(
|
| 266 |
+
"UPDATE users SET credits = credits + ? WHERE id = ?",
|
| 267 |
+
[amount, userId],
|
| 268 |
+
function (err) {
|
| 269 |
+
if (err) return res.status(500).json({ error: "Lỗi database" });
|
| 270 |
+
res.json({ success: true, message: "Đã cập nhật số dư user." });
|
| 271 |
+
io.emit("user:credit-update");
|
| 272 |
+
}
|
| 273 |
+
);
|
| 274 |
+
});
|
| 275 |
+
|
| 276 |
+
// --- CAPTCHA SOLVE QUEUE ---
|
| 277 |
+
const solveQueue = [];
|
| 278 |
+
let activeSolves = 0;
|
| 279 |
+
const MAX_CONCURRENT_SOLVES = 3; // Tăng lên 3 vì đã tối ưu logging
|
| 280 |
+
|
| 281 |
+
async function processQueue() {
|
| 282 |
+
if (solveQueue.length === 0 || activeSolves >= MAX_CONCURRENT_SOLVES) {
|
| 283 |
+
logger.updateStats(activeSolves, solveQueue.length);
|
| 284 |
+
return;
|
| 285 |
+
}
|
| 286 |
+
|
| 287 |
+
activeSolves++;
|
| 288 |
+
const { req, res, user, targetUrl, targetKey, action, proxy } = solveQueue.shift();
|
| 289 |
+
|
| 290 |
+
logger.debug(`Processing request for ${user.username}. Active: ${activeSolves}/${MAX_CONCURRENT_SOLVES}`);
|
| 291 |
+
logger.updateStats(activeSolves, solveQueue.length);
|
| 292 |
+
|
| 293 |
+
const solver = new RecaptchaSolver();
|
| 294 |
+
try {
|
| 295 |
+
const token = await solver.getRecaptchaToken(
|
| 296 |
+
targetUrl,
|
| 297 |
+
targetKey,
|
| 298 |
+
action || "homepage",
|
| 299 |
+
proxy
|
| 300 |
+
);
|
| 301 |
+
|
| 302 |
+
if (token && !token.startsWith("ERROR")) {
|
| 303 |
+
db.run("UPDATE users SET credits = credits - 1 WHERE id = ?", [user.id]);
|
| 304 |
+
db.run(
|
| 305 |
+
"INSERT INTO usage_logs (user_id, action, details) VALUES (?, ?, ?)",
|
| 306 |
+
[user.id, "SOLVE_CAPTCHA", targetUrl]
|
| 307 |
+
);
|
| 308 |
+
|
| 309 |
+
res.json({ success: true, token: token });
|
| 310 |
+
io.emit("user:credit-update");
|
| 311 |
+
logger.debug(`Success for ${user.username}`);
|
| 312 |
+
} else {
|
| 313 |
+
res.status(500).json({ success: false, error: token });
|
| 314 |
+
logger.warn(`Solver returned error for ${user.username}: ${token}`);
|
| 315 |
+
}
|
| 316 |
+
} catch (e) {
|
| 317 |
+
res.status(500).json({ success: false, error: e.message });
|
| 318 |
+
logger.error(`Catch error for ${user.username}: ${e.message}`);
|
| 319 |
+
} finally {
|
| 320 |
+
activeSolves--;
|
| 321 |
+
logger.updateStats(activeSolves, solveQueue.length);
|
| 322 |
+
processQueue();
|
| 323 |
+
}
|
| 324 |
+
}
|
| 325 |
+
|
| 326 |
+
// --- API GIẢI CAPTCHA ---
|
| 327 |
+
app.post("/api/solve", async (req, res) => {
|
| 328 |
+
const { action, proxy } = req.body;
|
| 329 |
+
const targetUrl = "https://labs.google";
|
| 330 |
+
const targetKey = "6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV";
|
| 331 |
+
const apiKey = req.headers["x-api-key"];
|
| 332 |
+
|
| 333 |
+
if (!apiKey) return res.status(401).json({ error: "Thiếu API Key" });
|
| 334 |
+
|
| 335 |
+
db.get(
|
| 336 |
+
"SELECT * FROM users WHERE api_key = ?",
|
| 337 |
+
[apiKey],
|
| 338 |
+
async (err, user) => {
|
| 339 |
+
if (!user) return res.status(403).json({ error: "API Key không hợp lệ" });
|
| 340 |
+
if (user.credits < 1) return res.status(402).json({ error: "Hết tiền, nạp thêm đi!" });
|
| 341 |
+
|
| 342 |
+
solveQueue.push({ req, res, user, targetUrl, targetKey, action, proxy });
|
| 343 |
+
logger.updateStats(activeSolves, solveQueue.length);
|
| 344 |
+
processQueue();
|
| 345 |
+
}
|
| 346 |
+
);
|
| 347 |
+
});
|
| 348 |
+
|
| 349 |
+
const https = require("https");
|
| 350 |
+
const fs = require("fs");
|
| 351 |
+
|
| 352 |
+
electronApp.on("window-all-closed", (e) => e.preventDefault());
|
| 353 |
+
electronApp.whenReady().then(() => {
|
| 354 |
+
// 1. Start Default Port 3000
|
| 355 |
+
server.listen(PORT, () =>
|
| 356 |
+
console.log(`Server running at: http://localhost:${PORT}`)
|
| 357 |
+
);
|
| 358 |
+
|
| 359 |
+
// 2. Start HTTP Port 80
|
| 360 |
+
try {
|
| 361 |
+
const httpServer = http.createServer(app);
|
| 362 |
+
io.attach(httpServer);
|
| 363 |
+
httpServer.listen(80, () => console.log("HTTP Server running on port 80"));
|
| 364 |
+
httpServer.on("error", (e) =>
|
| 365 |
+
console.error("Error on Port 80:", e.message)
|
| 366 |
+
);
|
| 367 |
+
} catch (e) {
|
| 368 |
+
console.error("Could not start HTTP server on port 80");
|
| 369 |
+
}
|
| 370 |
+
|
| 371 |
+
// 3. Start HTTPS Port 443
|
| 372 |
+
const certPath = path.join(__dirname, "cert.pem");
|
| 373 |
+
const keyPath = path.join(__dirname, "key.pem");
|
| 374 |
+
|
| 375 |
+
if (fs.existsSync(certPath) && fs.existsSync(keyPath)) {
|
| 376 |
+
try {
|
| 377 |
+
const httpsServer = https.createServer(
|
| 378 |
+
{
|
| 379 |
+
key: fs.readFileSync(keyPath),
|
| 380 |
+
cert: fs.readFileSync(certPath),
|
| 381 |
+
},
|
| 382 |
+
app
|
| 383 |
+
);
|
| 384 |
+
io.attach(httpsServer);
|
| 385 |
+
httpsServer.listen(443, () =>
|
| 386 |
+
console.log("HTTPS Server running on port 443")
|
| 387 |
+
);
|
| 388 |
+
httpsServer.on("error", (e) =>
|
| 389 |
+
console.error("Error on Port 443:", e.message)
|
| 390 |
+
);
|
| 391 |
+
} catch (e) {
|
| 392 |
+
console.error("Could not start HTTPS server on port 443");
|
| 393 |
+
}
|
| 394 |
+
} else {
|
| 395 |
+
console.log(
|
| 396 |
+
"No SSL certificates found (cert.pem, key.pem). Skipping HTTPS (443)."
|
| 397 |
+
);
|
| 398 |
+
}
|
| 399 |
+
});
|
solver.js
ADDED
|
@@ -0,0 +1,197 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const { BrowserWindow, session } = require("electron");
|
| 2 |
+
const logger = require("./logger");
|
| 3 |
+
|
| 4 |
+
class RecaptchaSolver {
|
| 5 |
+
constructor() {
|
| 6 |
+
this.solverWindow = null;
|
| 7 |
+
}
|
| 8 |
+
|
| 9 |
+
findChrome() {
|
| 10 |
+
return "Electron";
|
| 11 |
+
}
|
| 12 |
+
|
| 13 |
+
async createSolverWindow(targetUrl, proxy = null) {
|
| 14 |
+
if (this.solverWindow && !this.solverWindow.isDestroyed()) {
|
| 15 |
+
this.solverWindow.destroy();
|
| 16 |
+
}
|
| 17 |
+
|
| 18 |
+
const partition = `temp:solver-${Date.now()}-${Math.random().toString(36).substring(7)}`;
|
| 19 |
+
const ses = session.fromPartition(partition);
|
| 20 |
+
|
| 21 |
+
if (proxy) {
|
| 22 |
+
try {
|
| 23 |
+
const proxyUrl = new URL(proxy);
|
| 24 |
+
const proxyRules = proxyUrl.host; // ip:port
|
| 25 |
+
|
| 26 |
+
logger.debug(`[RecaptchaSolver] Setting proxy: ${proxyRules}`);
|
| 27 |
+
|
| 28 |
+
// Store credentials in session for app-level login handler (app.on('login'))
|
| 29 |
+
ses.proxyCredentials = {
|
| 30 |
+
username: proxyUrl.username,
|
| 31 |
+
password: proxyUrl.password
|
| 32 |
+
};
|
| 33 |
+
|
| 34 |
+
await ses.setProxy({ proxyRules });
|
| 35 |
+
} catch (e) {
|
| 36 |
+
logger.error(`[RecaptchaSolver] Proxy format error: ${proxy}. Skipping.`);
|
| 37 |
+
}
|
| 38 |
+
}
|
| 39 |
+
|
| 40 |
+
this.solverWindow = new BrowserWindow({
|
| 41 |
+
width: 360,
|
| 42 |
+
height: 640,
|
| 43 |
+
show: true,
|
| 44 |
+
x: -32000,
|
| 45 |
+
y: -32000,
|
| 46 |
+
frame: false,
|
| 47 |
+
skipTaskbar: true,
|
| 48 |
+
focusable: false,
|
| 49 |
+
webPreferences: {
|
| 50 |
+
nodeIntegration: false,
|
| 51 |
+
contextIsolation: false,
|
| 52 |
+
session: ses,
|
| 53 |
+
webSecurity: false,
|
| 54 |
+
backgroundThrottling: false,
|
| 55 |
+
},
|
| 56 |
+
});
|
| 57 |
+
|
| 58 |
+
this.solverWindow.webContents.session.webRequest.onHeadersReceived(
|
| 59 |
+
(details, callback) => {
|
| 60 |
+
const responseHeaders = Object.assign({}, details.responseHeaders);
|
| 61 |
+
|
| 62 |
+
if (responseHeaders["content-security-policy"])
|
| 63 |
+
delete responseHeaders["content-security-policy"];
|
| 64 |
+
if (responseHeaders["x-frame-options"])
|
| 65 |
+
delete responseHeaders["x-frame-options"];
|
| 66 |
+
callback({ responseHeaders, cancel: false });
|
| 67 |
+
}
|
| 68 |
+
);
|
| 69 |
+
|
| 70 |
+
this.solverWindow.webContents.setUserAgent(
|
| 71 |
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36"
|
| 72 |
+
);
|
| 73 |
+
|
| 74 |
+
this.solverWindow.webContents.setAudioMuted(true);
|
| 75 |
+
|
| 76 |
+
this.solverWindow.webContents.on("did-fail-load", (event, errorCode, errorDescription, validatedURL) => {
|
| 77 |
+
logger.warn(`[RecaptchaSolver] Failed to load URL: ${validatedURL} | Error: ${errorDescription} (${errorCode})`);
|
| 78 |
+
});
|
| 79 |
+
|
| 80 |
+
await this.solverWindow.loadURL(targetUrl);
|
| 81 |
+
}
|
| 82 |
+
|
| 83 |
+
async simulateHumanInteraction() {
|
| 84 |
+
if (!this.solverWindow || this.solverWindow.isDestroyed()) return;
|
| 85 |
+
|
| 86 |
+
const contents = this.solverWindow.webContents;
|
| 87 |
+
try {
|
| 88 |
+
contents.sendInputEvent({ type: "mouseEnter", x: 10, y: 10 });
|
| 89 |
+
contents.sendInputEvent({ type: "mouseMove", x: 100, y: 100 });
|
| 90 |
+
await new Promise((r) => setTimeout(r, 100));
|
| 91 |
+
contents.sendInputEvent({ type: "mouseMove", x: 200, y: 150 });
|
| 92 |
+
contents.sendInputEvent({
|
| 93 |
+
type: "mouseDown",
|
| 94 |
+
x: 200,
|
| 95 |
+
y: 150,
|
| 96 |
+
button: "left",
|
| 97 |
+
clickCount: 1,
|
| 98 |
+
});
|
| 99 |
+
await new Promise((r) => setTimeout(r, 50));
|
| 100 |
+
contents.sendInputEvent({
|
| 101 |
+
type: "mouseUp",
|
| 102 |
+
x: 200,
|
| 103 |
+
y: 150,
|
| 104 |
+
button: "left",
|
| 105 |
+
clickCount: 1,
|
| 106 |
+
});
|
| 107 |
+
} catch (e) { }
|
| 108 |
+
}
|
| 109 |
+
|
| 110 |
+
async getRecaptchaToken(websiteURL, websiteKey, pageAction, proxy = null) {
|
| 111 |
+
const FIXED_URL = "https://labs.google";
|
| 112 |
+
const FIXED_KEY = "6LdsFiUsAAAAAIjVDZcuLhaHiDn5nnHVXVRQGeMV";
|
| 113 |
+
try {
|
| 114 |
+
await this.createSolverWindow(FIXED_URL, proxy);
|
| 115 |
+
|
| 116 |
+
await this.simulateHumanInteraction();
|
| 117 |
+
|
| 118 |
+
const token = await this.solverWindow.webContents.executeJavaScript(
|
| 119 |
+
`
|
| 120 |
+
(async function() {
|
| 121 |
+
const siteKey = '${FIXED_KEY}';
|
| 122 |
+
const action = '${pageAction}';
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
| 126 |
+
|
| 127 |
+
|
| 128 |
+
async function ensureLibrary() {
|
| 129 |
+
if (window.grecaptcha && window.grecaptcha.execute) return;
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
const old = document.getElementById('recaptcha-solver-script');
|
| 133 |
+
if (old) old.remove();
|
| 134 |
+
|
| 135 |
+
return new Promise((resolve, reject) => {
|
| 136 |
+
const script = document.createElement('script');
|
| 137 |
+
script.id = 'recaptcha-solver-script';
|
| 138 |
+
script.src = 'https://www.google.com/recaptcha/api.js?render=' + siteKey;
|
| 139 |
+
script.onload = () => {
|
| 140 |
+
// Đợi thêm 1s để library init xong
|
| 141 |
+
setTimeout(resolve, 1000);
|
| 142 |
+
};
|
| 143 |
+
script.onerror = () => reject("Load script failed");
|
| 144 |
+
document.head.appendChild(script);
|
| 145 |
+
});
|
| 146 |
+
}
|
| 147 |
+
|
| 148 |
+
try {
|
| 149 |
+
await ensureLibrary();
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
let attempts = 0;
|
| 153 |
+
while (!window.grecaptcha || !window.grecaptcha.execute) {
|
| 154 |
+
if (attempts++ > 20) throw new Error("Timeout waiting for grecaptcha");
|
| 155 |
+
await wait(200);
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
return new Promise((resolve, reject) => {
|
| 159 |
+
window.grecaptcha.ready(() => {
|
| 160 |
+
window.grecaptcha.execute(siteKey, { action: action })
|
| 161 |
+
.then(token => resolve(token))
|
| 162 |
+
.catch(err => reject("Execute Error: " + err.message));
|
| 163 |
+
});
|
| 164 |
+
});
|
| 165 |
+
|
| 166 |
+
} catch (e) {
|
| 167 |
+
return "ERROR: " + e.message;
|
| 168 |
+
}
|
| 169 |
+
})();
|
| 170 |
+
`,
|
| 171 |
+
true
|
| 172 |
+
);
|
| 173 |
+
|
| 174 |
+
if (!token || typeof token !== "string" || token.startsWith("ERROR:")) {
|
| 175 |
+
throw new Error("Token lỗi: " + token);
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
logger.debug(`[Solver] Token generated: ${token.substring(0, 20)}...`);
|
| 179 |
+
|
| 180 |
+
this.close();
|
| 181 |
+
return token;
|
| 182 |
+
} catch (error) {
|
| 183 |
+
logger.error(`[Solver] Error: ${error.message}`);
|
| 184 |
+
this.close();
|
| 185 |
+
return "ERROR: " + error.message;
|
| 186 |
+
}
|
| 187 |
+
}
|
| 188 |
+
|
| 189 |
+
async close() {
|
| 190 |
+
if (this.solverWindow && !this.solverWindow.isDestroyed()) {
|
| 191 |
+
this.solverWindow.destroy();
|
| 192 |
+
this.solverWindow = null;
|
| 193 |
+
}
|
| 194 |
+
}
|
| 195 |
+
}
|
| 196 |
+
|
| 197 |
+
module.exports = RecaptchaSolver;
|
stress_test_queue.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const axios = require('axios');
|
| 2 |
+
|
| 3 |
+
const API_KEY = '5db36803-12cb-469b-8919-cc02dc284693'; // Replace with a valid key from your DB if needed
|
| 4 |
+
const URL = 'http://localhost:3000/api/solve';
|
| 5 |
+
|
| 6 |
+
async function sendRequest(id) {
|
| 7 |
+
console.log(`[Test] Sending request #${id}...`);
|
| 8 |
+
try {
|
| 9 |
+
const startTime = Date.now();
|
| 10 |
+
const response = await axios.post(URL, {
|
| 11 |
+
action: 'test_stress'
|
| 12 |
+
}, {
|
| 13 |
+
headers: { 'x-api-key': API_KEY }
|
| 14 |
+
});
|
| 15 |
+
const duration = ((Date.now() - startTime) / 1000).toFixed(2);
|
| 16 |
+
console.log(`[Test] Request #${id} finished in ${duration}s. Success: ${response.data.success}`);
|
| 17 |
+
} catch (error) {
|
| 18 |
+
console.error(`[Test] Request #${id} failed: ${error.response?.data?.error || error.message}`);
|
| 19 |
+
}
|
| 20 |
+
}
|
| 21 |
+
|
| 22 |
+
async function runTest() {
|
| 23 |
+
console.log("Starting stress test with 5 concurrent requests...");
|
| 24 |
+
console.log("Since MAX_CONCURRENT_SOLVES is 2, you should see them being processed 2 at a time.");
|
| 25 |
+
|
| 26 |
+
const requests = [];
|
| 27 |
+
for (let i = 1; i <= 5; i++) {
|
| 28 |
+
requests.push(sendRequest(i));
|
| 29 |
+
}
|
| 30 |
+
|
| 31 |
+
await Promise.all(requests);
|
| 32 |
+
console.log("Stress test completed.");
|
| 33 |
+
}
|
| 34 |
+
|
| 35 |
+
runTest();
|
test_proxy.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
const http = require('http');
|
| 2 |
+
const https = require('https');
|
| 3 |
+
|
| 4 |
+
/**
|
| 5 |
+
* Script kiểm tra API giải captcha với Proxy
|
| 6 |
+
* Hỗ trợ cả HTTP và HTTPS, tự động chọn module phù hợp
|
| 7 |
+
*/
|
| 8 |
+
async function testCaptchaWithProxy() {
|
| 9 |
+
const API_URL = 'https://kinxsoftware.online/api/solve';
|
| 10 |
+
const API_KEY = 'f064b7bf-8bcc-47f4-befe-1831540cffa5';
|
| 11 |
+
const PROXY = 'http://zlet9i3y:oZJQ0o4V@157.10.49.215:44128';
|
| 12 |
+
|
| 13 |
+
const postData = JSON.stringify({
|
| 14 |
+
action: 'FLOW_GENERATION',
|
| 15 |
+
proxy: PROXY
|
| 16 |
+
});
|
| 17 |
+
|
| 18 |
+
const url = new URL(API_URL);
|
| 19 |
+
const isHttps = url.protocol === 'https:';
|
| 20 |
+
const transport = isHttps ? https : http;
|
| 21 |
+
|
| 22 |
+
const options = {
|
| 23 |
+
hostname: url.hostname,
|
| 24 |
+
port: url.port || (isHttps ? 443 : 80),
|
| 25 |
+
path: url.pathname,
|
| 26 |
+
method: 'POST',
|
| 27 |
+
headers: {
|
| 28 |
+
'Content-Type': 'application/json',
|
| 29 |
+
'x-api-key': API_KEY,
|
| 30 |
+
'Content-Length': Buffer.byteLength(postData)
|
| 31 |
+
},
|
| 32 |
+
// Bỏ qua kiểm tra SSL nếu là localhost hoặc server tự cấp chứng chỉ
|
| 33 |
+
rejectUnauthorized: false
|
| 34 |
+
};
|
| 35 |
+
|
| 36 |
+
console.log('--- Đang gửi yêu cầu giải captcha với Proxy ---');
|
| 37 |
+
console.log('URL:', API_URL);
|
| 38 |
+
console.log('Proxy:', PROXY);
|
| 39 |
+
|
| 40 |
+
const req = transport.request(options, (res) => {
|
| 41 |
+
let data = '';
|
| 42 |
+
|
| 43 |
+
res.on('data', (chunk) => {
|
| 44 |
+
data += chunk;
|
| 45 |
+
});
|
| 46 |
+
|
| 47 |
+
res.on('end', () => {
|
| 48 |
+
console.log('Phản hồi từ Server:', res.statusCode);
|
| 49 |
+
try {
|
| 50 |
+
const jsonResponse = JSON.parse(data);
|
| 51 |
+
console.log('Kết quả:', JSON.stringify(jsonResponse, null, 2));
|
| 52 |
+
} catch (e) {
|
| 53 |
+
console.log('Phản hồi không phải JSON:', data);
|
| 54 |
+
}
|
| 55 |
+
});
|
| 56 |
+
});
|
| 57 |
+
|
| 58 |
+
req.on('error', (e) => {
|
| 59 |
+
console.error('Lỗi kết nối:', e.message);
|
| 60 |
+
});
|
| 61 |
+
|
| 62 |
+
req.write(postData);
|
| 63 |
+
req.end();
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
testCaptchaWithProxy();
|