arn-browser 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/package.json +31 -47
- package/src/all_routes/routeWithSuperagent.d.ts +67 -0
- package/src/all_routes/routeWithSuperagent.js +322 -0
- package/src/human-cursor/HumanCursor.js +448 -0
- package/src/human-cursor/bezier.js +248 -0
- package/src/human-cursor/index.d.ts +154 -0
- package/src/human-cursor/index.js +9 -0
- package/src/human-cursor/randomizer.js +149 -0
- package/src/human-cursor/tweening.js +260 -0
- package/src/index.d.ts +19 -0
- package/src/index.js +15 -0
- package/src/others/totp-generator.d.ts +15 -0
- package/src/others/totp-generator.js +86 -0
- package/src/utility/deleteDirectory.js +105 -0
- package/src/utility/launchBrowser.d.ts +248 -0
- package/src/utility/launchBrowser.js +899 -0
- package/src/utility/multilogin_token_manager.js +164 -0
- package/src/utility/playwright-helper.d.ts +61 -0
- package/src/utility/playwright-helper.js +129 -0
- package/src/utility/proxy-utility/custom-proxy.d.ts +93 -0
- package/src/utility/proxy-utility/custom-proxy.js +625 -0
- package/src/utility/proxy-utility/proxy-chain.d.ts +123 -0
- package/src/utility/proxy-utility/proxy-chain.js +337 -0
- package/src/utility/proxy-utility/proxy-helper.d.ts +91 -0
- package/src/utility/proxy-utility/proxy-helper.js +222 -0
- package/dist/__main__.d.ts +0 -2
- package/dist/__main__.js +0 -127
- package/dist/__version__.d.ts +0 -11
- package/dist/__version__.js +0 -16
- package/dist/addons.d.ts +0 -17
- package/dist/addons.js +0 -70
- package/dist/data-files/territoryInfo.xml +0 -2024
- package/dist/data-files/webgl_data.db +0 -0
- package/dist/exceptions.d.ts +0 -76
- package/dist/exceptions.js +0 -153
- package/dist/fingerprints.d.ts +0 -4
- package/dist/fingerprints.js +0 -82
- package/dist/index.d.ts +0 -3
- package/dist/index.js +0 -3
- package/dist/ip.d.ts +0 -25
- package/dist/ip.js +0 -90
- package/dist/locale.d.ts +0 -26
- package/dist/locale.js +0 -280
- package/dist/mappings/browserforge.config.d.ts +0 -47
- package/dist/mappings/browserforge.config.js +0 -72
- package/dist/mappings/fonts.config.d.ts +0 -6
- package/dist/mappings/fonts.config.js +0 -822
- package/dist/mappings/warnings.config.d.ts +0 -16
- package/dist/mappings/warnings.config.js +0 -28
- package/dist/pkgman.d.ts +0 -62
- package/dist/pkgman.js +0 -347
- package/dist/server.d.ts +0 -6
- package/dist/server.js +0 -9
- package/dist/sync_api.d.ts +0 -7
- package/dist/sync_api.js +0 -27
- package/dist/utils.d.ts +0 -88
- package/dist/utils.js +0 -500
- package/dist/virtdisplay.d.ts +0 -20
- package/dist/virtdisplay.js +0 -123
- package/dist/warnings.d.ts +0 -4
- package/dist/warnings.js +0 -30
- package/dist/webgl/db-compat.d.ts +0 -9
- package/dist/webgl/db-compat.js +0 -44
- package/dist/webgl/sample.d.ts +0 -19
- package/dist/webgl/sample.js +0 -85
- /package/{LICENSE.md → LICENSE} +0 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
import {
|
|
2
|
+
EC2Client,
|
|
3
|
+
StopInstancesCommand,
|
|
4
|
+
StartInstancesCommand,
|
|
5
|
+
DescribeInstancesCommand,
|
|
6
|
+
ReleaseAddressCommand,
|
|
7
|
+
AllocateAddressCommand,
|
|
8
|
+
AssociateAddressCommand,
|
|
9
|
+
DescribeAddressesCommand,
|
|
10
|
+
DisassociateAddressCommand,
|
|
11
|
+
CreateTagsCommand,
|
|
12
|
+
} from "@aws-sdk/client-ec2";
|
|
13
|
+
import { SocksProxyAgent } from "socks-proxy-agent";
|
|
14
|
+
import { setTimeout as sleep } from "timers/promises";
|
|
15
|
+
import https from "https";
|
|
16
|
+
import http from "http";
|
|
17
|
+
import { arn, query } from "arn-knexjs";
|
|
18
|
+
|
|
19
|
+
// ==========================================
|
|
20
|
+
// SECTION 1: CONFIGURATION & UTILITIES
|
|
21
|
+
// ==========================================
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Native Node.js implementation of an HTTP GET request to replace Superagent.
|
|
25
|
+
* Supports custom agents (SOCKS/HTTP).
|
|
26
|
+
*
|
|
27
|
+
* @param {string} url - The URL to fetch.
|
|
28
|
+
* @param {object} agent - The proxy agent (optional).
|
|
29
|
+
* @param {number} timeout - Request timeout in ms.
|
|
30
|
+
* @returns {Promise<string>} - The response body string.
|
|
31
|
+
*/
|
|
32
|
+
function nativeGet(url, agent = null, timeout = 5000) {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const urlObj = new URL(url);
|
|
35
|
+
const requestModule = urlObj.protocol === "https:" ? https : http;
|
|
36
|
+
|
|
37
|
+
const options = {
|
|
38
|
+
agent: agent,
|
|
39
|
+
timeout: timeout,
|
|
40
|
+
headers: { "User-Agent": "Mozilla/5.0 (Node.js Proxy Checker)" },
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const req = requestModule.get(url, options, (res) => {
|
|
44
|
+
if (res.statusCode < 200 || res.statusCode >= 300) {
|
|
45
|
+
res.resume(); // Consume response data to free up memory
|
|
46
|
+
return reject(new Error(`Status Code: ${res.statusCode}`));
|
|
47
|
+
}
|
|
48
|
+
let data = "";
|
|
49
|
+
res.setEncoding("utf8");
|
|
50
|
+
res.on("data", (chunk) => {
|
|
51
|
+
data += chunk;
|
|
52
|
+
});
|
|
53
|
+
res.on("end", () => resolve(data));
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
req.on("error", (err) => reject(err));
|
|
57
|
+
req.on("timeout", () => {
|
|
58
|
+
req.destroy();
|
|
59
|
+
reject(new Error("Request Timed Out"));
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ==========================================
|
|
65
|
+
// SECTION 2: AWS EC2 CLIENT HELPERS
|
|
66
|
+
// ==========================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Fetches AWS credentials from the database and initializes the EC2 Client.
|
|
70
|
+
*
|
|
71
|
+
* @param {object} params - Object containing { instance_name }.
|
|
72
|
+
* @returns {Promise<object>} - Returns { ec2Client, instanceId, elasticIpName }.
|
|
73
|
+
*/
|
|
74
|
+
async function fetchAwsClientAndInstanceId({ instance_name }) {
|
|
75
|
+
const { data: [data] = [], error } = await arn.single(
|
|
76
|
+
query("api_aws_proxy_account").select("*").where({ instance_name }).limit(1)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
if (error || !data) {
|
|
80
|
+
console.error("Error fetching AWS details:", error || "data not found");
|
|
81
|
+
throw new Error("Failed to fetch AWS details from Supabase.");
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const ec2Client = new EC2Client({
|
|
85
|
+
region: data.region,
|
|
86
|
+
credentials: {
|
|
87
|
+
accessKeyId: data.access_key,
|
|
88
|
+
secretAccessKey: data.secret_access_key,
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
ec2Client,
|
|
94
|
+
instanceId: data.instance_id,
|
|
95
|
+
elasticIpName: data.instance_name, // Using instance_name as the tag for Elastic IP
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ==========================================
|
|
100
|
+
// SECTION 3: INSTANCE STATE MANAGEMENT
|
|
101
|
+
// ==========================================
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Gets the current state name of the instance (e.g., 'running', 'stopped').
|
|
105
|
+
*/
|
|
106
|
+
export async function getInstanceStatus({ instance_name }) {
|
|
107
|
+
const { ec2Client, instanceId } = await fetchAwsClientAndInstanceId({ instance_name });
|
|
108
|
+
|
|
109
|
+
const params = { InstanceIds: [instanceId] };
|
|
110
|
+
const data = await ec2Client.send(new DescribeInstancesCommand(params));
|
|
111
|
+
return data.Reservations[0].Instances[0].State.Name;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Gets the Public IP address currently assigned to the instance.
|
|
116
|
+
*/
|
|
117
|
+
export async function getPublicIpAddress({ instance_name }) {
|
|
118
|
+
const { ec2Client, instanceId } = await fetchAwsClientAndInstanceId({ instance_name });
|
|
119
|
+
|
|
120
|
+
const params = { InstanceIds: [instanceId] };
|
|
121
|
+
const data = await ec2Client.send(new DescribeInstancesCommand(params));
|
|
122
|
+
return data.Reservations[0].Instances[0].PublicIpAddress;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const MAX_RETRIES = 30;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Stops the EC2 instance.
|
|
129
|
+
* Waits for pending states to resolve before attempting to stop.
|
|
130
|
+
*/
|
|
131
|
+
export async function manageInstanceStop({ instance_name }) {
|
|
132
|
+
const { ec2Client, instanceId } = await fetchAwsClientAndInstanceId({ instance_name });
|
|
133
|
+
const params = { InstanceIds: [instanceId] };
|
|
134
|
+
|
|
135
|
+
// 1. Check current state and wait if in transition
|
|
136
|
+
let currentState = await getInstanceStatus({ instance_name });
|
|
137
|
+
let waitingRetries = 0;
|
|
138
|
+
|
|
139
|
+
while (currentState !== "running" && currentState !== "stopped" && waitingRetries < MAX_RETRIES) {
|
|
140
|
+
console.log("Waiting for instance to be either running or stopped...");
|
|
141
|
+
await sleep(10000);
|
|
142
|
+
currentState = await getInstanceStatus({ instance_name });
|
|
143
|
+
waitingRetries++;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (waitingRetries === MAX_RETRIES) {
|
|
147
|
+
console.log("Maximum retries reached. Instance did not reach a valid state.");
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (currentState === "stopped") {
|
|
152
|
+
console.log("Instance already stopped...");
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// 2. Send Stop Command
|
|
157
|
+
try {
|
|
158
|
+
const data = await ec2Client.send(new StopInstancesCommand(params));
|
|
159
|
+
console.log("Instance stopping...");
|
|
160
|
+
|
|
161
|
+
// 3. Wait for confirmation
|
|
162
|
+
let stoppingRetries = 0;
|
|
163
|
+
let newState = await getInstanceStatus({ instance_name });
|
|
164
|
+
while (newState !== "stopped" && stoppingRetries < MAX_RETRIES) {
|
|
165
|
+
await sleep(10000);
|
|
166
|
+
newState = await getInstanceStatus({ instance_name });
|
|
167
|
+
stoppingRetries++;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (newState !== "stopped") {
|
|
171
|
+
console.log("Timeout reached. Instance might not have stopped.");
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
console.log("Instance stopped.");
|
|
176
|
+
return data.StoppingInstances[0].InstanceId;
|
|
177
|
+
} catch (err) {
|
|
178
|
+
console.error("Error stopping instance", err);
|
|
179
|
+
throw err;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Starts the EC2 instance.
|
|
185
|
+
* Waits for pending states to resolve before attempting to start.
|
|
186
|
+
*/
|
|
187
|
+
export async function manageInstanceStart({ instance_name }) {
|
|
188
|
+
const { ec2Client, instanceId } = await fetchAwsClientAndInstanceId({ instance_name });
|
|
189
|
+
const params = { InstanceIds: [instanceId] };
|
|
190
|
+
|
|
191
|
+
// 1. Check current state and wait if in transition
|
|
192
|
+
let currentState = await getInstanceStatus({ instance_name });
|
|
193
|
+
let waitingRetries = 0;
|
|
194
|
+
|
|
195
|
+
while (currentState !== "running" && currentState !== "stopped" && waitingRetries < MAX_RETRIES) {
|
|
196
|
+
console.log("Waiting for instance to be either running or stopped...");
|
|
197
|
+
await sleep(10000);
|
|
198
|
+
currentState = await getInstanceStatus({ instance_name });
|
|
199
|
+
waitingRetries++;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
if (waitingRetries === MAX_RETRIES) {
|
|
203
|
+
console.log("Maximum retries reached. Instance did not reach a valid state.");
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (currentState === "running") {
|
|
208
|
+
console.log("Instance already running...");
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 2. Send Start Command
|
|
213
|
+
try {
|
|
214
|
+
const data = await ec2Client.send(new StartInstancesCommand(params));
|
|
215
|
+
console.log("Instance starting...");
|
|
216
|
+
|
|
217
|
+
// 3. Wait for confirmation
|
|
218
|
+
let startingRetries = 0;
|
|
219
|
+
let newState = await getInstanceStatus({ instance_name });
|
|
220
|
+
while (newState !== "running" && startingRetries < MAX_RETRIES) {
|
|
221
|
+
await sleep(10000);
|
|
222
|
+
newState = await getInstanceStatus({ instance_name });
|
|
223
|
+
startingRetries++;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (newState !== "running") {
|
|
227
|
+
console.log("Timeout reached. Instance might not have started.");
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
console.log("Instance running.");
|
|
232
|
+
return data.StartingInstances[0].InstanceId;
|
|
233
|
+
} catch (err) {
|
|
234
|
+
console.error("Error starting instance", err);
|
|
235
|
+
throw err;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// ==========================================
|
|
240
|
+
// SECTION 4: ELASTIC IP MANAGEMENT
|
|
241
|
+
// ==========================================
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Associates an existing Elastic IP or allocates a new one if none exist.
|
|
245
|
+
*/
|
|
246
|
+
export async function AssociateAddress({ instance_name }) {
|
|
247
|
+
const { ec2Client, instanceId, elasticIpName } = await fetchAwsClientAndInstanceId({ instance_name });
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
// Fetch Elastic IPs tagged with this instance name
|
|
251
|
+
const describeAddressesData = await ec2Client.send(
|
|
252
|
+
new DescribeAddressesCommand({ Filters: [{ Name: "tag:Name", Values: [elasticIpName] }] })
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
// Check if already associated correctly
|
|
256
|
+
for (const address of describeAddressesData.Addresses) {
|
|
257
|
+
if (address.InstanceId === instanceId) {
|
|
258
|
+
console.log(`Elastic IP ${elasticIpName} is already associated with this instance.`);
|
|
259
|
+
return true;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Find an unassociated IP from the pool and associate it
|
|
264
|
+
for (const address of describeAddressesData.Addresses) {
|
|
265
|
+
if (!address.InstanceId) {
|
|
266
|
+
await ec2Client.send(
|
|
267
|
+
new AssociateAddressCommand({ InstanceId: instanceId, AllocationId: address.AllocationId })
|
|
268
|
+
);
|
|
269
|
+
console.log(`Elastic IP ${elasticIpName} associated successfully with instance.`);
|
|
270
|
+
return true;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// No free IP found: Allocate new, Tag it, Associate it
|
|
275
|
+
const allocateResponse = await ec2Client.send(new AllocateAddressCommand({ Domain: "vpc" }));
|
|
276
|
+
|
|
277
|
+
await ec2Client.send(
|
|
278
|
+
new CreateTagsCommand({
|
|
279
|
+
Resources: [allocateResponse.AllocationId],
|
|
280
|
+
Tags: [{ Key: "Name", Value: elasticIpName }],
|
|
281
|
+
})
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
await ec2Client.send(
|
|
285
|
+
new AssociateAddressCommand({ InstanceId: instanceId, AllocationId: allocateResponse.AllocationId })
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
console.log(`New Elastic IP allocated, tagged '${elasticIpName}', and associated.`);
|
|
289
|
+
return true;
|
|
290
|
+
} catch (error) {
|
|
291
|
+
console.error("Error during Elastic IP association:", error);
|
|
292
|
+
return false;
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Disassociates the Elastic IP from the instance.
|
|
298
|
+
*/
|
|
299
|
+
export async function DisassociateAddress({ instance_name }) {
|
|
300
|
+
const { ec2Client, instanceId, elasticIpName } = await fetchAwsClientAndInstanceId({ instance_name });
|
|
301
|
+
|
|
302
|
+
try {
|
|
303
|
+
const describeAddressesData = await ec2Client.send(
|
|
304
|
+
new DescribeAddressesCommand({ Filters: [{ Name: "tag:Name", Values: [elasticIpName] }] })
|
|
305
|
+
);
|
|
306
|
+
|
|
307
|
+
if (describeAddressesData.Addresses.length > 0) {
|
|
308
|
+
const address = describeAddressesData.Addresses[0];
|
|
309
|
+
// Only disassociate if it is currently attached to our instance
|
|
310
|
+
if (address.InstanceId === instanceId) {
|
|
311
|
+
await ec2Client.send(new DisassociateAddressCommand({ AssociationId: address.AssociationId }));
|
|
312
|
+
console.log(`Elastic IP ${elasticIpName} disassociated successfully.`);
|
|
313
|
+
return true;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
console.log(`No associated Elastic IP found with name ${elasticIpName} to disassociate.`);
|
|
317
|
+
return false;
|
|
318
|
+
} catch (error) {
|
|
319
|
+
console.error("Error during disassociation", error);
|
|
320
|
+
return false;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Deletes (Releases) any Elastic IPs that are not currently attached to an instance.
|
|
326
|
+
*/
|
|
327
|
+
export async function DeleteUnassociatedElasticIPs({ instance_name }) {
|
|
328
|
+
const { ec2Client, elasticIpName } = await fetchAwsClientAndInstanceId({ instance_name });
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
const describeAddressesData = await ec2Client.send(
|
|
332
|
+
new DescribeAddressesCommand({ Filters: [{ Name: "tag:Name", Values: [elasticIpName] }] })
|
|
333
|
+
);
|
|
334
|
+
let foundUnassociated = false;
|
|
335
|
+
|
|
336
|
+
for (const address of describeAddressesData.Addresses) {
|
|
337
|
+
if (!address.AssociationId) {
|
|
338
|
+
await ec2Client.send(new ReleaseAddressCommand({ AllocationId: address.AllocationId }));
|
|
339
|
+
console.log(`Unassociated Elastic IP ${elasticIpName} deleted successfully.`);
|
|
340
|
+
foundUnassociated = true;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (!foundUnassociated) {
|
|
345
|
+
console.log(`No unassociated Elastic IPs found with name ${elasticIpName} to delete.`);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
return true;
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.error("Error during deletion of unassociated Elastic IPs", error);
|
|
351
|
+
return false;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// ==========================================
|
|
356
|
+
// SECTION 5: DATABASE & LOGIC
|
|
357
|
+
// ==========================================
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Resets servers that haven't been updated in 25+ minutes.
|
|
361
|
+
* Stops the instance and releases IPs.
|
|
362
|
+
*/
|
|
363
|
+
export async function resetServersAndIp() {
|
|
364
|
+
const thirtyMinutesAgo = new Date(new Date().getTime() - 25 * 60000).toISOString();
|
|
365
|
+
|
|
366
|
+
const { data, error } = await arn.single(
|
|
367
|
+
query("api_aws_proxy_account").select("*").where({ status: 1 }).andWhere("updated_at", "<=", thirtyMinutesAgo)
|
|
368
|
+
);
|
|
369
|
+
|
|
370
|
+
if (error) {
|
|
371
|
+
console.log(error);
|
|
372
|
+
return;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
for (const server of data) {
|
|
376
|
+
await DisassociateAddress({ instance_name: server.instance_name });
|
|
377
|
+
|
|
378
|
+
if (await DeleteUnassociatedElasticIPs({ instance_name: server.instance_name })) {
|
|
379
|
+
console.log("DeleteUnassociatedElasticIPs Successful");
|
|
380
|
+
} else {
|
|
381
|
+
console.log("DeleteUnassociatedElasticIPs Failed");
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
await manageInstanceStop({ instance_name: server.instance_name });
|
|
385
|
+
|
|
386
|
+
// Update DB status to 2 (Available/Reset)
|
|
387
|
+
const { error: updateError } = await arn.single(
|
|
388
|
+
query("api_aws_proxy_account").update({ status: 2 }).where({ instance_name: server.instance_name })
|
|
389
|
+
);
|
|
390
|
+
|
|
391
|
+
if (updateError) {
|
|
392
|
+
console.log(`Error updating status for ${server.instance_name}:`, updateError);
|
|
393
|
+
} else {
|
|
394
|
+
console.log(`Status updated to 2 for ${server.instance_name}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Updates the database to indicate the server is currently being used (status: 1).
|
|
401
|
+
*/
|
|
402
|
+
async function keepServerAlivetoDB({ instance_name }) {
|
|
403
|
+
const { error: updateError } = await arn.single(
|
|
404
|
+
query("api_aws_proxy_account")
|
|
405
|
+
.update({
|
|
406
|
+
status: 1,
|
|
407
|
+
total_used: query.raw("total_used + 1"), // Increment total_used
|
|
408
|
+
})
|
|
409
|
+
.where({ instance_name: instance_name })
|
|
410
|
+
);
|
|
411
|
+
|
|
412
|
+
if (updateError) {
|
|
413
|
+
console.log(`Error updating status for ${instance_name}:`, updateError);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
/**
|
|
418
|
+
* Tracks usage statistics for a specific IP address in the database.
|
|
419
|
+
*/
|
|
420
|
+
async function updateProxyToDB({ instance_name, ip_address }) {
|
|
421
|
+
// 1. Get Account ID
|
|
422
|
+
let { data: [accountData] = [], error: accountError } = await arn.single(
|
|
423
|
+
query("api_aws_proxy_account").select("id").where({ instance_name }).limit(1)
|
|
424
|
+
);
|
|
425
|
+
|
|
426
|
+
if (accountError || !accountData) {
|
|
427
|
+
console.error(`Error fetching account for ${instance_name}:`, accountError || "accountData not found");
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
if (accountData) {
|
|
432
|
+
const accountId = accountData.id;
|
|
433
|
+
|
|
434
|
+
// 2. Check for existing usage record
|
|
435
|
+
let { data: [usageData] = [], error: usageError } = await arn.single(
|
|
436
|
+
query("api_aws_proxy_usage")
|
|
437
|
+
.select("id", "total_used")
|
|
438
|
+
.where({ account_id: accountId, ip_address: ip_address })
|
|
439
|
+
.limit(1)
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
if (usageError) {
|
|
443
|
+
console.error(`Error checking usage for account ID ${accountId}:`, usageError);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// 3. Insert or Update
|
|
448
|
+
if (!usageData) {
|
|
449
|
+
// Insert new record
|
|
450
|
+
const { error: insertError } = await arn.single(
|
|
451
|
+
query("api_aws_proxy_usage").insert([{ account_id: accountId, ip_address: ip_address, total_used: 1 }])
|
|
452
|
+
);
|
|
453
|
+
if (insertError) console.error(`Error inserting usage:`, insertError);
|
|
454
|
+
} else {
|
|
455
|
+
// Update existing record
|
|
456
|
+
const { error: updateError } = await arn.single(
|
|
457
|
+
query("api_aws_proxy_usage")
|
|
458
|
+
.update({
|
|
459
|
+
total_used: usageData.total_used + 1,
|
|
460
|
+
last_used_time: new Date().toISOString(),
|
|
461
|
+
})
|
|
462
|
+
.where({ id: usageData.id })
|
|
463
|
+
);
|
|
464
|
+
if (updateError) console.error(`Error updating usage:`, updateError);
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// ==========================================
|
|
470
|
+
// SECTION 6: PROXY RETRIEVAL FLOWS
|
|
471
|
+
// ==========================================
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* FLOW 1: Standard Active Proxy.
|
|
475
|
+
* Starts instance, cycles IPs by associating/disassociating until a working one is found.
|
|
476
|
+
*/
|
|
477
|
+
export async function getActiveProxy({ instance_name }) {
|
|
478
|
+
await keepServerAlivetoDB({ instance_name });
|
|
479
|
+
await manageInstanceStart({ instance_name });
|
|
480
|
+
|
|
481
|
+
const MAX_IP_RETRIES = 3;
|
|
482
|
+
for (let i = 0; i < MAX_IP_RETRIES; i++) {
|
|
483
|
+
console.log("Unique IP Check Loop: ", i + 1);
|
|
484
|
+
|
|
485
|
+
try {
|
|
486
|
+
// Cycle the IP
|
|
487
|
+
await AssociateAddress({ instance_name });
|
|
488
|
+
await DisassociateAddress({ instance_name });
|
|
489
|
+
// Note: DeleteUnassociatedElasticIPs ensures we don't pay for unused IPs,
|
|
490
|
+
// but the original logic comments it out here. Uncomment if needed.
|
|
491
|
+
} catch (err) {
|
|
492
|
+
console.error("Error managing instance state:", err);
|
|
493
|
+
continue;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
console.log("Please wait 1-2 Minutes");
|
|
497
|
+
await sleep(2000);
|
|
498
|
+
|
|
499
|
+
let publicIp;
|
|
500
|
+
try {
|
|
501
|
+
publicIp = await getPublicIpAddress({ instance_name });
|
|
502
|
+
console.log("Got New IP: ", publicIp);
|
|
503
|
+
} catch (err) {
|
|
504
|
+
console.error("Error getting public IP:", err);
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
await sleep(15000); // Wait for boot
|
|
509
|
+
const proxyAlive = await isProxyAlive(publicIp, 9002);
|
|
510
|
+
await sleep(5000);
|
|
511
|
+
|
|
512
|
+
if (!proxyAlive) {
|
|
513
|
+
console.log("No Unique IP Found or Proxy Dead");
|
|
514
|
+
continue;
|
|
515
|
+
} else {
|
|
516
|
+
await updateProxyToDB({ instance_name, ip_address: publicIp });
|
|
517
|
+
return publicIp;
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return null;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/**
|
|
525
|
+
* FLOW 2: Hard Reset Proxy.
|
|
526
|
+
* Stops the instance completely, then starts it to get a new IP.
|
|
527
|
+
*/
|
|
528
|
+
export async function getActiveProxyWithStartStop({ instance_name }) {
|
|
529
|
+
await keepServerAlivetoDB({ instance_name });
|
|
530
|
+
const MAX_IP_RETRIES = 3;
|
|
531
|
+
|
|
532
|
+
for (let i = 0; i < MAX_IP_RETRIES; i++) {
|
|
533
|
+
await manageInstanceStop({ instance_name });
|
|
534
|
+
await manageInstanceStart({ instance_name });
|
|
535
|
+
|
|
536
|
+
let publicIp = await getPublicIpAddress({ instance_name });
|
|
537
|
+
|
|
538
|
+
await sleep(15000); // Wait for boot
|
|
539
|
+
const proxyAlive = await isProxyAlive(publicIp, 9002);
|
|
540
|
+
|
|
541
|
+
if (!proxyAlive) {
|
|
542
|
+
console.log("No Unique IP Found or Proxy Dead");
|
|
543
|
+
continue;
|
|
544
|
+
} else {
|
|
545
|
+
await updateProxyToDB({ instance_name, ip_address: publicIp });
|
|
546
|
+
return publicIp;
|
|
547
|
+
}
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
return null;
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* FLOW 3: Conditional Proxy.
|
|
555
|
+
* Checks if IP exists; if so, refreshes it. If not, allocates new.
|
|
556
|
+
*/
|
|
557
|
+
export async function getActiveProxyConditional({ instance_name }) {
|
|
558
|
+
await keepServerAlivetoDB({ instance_name });
|
|
559
|
+
await manageInstanceStart({ instance_name });
|
|
560
|
+
|
|
561
|
+
const { ec2Client, instanceId } = await fetchAwsClientAndInstanceId({ instance_name });
|
|
562
|
+
const describeAddressesData = await ec2Client.send(
|
|
563
|
+
new DescribeAddressesCommand({ Filters: [{ Name: "instance-id", Values: [instanceId] }] })
|
|
564
|
+
);
|
|
565
|
+
|
|
566
|
+
if (describeAddressesData.Addresses.length > 0) {
|
|
567
|
+
console.log("Elastic IP already associated. Disassociating and deleting unassociated IPs...");
|
|
568
|
+
await DisassociateAddress({ instance_name });
|
|
569
|
+
await DeleteUnassociatedElasticIPs({ instance_name });
|
|
570
|
+
} else {
|
|
571
|
+
console.log("No Elastic IP associated. Associating a new one...");
|
|
572
|
+
await AssociateAddress({ instance_name });
|
|
573
|
+
await DeleteUnassociatedElasticIPs({ instance_name });
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
console.log("Please wait 1-2 minutes for changes to take effect.");
|
|
577
|
+
await sleep(2000);
|
|
578
|
+
|
|
579
|
+
let publicIp = await getPublicIpAddress({ instance_name });
|
|
580
|
+
const proxyAlive = await isProxyAlive(publicIp, 9002);
|
|
581
|
+
|
|
582
|
+
if (!proxyAlive) {
|
|
583
|
+
console.log("Proxy is not alive or no unique IP found.");
|
|
584
|
+
return null;
|
|
585
|
+
} else {
|
|
586
|
+
await updateProxyToDB({ instance_name, ip_address: publicIp });
|
|
587
|
+
return publicIp;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// ==========================================
|
|
592
|
+
// SECTION 7: PROXY VALIDATION
|
|
593
|
+
// ==========================================
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Checks if the proxy is functioning by attempting to reach an external endpoint.
|
|
597
|
+
* Uses native Node.js HTTP/HTTPS modules via 'nativeGet'.
|
|
598
|
+
*
|
|
599
|
+
* @param {string} proxyHost - IP address of the proxy.
|
|
600
|
+
* @param {number} proxyPort - Port of the proxy (usually 9002).
|
|
601
|
+
*/
|
|
602
|
+
export async function isProxyAlive(proxyHost, proxyPort) {
|
|
603
|
+
const agent = new SocksProxyAgent(`socks5://${proxyHost}:${proxyPort}`);
|
|
604
|
+
const maxAttempts = 60;
|
|
605
|
+
|
|
606
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
607
|
+
try {
|
|
608
|
+
// Using nativeGet instead of superagent
|
|
609
|
+
await nativeGet("https://httpbin.org/ip", agent, 5000);
|
|
610
|
+
|
|
611
|
+
console.log(`Proxy ${proxyHost} is alive!`);
|
|
612
|
+
return proxyHost;
|
|
613
|
+
} catch (error) {
|
|
614
|
+
if (attempt < maxAttempts) {
|
|
615
|
+
console.log("Proxy live check loop...", attempt);
|
|
616
|
+
await sleep(500);
|
|
617
|
+
} else {
|
|
618
|
+
console.log("Proxy is dead or not responding.", error.message);
|
|
619
|
+
return null;
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
return null;
|
|
625
|
+
}
|