diff --git a/package.json b/package.json index 828e0d1b..20ff59d9 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "express": "^4.18.2", "express-async-errors": "^3.1.1", "hbs": "^4.2.0", + "https-proxy-agent": "^7.0.2", "jimp": "^0.16.13", "join": "^3.0.0", "js-yaml": "^4.1.0", @@ -73,7 +74,6 @@ "node-mime-types": "^1.1.0", "node-windows": "^1.0.0-beta.8", "pino": "^8.11.0", - "proxy-agent": "^6.3.0", "qrcode": "^1.5.1", "qrcode-terminal": "^0.12.0", "redis": "^4.6.5", diff --git a/src/utils/makeProxyAgent.ts b/src/utils/makeProxyAgent.ts new file mode 100644 index 00000000..45486523 --- /dev/null +++ b/src/utils/makeProxyAgent.ts @@ -0,0 +1,17 @@ +import { HttpsProxyAgent } from 'https-proxy-agent'; + +import { wa } from '../whatsapp/types/wa.types'; + +export function makeProxyAgent(proxy: wa.Proxy | string) { + if (typeof proxy === 'string') { + return new HttpsProxyAgent(proxy); + } + + const { host, password, port, protocol, username } = proxy; + let proxyUrl = `${protocol}://${host}:${port}`; + + if (username && password) { + proxyUrl = `${protocol}://${username}:${password}@${host}:${port}`; + } + return new HttpsProxyAgent(proxyUrl); +} diff --git a/src/whatsapp/controllers/proxy.controller.ts b/src/whatsapp/controllers/proxy.controller.ts index 555c5975..0dc79a3a 100644 --- a/src/whatsapp/controllers/proxy.controller.ts +++ b/src/whatsapp/controllers/proxy.controller.ts @@ -1,19 +1,25 @@ import axios from 'axios'; import { Logger } from '../../config/logger.config'; -import { BadRequestException } from '../../exceptions'; +import { BadRequestException, NotFoundException } from '../../exceptions'; +import { makeProxyAgent } from '../../utils/makeProxyAgent'; import { InstanceDto } from '../dto/instance.dto'; import { ProxyDto } from '../dto/proxy.dto'; +import { WAMonitoringService } from '../services/monitor.service'; import { ProxyService } from '../services/proxy.service'; const logger = new Logger('ProxyController'); export class ProxyController { - constructor(private readonly proxyService: ProxyService) {} + constructor(private readonly proxyService: ProxyService, private readonly waMonitor: WAMonitoringService) {} public async createProxy(instance: InstanceDto, data: ProxyDto) { logger.verbose('requested createProxy from ' + instance.instanceName + ' instance'); + if (!this.waMonitor.waInstances[instance.instanceName]) { + throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`); + } + if (!data.enabled) { logger.verbose('proxy disabled'); data.proxy = null; @@ -21,8 +27,7 @@ export class ProxyController { if (data.proxy) { logger.verbose('proxy enabled'); - const { host, port, protocol, username, password } = data.proxy; - const testProxy = await this.testProxy(host, port, protocol, username, password); + const testProxy = await this.testProxy(data.proxy); if (!testProxy) { throw new BadRequestException('Invalid proxy'); } @@ -33,37 +38,30 @@ export class ProxyController { public async findProxy(instance: InstanceDto) { logger.verbose('requested findProxy from ' + instance.instanceName + ' instance'); + + if (!this.waMonitor.waInstances[instance.instanceName]) { + throw new NotFoundException(`The "${instance.instanceName}" instance does not exist`); + } + return this.proxyService.find(instance); } - private async testProxy(host: string, port: string, protocol: string, username?: string, password?: string) { + private async testProxy(proxy: ProxyDto['proxy']) { logger.verbose('requested testProxy'); try { - let proxyConfig: any = { - host: host, - port: parseInt(port), - protocol: protocol, - }; - - if (username && password) { - proxyConfig = { - ...proxyConfig, - auth: { - username: username, - password: password, - }, - }; - } - const serverIp = await axios.get('http://meuip.com/api/meuip.php'); - - const response = await axios.get('http://meuip.com/api/meuip.php', { - proxy: proxyConfig, + const serverIp = await axios.get('https://icanhazip.com/'); + const response = await axios.get('https://icanhazip.com/', { + httpsAgent: makeProxyAgent(proxy), }); logger.verbose('testProxy response: ' + response.data); return response.data !== serverIp.data; } catch (error) { - logger.error('testProxy error: ' + error); + let errorMessage = error; + if (axios.isAxiosError(error) && error.response.data) { + errorMessage = error.response.data; + } + logger.error('testProxy error: ' + errorMessage); return false; } } diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 19155f7c..0d8fb5b2 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -45,7 +45,6 @@ import { getMIMEType } from 'node-mime-types'; import { release } from 'os'; import { join } from 'path'; import P from 'pino'; -import { ProxyAgent } from 'proxy-agent'; import qrcode, { QRCodeToDataURLOptions } from 'qrcode'; import qrcodeTerminal from 'qrcode-terminal'; import sharp from 'sharp'; @@ -73,6 +72,7 @@ import { dbserver } from '../../libs/db.connect'; import { RedisCache } from '../../libs/redis.client'; import { getIO } from '../../libs/socket.server'; import { getSQS, removeQueues as removeQueuesSQS } from '../../libs/sqs.server'; +import { makeProxyAgent } from '../../utils/makeProxyAgent'; import { useMultiFileAuthStateDb } from '../../utils/use-multi-file-auth-state-db'; import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; import { @@ -1385,24 +1385,21 @@ export class WAStartupService { this.logger.info('Proxy enabled: ' + this.localProxy.proxy); if (this.localProxy.proxy.host.includes('proxyscrape')) { - const response = await axios.get(this.localProxy.proxy.host); - const text = response.data; - const proxyUrls = text.split('\r\n'); - const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length)); - const proxyUrl = 'http://' + proxyUrls[rand]; - options = { - agent: new ProxyAgent(proxyUrl as any), - }; - } else { - let proxyUri = - this.localProxy.proxy.protocol + '://' + this.localProxy.proxy.host + ':' + this.localProxy.proxy.port; - - if (this.localProxy.proxy.username && this.localProxy.proxy.password) { - proxyUri = `${this.localProxy.proxy.username}:${this.localProxy.proxy.password}@${proxyUri}`; + try { + const response = await axios.get(this.localProxy.proxy.host); + const text = response.data; + const proxyUrls = text.split('\r\n'); + const rand = Math.floor(Math.random() * Math.floor(proxyUrls.length)); + const proxyUrl = 'http://' + proxyUrls[rand]; + options = { + agent: makeProxyAgent(proxyUrl), + }; + } catch (error) { + this.localProxy.enabled = false; } - + } else { options = { - agent: new ProxyAgent(proxyUri as any), + agent: makeProxyAgent(this.localProxy.proxy), }; } } @@ -1489,8 +1486,8 @@ export class WAStartupService { if (this.localProxy.enabled) { this.logger.verbose('Proxy enabled'); options = { - agent: new ProxyAgent(this.localProxy.proxy as any), - fetchAgent: new ProxyAgent(this.localProxy.proxy as any), + agent: makeProxyAgent(this.localProxy.proxy), + fetchAgent: makeProxyAgent(this.localProxy.proxy), }; } diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index 0b5da554..3e52504f 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -119,7 +119,7 @@ export const websocketController = new WebsocketController(websocketService); const proxyService = new ProxyService(waMonitor); -export const proxyController = new ProxyController(proxyService); +export const proxyController = new ProxyController(proxyService, waMonitor); const chamaaiService = new ChamaaiService(waMonitor, configService);