From 763e30bd1d4f4c4547ec65ba61018e89bc39e156 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 09:34:39 -0300 Subject: [PATCH 1/9] fix: fix in update settings that needed to restart after updated --- CHANGELOG.md | 6 ++++++ package.json | 2 +- src/whatsapp/services/whatsapp.service.ts | 19 ++++++++++++++++++- 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d70a8685..384a28af 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 1.3.2 (homolog) + +### Fixed + +* Fix in update settings that needed to restart after updated + # 1.3.1 (2023-07-20 07:48) ### Fixed diff --git a/package.json b/package.json index b56b045d..7bc6f476 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "evolution-api", - "version": "1.3.1", + "version": "1.3.2", "description": "Rest api for communication with WhatsApp", "main": "./dist/src/main.js", "scripts": { diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 65cb40bb..6fead74a 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -2364,6 +2364,9 @@ export class WAStartupService { public async fetchMessages(query: MessageQuery) { this.logger.verbose('Fetching messages'); if (query?.where) { + if (query.where?.key?.remoteJid) { + query.where.key.remoteJid = this.createJid(query.where.key.remoteJid); + } query.where.owner = this.instance.name; } else { query = { @@ -2379,6 +2382,9 @@ export class WAStartupService { public async fetchStatusMessage(query: MessageUpQuery) { this.logger.verbose('Fetching status messages'); if (query?.where) { + if (query.where?.remoteJid) { + query.where.remoteJid = this.createJid(query.where.remoteJid); + } query.where.owner = this.instance.name; } else { query = { @@ -2423,8 +2429,19 @@ export class WAStartupService { this.logger.verbose('Groups add privacy updated'); // reinicia a instancia + this.client?.ws?.close(); - return { update: 'success', data: await this.client.fetchPrivacySettings() }; + return { + update: 'success', + data: { + readreceipts: settings.privacySettings.readreceipts, + profile: settings.privacySettings.profile, + status: settings.privacySettings.status, + online: settings.privacySettings.online, + last: settings.privacySettings.last, + groupadd: settings.privacySettings.groupadd, + }, + }; } catch (error) { throw new InternalServerErrorException( 'Error updating privacy settings', From 796287a776cc900e5b3d9fcaaf8363419e3f8f21 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 11:38:36 -0300 Subject: [PATCH 2/9] fix: Adjustments to search endpoint for contacts, chats, messages and Status messages --- CHANGELOG.md | 2 ++ src/whatsapp/repository/auth.repository.ts | 2 +- src/whatsapp/services/monitor.service.ts | 2 +- src/whatsapp/services/whatsapp.service.ts | 23 ++++++++++++++++++++-- src/whatsapp/types/wa.types.ts | 1 + 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 384a28af..875d7ac4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,8 @@ ### Fixed * Fix in update settings that needed to restart after updated +* Correction in the use of the api with mongodb +* Adjustments to search endpoint for contacts, chats, messages and Status messages # 1.3.1 (2023-07-20 07:48) diff --git a/src/whatsapp/repository/auth.repository.ts b/src/whatsapp/repository/auth.repository.ts index c795737c..0d7e177f 100644 --- a/src/whatsapp/repository/auth.repository.ts +++ b/src/whatsapp/repository/auth.repository.ts @@ -1,5 +1,5 @@ import { join } from 'path'; -import { Auth, ConfigService } from '../../config/env.config'; +import { Auth, ConfigService, Database } from '../../config/env.config'; import { IInsert, Repository } from '../abstract/abstract.repository'; import { IAuthModel, AuthRaw } from '../models'; import { readFileSync } from 'fs'; diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 8b347c21..8fdac88a 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -90,7 +90,7 @@ export class WAMonitoringService { const findChatwoot = await this.waInstances[key].findChatwoot(); - if (findChatwoot.enabled) { + if (findChatwoot && findChatwoot.enabled) { chatwoot = { ...findChatwoot, webhook_url: `${urlServer}/chatwoot/webhook/${key}`, diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 6fead74a..8035bbfb 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -328,7 +328,7 @@ export class WAStartupService { if (!data) { this.logger.verbose('Chatwoot not found'); - throw new NotFoundException('Chatwoot not found'); + return null; } this.logger.verbose(`Chatwoot account id: ${data.account_id}`); @@ -351,7 +351,7 @@ export class WAStartupService { const expose = this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES; const tokenStore = await this.repository.auth.find(this.instanceName); - const instanceApikey = tokenStore.apikey || 'Apikey not found'; + const instanceApikey = tokenStore?.apikey || 'Apikey not found'; const globalApiKey = this.configService.get('AUTHENTICATION').API_KEY.KEY; @@ -1190,6 +1190,22 @@ export class WAStartupService { this.logger.verbose('Sending data to webhook in event MESSAGE_DELETE'); await this.sendDataWebhook(Events.MESSAGES_DELETE, key); + + const message: MessageUpdateRaw = { + ...key, + status: 'DELETED', + datetime: Date.now(), + owner: this.instance.name, + }; + + this.logger.verbose(message); + + this.logger.verbose('Inserting message in database'); + await this.repository.messageUpdate.insert( + [message], + this.instance.name, + database.SAVE_DATA.MESSAGE_UPDATE, + ); return; } @@ -2351,6 +2367,9 @@ export class WAStartupService { this.logger.verbose('Fetching contacts'); if (query?.where) { query.where.owner = this.instance.name; + if (query.where?.id) { + query.where.id = this.createJid(query.where.id); + } } else { query = { where: { diff --git a/src/whatsapp/types/wa.types.ts b/src/whatsapp/types/wa.types.ts index 169df515..6869545f 100644 --- a/src/whatsapp/types/wa.types.ts +++ b/src/whatsapp/types/wa.types.ts @@ -63,6 +63,7 @@ export declare namespace wa { | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' + | 'DELETED' | 'PLAYED'; } From 897f8164b90af48202e4b9d696429afef2855a97 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 12:13:03 -0300 Subject: [PATCH 3/9] fix: Now when deleting the instance, the data referring to it in mongodb is also deleted --- CHANGELOG.md | 1 + src/whatsapp/services/monitor.service.ts | 31 +++++++++++++++++++++--- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 875d7ac4..2311edac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ * Fix in update settings that needed to restart after updated * Correction in the use of the api with mongodb * Adjustments to search endpoint for contacts, chats, messages and Status messages +* Now when deleting the instance, the data referring to it in mongodb is also deleted # 1.3.1 (2023-07-20 07:48) diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 8fdac88a..6aa43b70 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -18,6 +18,16 @@ import { Db } from 'mongodb'; import { initInstance } from '../whatsapp.module'; import { RedisCache } from '../../db/redis.client'; import { execSync } from 'child_process'; +import { dbserver } from '../../db/db.connect'; +import mongoose from 'mongoose'; +import { + AuthModel, + ChatwootModel, + ContactModel, + MessageModel, + MessageUpModel, + WebhookModel, +} from '../models'; export class WAMonitoringService { constructor( @@ -45,6 +55,8 @@ export class WAMonitoringService { private dbInstance: Db; + private dbStore = dbserver; + private readonly logger = new Logger(WAMonitoringService.name); public readonly waInstances: Record = {}; @@ -218,11 +230,8 @@ export class WAMonitoringService { } public async cleaningStoreFiles(instanceName: string) { - this.logger.verbose('cleaning store files instance: ' + instanceName); - if (!this.db.ENABLED) { - const instance = this.waInstances[instanceName]; - + this.logger.verbose('cleaning store files instance: ' + instanceName); rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`); @@ -233,7 +242,21 @@ export class WAMonitoringService { execSync(`rm -rf ${join(STORE_DIR, 'auth', 'apikey', instanceName + '.json')}`); execSync(`rm -rf ${join(STORE_DIR, 'webhook', instanceName + '.json')}`); execSync(`rm -rf ${join(STORE_DIR, 'chatwoot', instanceName + '*')}`); + + return; } + + this.logger.verbose('cleaning store database instance: ' + instanceName); + + await AuthModel.deleteMany({ owner: instanceName }); + await ContactModel.deleteMany({ owner: instanceName }); + await MessageModel.deleteMany({ owner: instanceName }); + await MessageUpModel.deleteMany({ owner: instanceName }); + await AuthModel.deleteMany({ _id: instanceName }); + await WebhookModel.deleteMany({ _id: instanceName }); + await ChatwootModel.deleteMany({ _id: instanceName }); + + return; } public async loadInstance() { From d7f264c1c2265d145b0ca0542cd1984600974080 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 12:26:43 -0300 Subject: [PATCH 4/9] fix: It is now validated if the instance name contains uppercase and special characters --- CHANGELOG.md | 1 + src/whatsapp/controllers/instance.controller.ts | 6 ++++++ src/whatsapp/services/whatsapp.service.ts | 2 -- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2311edac..d9b5ac6b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ * Correction in the use of the api with mongodb * Adjustments to search endpoint for contacts, chats, messages and Status messages * Now when deleting the instance, the data referring to it in mongodb is also deleted +* It is now validated if the instance name contains uppercase and special characters # 1.3.1 (2023-07-20 07:48) diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index f0adb3a3..ef0c00aa 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -42,6 +42,12 @@ export class InstanceController { }: InstanceDto) { this.logger.verbose('requested createInstance from ' + instanceName + ' instance'); + if (instanceName !== instanceName.toLowerCase().replace(/[^a-z0-9]/g, '')) { + throw new BadRequestException( + 'The instance name must be lowercase and without special characters', + ); + } + const mode = this.configService.get('AUTHENTICATION').INSTANCE.MODE; if (mode === 'container') { diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 8035bbfb..549dd7d6 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -1412,8 +1412,6 @@ export class WAStartupService { } if (Number(countryCode) === 52 || Number(countryCode) === 54) { - console.log('numero mexicano'); - const formattedMXARNumber = this.formatMXOrARNumber(number); if (formattedMXARNumber !== number) { From 091b920a222821b20290ad76650c49b7cbd62f99 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 12:40:58 -0300 Subject: [PATCH 5/9] fix: It is now validated if the instance name contains uppercase and special characters --- src/whatsapp/repository/repository.manager.ts | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/whatsapp/repository/repository.manager.ts b/src/whatsapp/repository/repository.manager.ts index aa0fbd65..6c2a3091 100644 --- a/src/whatsapp/repository/repository.manager.ts +++ b/src/whatsapp/repository/repository.manager.ts @@ -55,7 +55,6 @@ export class RepositoryBroker { const chatwootDir = join(storePath, 'chatwoot'); const tempDir = join(storePath, 'temp'); - // Check if directories exist, create them if not if (!fs.existsSync(authDir)) { this.logger.verbose('creating auth dir: ' + authDir); fs.mkdirSync(authDir, { recursive: true }); @@ -91,6 +90,21 @@ export class RepositoryBroker { } catch (error) { this.logger.error(error); } + } else { + const storePath = join(process.cwd(), 'store'); + + this.logger.verbose('creating store path: ' + storePath); + + const tempDir = join(storePath, 'temp'); + + if (!fs.existsSync(tempDir)) { + this.logger.verbose('creating temp dir: ' + tempDir); + fs.mkdirSync(tempDir, { recursive: true }); + } + try { + } catch (error) { + this.logger.error(error); + } } } } From 19039aa281a3a192f833572f8b2355877f88cf73 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 13:12:55 -0300 Subject: [PATCH 6/9] fix: For compatibility reasons, container mode has been removed --- CHANGELOG.md | 1 + Docker/.env.example | 10 - src/config/env.config.ts | 19 +- src/dev-env.yml | 12 +- .../controllers/instance.controller.ts | 375 ++++++------------ src/whatsapp/services/monitor.service.ts | 4 - src/whatsapp/services/whatsapp.service.ts | 11 +- src/whatsapp/whatsapp.module.ts | 107 ----- 8 files changed, 121 insertions(+), 418 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d9b5ac6b..2a864fe5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Adjustments to search endpoint for contacts, chats, messages and Status messages * Now when deleting the instance, the data referring to it in mongodb is also deleted * It is now validated if the instance name contains uppercase and special characters +* For compatibility reasons, container mode has been removed # 1.3.1 (2023-07-20 07:48) diff --git a/Docker/.env.example b/Docker/.env.example index 81eee8ca..a3782d4e 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -97,13 +97,3 @@ AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true # seconds - 3600s ===1h | zero (0) - never expires AUTHENTICATION_JWT_EXPIRIN_IN=0 AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG' -# Set the instance name and webhook url to create an instance in init the application -# With this option activated, you work with a url per webhook event, respecting the local url and the name of each event -# container or server -AUTHENTICATION_INSTANCE_MODE=server -# if you are using container mode, set the container name and the webhook url to default instance -AUTHENTICATION_INSTANCE_NAME=evolution -AUTHENTICATION_INSTANCE_WEBHOOK_URL='' -AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID=1 -AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN=123456 -AUTHENTICATION_INSTANCE_CHATWOOT_URL='' diff --git a/src/config/env.config.ts b/src/config/env.config.ts index 7221f474..88b718de 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -94,20 +94,12 @@ export type EventsWebhook = { export type ApiKey = { KEY: string }; export type Jwt = { EXPIRIN_IN: number; SECRET: string }; -export type Instance = { - NAME: string; - WEBHOOK_URL: string; - MODE: string; - CHATWOOT_ACCOUNT_ID: string; - CHATWOOT_TOKEN: string; - CHATWOOT_URL: string; -}; + export type Auth = { API_KEY: ApiKey; EXPOSE_IN_FETCH_INSTANCES: boolean; JWT: Jwt; TYPE: 'jwt' | 'apikey'; - INSTANCE: Instance; }; export type DelInstance = number | boolean; @@ -276,15 +268,6 @@ export class ConfigService { : 3600, SECRET: process.env.AUTHENTICATION_JWT_SECRET, }, - INSTANCE: { - NAME: process.env.AUTHENTICATION_INSTANCE_NAME, - WEBHOOK_URL: process.env.AUTHENTICATION_INSTANCE_WEBHOOK_URL, - MODE: process.env.AUTHENTICATION_INSTANCE_MODE, - CHATWOOT_ACCOUNT_ID: - process.env.AUTHENTICATION_INSTANCE_CHATWOOT_ACCOUNT_ID || '', - CHATWOOT_TOKEN: process.env.AUTHENTICATION_INSTANCE_CHATWOOT_TOKEN || '', - CHATWOOT_URL: process.env.AUTHENTICATION_INSTANCE_CHATWOOT_URL || '', - }, }, }; } diff --git a/src/dev-env.yml b/src/dev-env.yml index c0f907fc..41368ea4 100644 --- a/src/dev-env.yml +++ b/src/dev-env.yml @@ -136,14 +136,4 @@ AUTHENTICATION: # Set the secret key to encrypt and decrypt your token and its expiration time. JWT: EXPIRIN_IN: 0 # seconds - 3600s === 1h | zero (0) - never expires - SECRET: L=0YWt]b2w[WF>#>:&E` - # Set the instance name and webhook url to create an instance in init the application - INSTANCE: - # With this option activated, you work with a url per webhook event, respecting the local url and the name of each event - MODE: server # container or server - # if you are using container mode, set the container name and the webhook url to default instance - NAME: evolution - WEBHOOK_URL: - CHATWOOT_ACCOUNT_ID: 1 - CHATWOOT_TOKEN: 123456 - CHATWOOT_URL: + SECRET: L=0YWt]b2w[WF>#>:&E` \ No newline at end of file diff --git a/src/whatsapp/controllers/instance.controller.ts b/src/whatsapp/controllers/instance.controller.ts index ef0c00aa..75911848 100644 --- a/src/whatsapp/controllers/instance.controller.ts +++ b/src/whatsapp/controllers/instance.controller.ts @@ -48,273 +48,80 @@ export class InstanceController { ); } - const mode = this.configService.get('AUTHENTICATION').INSTANCE.MODE; + this.logger.verbose('checking duplicate token'); + await this.authService.checkDuplicateToken(token); - if (mode === 'container') { - this.logger.verbose('container mode'); + this.logger.verbose('creating instance'); + const instance = new WAStartupService( + this.configService, + this.eventEmitter, + this.repository, + this.cache, + ); + instance.instanceName = instanceName + .toLowerCase() + .replace(/[^a-z0-9]/g, '') + .replace(' ', ''); - if (Object.keys(this.waMonitor.waInstances).length > 0) { - throw new BadRequestException([ - 'Instance already created', - 'Only one instance can be created', - ]); + this.logger.verbose('instance: ' + instance.instanceName + ' created'); + + this.waMonitor.waInstances[instance.instanceName] = instance; + this.waMonitor.delInstanceTime(instance.instanceName); + + this.logger.verbose('generating hash'); + const hash = await this.authService.generateHash( + { + instanceName: instance.instanceName, + }, + token, + ); + + this.logger.verbose('hash: ' + hash + ' generated'); + + let getEvents: string[]; + + if (webhook) { + if (!isURL(webhook, { require_tld: false })) { + throw new BadRequestException('Invalid "url" property in webhook'); } - this.logger.verbose('checking duplicate token'); - await this.authService.checkDuplicateToken(token); - - this.logger.verbose('creating instance'); - const instance = new WAStartupService( - this.configService, - this.eventEmitter, - this.repository, - this.cache, - ); - instance.instanceName = instanceName - .toLowerCase() - .replace(/[^a-z0-9]/g, '') - .replace(' ', ''); - this.logger.verbose('instance: ' + instance.instanceName + ' created'); - - this.waMonitor.waInstances[instance.instanceName] = instance; - this.waMonitor.delInstanceTime(instance.instanceName); - - this.logger.verbose('generating hash'); - const hash = await this.authService.generateHash( - { - instanceName: instance.instanceName, - }, - token, - ); - - this.logger.verbose('hash: ' + hash + ' generated'); - - let getEvents: string[]; - - if (webhook) { - if (!isURL(webhook, { require_tld: false })) { - throw new BadRequestException('Invalid "url" property in webhook'); - } - this.logger.verbose('creating webhook'); - try { - this.webhookService.create(instance, { - enabled: true, - url: webhook, - events, - webhook_by_events, - }); - - getEvents = (await this.webhookService.find(instance)).events; - } catch (error) { - this.logger.log(error); - } - } - - if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) { - this.logger.verbose('instance created'); - this.logger.verbose({ - instance: { - instanceName: instance.instanceName, - status: 'created', - }, - hash, - webhook, - events: getEvents, - }); - - return { - instance: { - instanceName: instance.instanceName, - status: 'created', - }, - hash, - webhook, - events: getEvents, - }; - } - - if (!chatwoot_account_id) { - throw new BadRequestException('account_id is required'); - } - - if (!chatwoot_token) { - throw new BadRequestException('token is required'); - } - - if (!chatwoot_url) { - throw new BadRequestException('url is required'); - } - - if (!isURL(chatwoot_url, { require_tld: false })) { - throw new BadRequestException('Invalid "url" property in chatwoot'); - } - - const urlServer = this.configService.get('SERVER').URL; - + this.logger.verbose('creating webhook'); try { - this.chatwootService.create(instance, { + this.webhookService.create(instance, { enabled: true, - account_id: chatwoot_account_id, - token: chatwoot_token, - url: chatwoot_url, - sign_msg: chatwoot_sign_msg || false, - name_inbox: instance.instanceName, + url: webhook, + events, + webhook_by_events, }); - this.chatwootService.initInstanceChatwoot( - instance, - instance.instanceName, - `${urlServer}/chatwoot/webhook/${instance.instanceName}`, - qrcode, - ); + getEvents = (await this.webhookService.find(instance)).events; } catch (error) { this.logger.log(error); } + } - return { + if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) { + let getQrcode: wa.QrCode; + + if (qrcode) { + this.logger.verbose('creating qrcode'); + await instance.connectToWhatsapp(); + await delay(2000); + getQrcode = instance.qrCode; + } + + this.logger.verbose('instance created'); + this.logger.verbose({ instance: { instanceName: instance.instanceName, status: 'created', }, hash, - chatwoot: { - enabled: true, - account_id: chatwoot_account_id, - token: chatwoot_token, - url: chatwoot_url, - sign_msg: chatwoot_sign_msg || false, - name_inbox: instance.instanceName, - webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`, - }, - }; - } else { - this.logger.verbose('server mode'); - - this.logger.verbose('checking duplicate token'); - await this.authService.checkDuplicateToken(token); - - this.logger.verbose('creating instance'); - const instance = new WAStartupService( - this.configService, - this.eventEmitter, - this.repository, - this.cache, - ); - instance.instanceName = instanceName - .toLowerCase() - .replace(/[^a-z0-9]/g, '') - .replace(' ', ''); - - this.logger.verbose('instance: ' + instance.instanceName + ' created'); - - this.waMonitor.waInstances[instance.instanceName] = instance; - this.waMonitor.delInstanceTime(instance.instanceName); - - this.logger.verbose('generating hash'); - const hash = await this.authService.generateHash( - { - instanceName: instance.instanceName, - }, - token, - ); - - this.logger.verbose('hash: ' + hash + ' generated'); - - let getEvents: string[]; - - if (webhook) { - if (!isURL(webhook, { require_tld: false })) { - throw new BadRequestException('Invalid "url" property in webhook'); - } - - this.logger.verbose('creating webhook'); - try { - this.webhookService.create(instance, { - enabled: true, - url: webhook, - events, - webhook_by_events, - }); - - getEvents = (await this.webhookService.find(instance)).events; - } catch (error) { - this.logger.log(error); - } - } - - if (!chatwoot_account_id || !chatwoot_token || !chatwoot_url) { - let getQrcode: wa.QrCode; - - if (qrcode) { - this.logger.verbose('creating qrcode'); - await instance.connectToWhatsapp(); - await delay(2000); - getQrcode = instance.qrCode; - } - - this.logger.verbose('instance created'); - this.logger.verbose({ - instance: { - instanceName: instance.instanceName, - status: 'created', - }, - hash, - webhook, - webhook_by_events, - events: getEvents, - qrcode: getQrcode, - }); - - return { - instance: { - instanceName: instance.instanceName, - status: 'created', - }, - hash, - webhook, - webhook_by_events, - events: getEvents, - qrcode: getQrcode, - }; - } - - if (!chatwoot_account_id) { - throw new BadRequestException('account_id is required'); - } - - if (!chatwoot_token) { - throw new BadRequestException('token is required'); - } - - if (!chatwoot_url) { - throw new BadRequestException('url is required'); - } - - if (!isURL(chatwoot_url, { require_tld: false })) { - throw new BadRequestException('Invalid "url" property in chatwoot'); - } - - const urlServer = this.configService.get('SERVER').URL; - - try { - this.chatwootService.create(instance, { - enabled: true, - account_id: chatwoot_account_id, - token: chatwoot_token, - url: chatwoot_url, - sign_msg: chatwoot_sign_msg || false, - name_inbox: instance.instanceName, - }); - - this.chatwootService.initInstanceChatwoot( - instance, - instance.instanceName, - `${urlServer}/chatwoot/webhook/${instance.instanceName}`, - qrcode, - ); - } catch (error) { - this.logger.log(error); - } + webhook, + webhook_by_events, + events: getEvents, + qrcode: getQrcode, + }); return { instance: { @@ -325,17 +132,67 @@ export class InstanceController { webhook, webhook_by_events, events: getEvents, - chatwoot: { - enabled: true, - account_id: chatwoot_account_id, - token: chatwoot_token, - url: chatwoot_url, - sign_msg: chatwoot_sign_msg || false, - name_inbox: instance.instanceName, - webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`, - }, + qrcode: getQrcode, }; } + + if (!chatwoot_account_id) { + throw new BadRequestException('account_id is required'); + } + + if (!chatwoot_token) { + throw new BadRequestException('token is required'); + } + + if (!chatwoot_url) { + throw new BadRequestException('url is required'); + } + + if (!isURL(chatwoot_url, { require_tld: false })) { + throw new BadRequestException('Invalid "url" property in chatwoot'); + } + + const urlServer = this.configService.get('SERVER').URL; + + try { + this.chatwootService.create(instance, { + enabled: true, + account_id: chatwoot_account_id, + token: chatwoot_token, + url: chatwoot_url, + sign_msg: chatwoot_sign_msg || false, + name_inbox: instance.instanceName, + }); + + this.chatwootService.initInstanceChatwoot( + instance, + instance.instanceName, + `${urlServer}/chatwoot/webhook/${instance.instanceName}`, + qrcode, + ); + } catch (error) { + this.logger.log(error); + } + + return { + instance: { + instanceName: instance.instanceName, + status: 'created', + }, + hash, + webhook, + webhook_by_events, + events: getEvents, + chatwoot: { + enabled: true, + account_id: chatwoot_account_id, + token: chatwoot_token, + url: chatwoot_url, + sign_msg: chatwoot_sign_msg || false, + name_inbox: instance.instanceName, + webhook_url: `${urlServer}/chatwoot/webhook/${instance.instanceName}`, + }, + }; } public async connectToWhatsapp({ instanceName }: InstanceDto) { diff --git a/src/whatsapp/services/monitor.service.ts b/src/whatsapp/services/monitor.service.ts index 6aa43b70..6c31ad58 100644 --- a/src/whatsapp/services/monitor.service.ts +++ b/src/whatsapp/services/monitor.service.ts @@ -15,7 +15,6 @@ import { import { RepositoryBroker } from '../repository/repository.manager'; import { NotFoundException } from '../../exceptions'; import { Db } from 'mongodb'; -import { initInstance } from '../whatsapp.module'; import { RedisCache } from '../../db/redis.client'; import { execSync } from 'child_process'; import { dbserver } from '../../db/db.connect'; @@ -287,7 +286,6 @@ export class WAMonitoringService { keys.forEach(async (k) => await set(k.split(':')[1])); } else { this.logger.verbose('no instance keys found'); - initInstance(); } return; } @@ -303,7 +301,6 @@ export class WAMonitoringService { ); } else { this.logger.verbose('no collections found'); - initInstance(); } return; } @@ -324,7 +321,6 @@ export class WAMonitoringService { await set(dirent.name); } else { this.logger.verbose('no instance files found'); - initInstance(); } } } catch (error) { diff --git a/src/whatsapp/services/whatsapp.service.ts b/src/whatsapp/services/whatsapp.service.ts index 549dd7d6..3851f067 100644 --- a/src/whatsapp/services/whatsapp.service.ts +++ b/src/whatsapp/services/whatsapp.service.ts @@ -346,7 +346,6 @@ export class WAStartupService { const serverUrl = this.configService.get('SERVER').URL; const we = event.replace(/[\.-]/gm, '_').toUpperCase(); const transformedWe = we.replace(/_/gm, '-').toLowerCase(); - const instance = this.configService.get('AUTHENTICATION').INSTANCE; const expose = this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES; @@ -355,7 +354,7 @@ export class WAStartupService { const globalApiKey = this.configService.get('AUTHENTICATION').API_KEY.KEY; - if (local && instance.MODE !== 'container') { + if (local) { if (Array.isArray(webhookLocal) && webhookLocal.includes(we)) { this.logger.verbose('Sending data to webhook local'); let baseURL; @@ -432,13 +431,7 @@ export class WAStartupService { globalURL = globalWebhook.URL; } - let localUrl; - - if (instance.MODE === 'container') { - localUrl = instance.WEBHOOK_URL; - } else { - localUrl = this.localWebhook.url; - } + const localUrl = this.localWebhook.url; if (this.configService.get('LOG').LEVEL.includes('WEBHOOKS')) { const logData = { diff --git a/src/whatsapp/whatsapp.module.ts b/src/whatsapp/whatsapp.module.ts index dbc80427..c91ee9c4 100644 --- a/src/whatsapp/whatsapp.module.ts +++ b/src/whatsapp/whatsapp.module.ts @@ -91,111 +91,4 @@ export const sendMessageController = new SendMessageController(waMonitor); export const chatController = new ChatController(waMonitor); export const groupController = new GroupController(waMonitor); -export async function initInstance() { - const instance = new WAStartupService(configService, eventEmitter, repository, cache); - - const mode = configService.get('AUTHENTICATION').INSTANCE.MODE; - - logger.verbose('Sending data webhook for event: ' + Events.APPLICATION_STARTUP); - instance.sendDataWebhook( - Events.APPLICATION_STARTUP, - { - message: 'Application startup', - mode, - }, - false, - ); - - if (mode === 'container') { - logger.verbose('Application startup in container mode'); - - const instanceName = configService.get('AUTHENTICATION').INSTANCE.NAME; - logger.verbose('Instance name: ' + instanceName); - - const instanceWebhook = - configService.get('AUTHENTICATION').INSTANCE.WEBHOOK_URL; - logger.verbose('Instance webhook: ' + instanceWebhook); - - // const chatwootAccountId = - // configService.get('AUTHENTICATION').INSTANCE.CHATWOOT_ACCOUNT_ID; - // logger.verbose('Chatwoot account id: ' + chatwootAccountId); - - // const chatwootToken = - // configService.get('AUTHENTICATION').INSTANCE.CHATWOOT_TOKEN; - // logger.verbose('Chatwoot token: ' + chatwootToken); - - // const chatwootUrl = configService.get('AUTHENTICATION').INSTANCE.CHATWOOT_URL; - // logger.verbose('Chatwoot url: ' + chatwootUrl); - - instance.instanceName = instanceName; - - waMonitor.waInstances[instance.instanceName] = instance; - waMonitor.delInstanceTime(instance.instanceName); - - const hash = await authService.generateHash({ - instanceName: instance.instanceName, - token: configService.get('AUTHENTICATION').API_KEY.KEY, - }); - logger.verbose('Hash generated: ' + hash); - - if (instanceWebhook) { - logger.verbose('Creating webhook for instance: ' + instanceName); - try { - webhookService.create(instance, { enabled: true, url: instanceWebhook }); - logger.verbose('Webhook created'); - } catch (error) { - logger.log(error); - } - } - - // if (chatwootUrl && chatwootToken && chatwootAccountId) { - // logger.verbose('Creating chatwoot for instance: ' + instanceName); - // try { - // chatwootService.create(instance, { - // enabled: true, - // url: chatwootUrl, - // token: chatwootToken, - // account_id: chatwootAccountId, - // sign_msg: false, - // }); - // logger.verbose('Chatwoot created'); - // } catch (error) { - // logger.log(error); - // } - // } - - try { - const state = instance.connectionStatus?.state; - - switch (state) { - case 'close': - await instance.connectToWhatsapp(); - await delay(2000); - return instance.qrCode; - case 'connecting': - return instance.qrCode; - default: - return await this.connectionState({ instanceName }); - } - } catch (error) { - logger.log(error); - } - - const result = { - instance: { - instanceName: instance.instanceName, - status: 'created', - }, - hash, - webhook: instanceWebhook, - }; - - logger.info(result); - - return result; - } - - return null; -} - logger.info('Module - ON'); From f847f38812fe4d5de3be8681557ebf2196bd6d25 Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 15:13:49 -0300 Subject: [PATCH 7/9] fix: added docker-compose files example --- Docker/.env.example | 24 ++--- Dockerfile | 4 +- ...ompose.yaml => docker-compose.yaml.example | 2 + docker-compose.yaml.example.complete | 88 +++++++++++++++++++ 4 files changed, 104 insertions(+), 14 deletions(-) rename docker-compose.yaml => docker-compose.yaml.example (92%) create mode 100644 docker-compose.yaml.example.complete diff --git a/Docker/.env.example b/Docker/.env.example index a3782d4e..c3dbe505 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -1,13 +1,13 @@ # Server URL - Set your application url -SERVER_URL='http://localhost:8080' +SERVER_URL=http://localhost:8080 # Cors - * for all or set separate by commas - ex.: 'yourdomain1.com, yourdomain2.com' -CORS_ORIGIN='*' -CORS_METHODS='POST,GET,PUT,DELETE' +CORS_ORIGIN=* +CORS_METHODS=POST,GET,PUT,DELETE CORS_CREDENTIALS=true # Determine the logs to be displayed -LOG_LEVEL='ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS' +LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS LOG_COLOR=true # Log Baileys - "fatal" | "error" | "warn" | "info" | "debug" | "trace" LOG_BAILEYS=error @@ -31,9 +31,9 @@ CLEAN_STORE_CONTACTS=true CLEAN_STORE_CHATS=true # Permanent data storage -DATABASE_ENABLED=false +DATABASE_ENABLED=true DATABASE_CONNECTION_URI=mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true -DATABASE_CONNECTION_DB_PREFIX_NAME=evolution +DATABASE_CONNECTION_DB_PREFIX_NAME=evdocker # Choose the data you want to save in the application's database or store DATABASE_SAVE_DATA_INSTANCE=false @@ -42,9 +42,9 @@ DATABASE_SAVE_MESSAGE_UPDATE=false DATABASE_SAVE_DATA_CONTACTS=false DATABASE_SAVE_DATA_CHATS=false -REDIS_ENABLED=false +REDIS_ENABLED=true REDIS_URI=redis://redis:6379 -REDIS_PREFIX_KEY=evolution +REDIS_PREFIX_KEY=evdocker # Global Webhook Settings # Each instance's Webhook URL and events will be requested at the time it is created @@ -77,7 +77,7 @@ WEBHOOK_EVENTS_CONNECTION_UPDATE=true WEBHOOK_EVENTS_NEW_JWT_TOKEN=false # Name that will be displayed on smartphone connection -CONFIG_SESSION_PHONE_CLIENT='Evolution API' +CONFIG_SESSION_PHONE_CLIENT=EvolutionAPI # Browser Name = chrome | firefox | edge | opera | safari CONFIG_SESSION_PHONE_NAME=chrome @@ -88,12 +88,12 @@ QRCODE_LIMIT=30 # We recommend using the apikey because it will allow you to use a custom token, # if you use jwt, a random token will be generated and may be expired and you will have to generate a new token # jwt or 'apikey' -AUTHENTICATION_TYPE='apikey' +AUTHENTICATION_TYPE=apikey ## Define a global apikey to access all instances. ### OBS: This key must be inserted in the request header to create an instance. -AUTHENTICATION_API_KEY='B6D711FCDE4D4FD5936544120E713976' +AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976 AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true ## Set the secret key to encrypt and decrypt your token and its expiration time # seconds - 3600s ===1h | zero (0) - never expires AUTHENTICATION_JWT_EXPIRIN_IN=0 -AUTHENTICATION_JWT_SECRET='L0YWtjb2w554WFqPG' +AUTHENTICATION_JWT_SECRET='L=0YWt]b2w[WF>#>:&E`' diff --git a/Dockerfile b/Dockerfile index 088f6fa1..93fa60c4 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ COPY ./package.json . ENV DOCKER_ENV=true -ENV SERVER_URL='http://localhost:8080' +ENV SERVER_URL=http://localhost:8080 ENV CORS_ORIGIN=* ENV CORS_METHODS=POST,GET,PUT,DELETE @@ -77,7 +77,7 @@ ENV WEBHOOK_EVENTS_CONNECTION_UPDATE=true ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=false -ENV CONFIG_SESSION_PHONE_CLIENT='Evolution API' +ENV CONFIG_SESSION_PHONE_CLIENT=EvolutionAPI ENV CONFIG_SESSION_PHONE_NAME=chrome ENV QRCODE_LIMIT=30 diff --git a/docker-compose.yaml b/docker-compose.yaml.example similarity index 92% rename from docker-compose.yaml rename to docker-compose.yaml.example index c6d1bc73..93e04cc0 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml.example @@ -10,6 +10,8 @@ services: volumes: - evolution_instances:/evolution/instances - evolution_store:/evolution/store + networks: + - evolution-net env_file: - ./Docker/.env command: ['node', './dist/src/main.js'] diff --git a/docker-compose.yaml.example.complete b/docker-compose.yaml.example.complete new file mode 100644 index 00000000..23f316c8 --- /dev/null +++ b/docker-compose.yaml.example.complete @@ -0,0 +1,88 @@ +version: '3.3' + +services: + api: + container_name: evolution_api + image: evolution/api:local + restart: always + ports: + - 8080:8080 + volumes: + - evolution_instances:/evolution/instances + - evolution_store:/evolution/store + networks: + - evolution-net + env_file: + - ./Docker/.env + command: ['node', './dist/src/main.js'] + expose: + - 8080 + + mongodb: + container_name: mongodb + image: mongo + restart: always + ports: + - 27017:27017 + environment: + - MONGO_INITDB_ROOT_USERNAME=root + - MONGO_INITDB_ROOT_PASSWORD=root + - PUID=1000 + - PGID=1000 + volumes: + - evolution_mongodb_data:/data/db + - evolution_mongodb_configdb:/data/configdb + networks: + - evolution-net + expose: + - 27017 + + mongo-express: + image: mongo-express + networks: + - evolution-net + environment: + ME_CONFIG_BASICAUTH_USERNAME: root + ME_CONFIG_BASICAUTH_PASSWORD: root + ME_CONFIG_MONGODB_SERVER: mongodb + ME_CONFIG_MONGODB_ADMINUSERNAME: root + ME_CONFIG_MONGODB_ADMINPASSWORD: root + ports: + - 8081:8081 + links: + - mongodb + + redis: + image: redis:latest + container_name: redis + command: > + redis-server + --port 6379 + --appendonly yes + volumes: + - evolution_redis:/data + networks: + - evolution-net + ports: + - 6379:6379 + + rebrow: + image: marian/rebrow + networks: + - evolution-net + ports: + - 5001:5001 + links: + - redis + +volumes: + evolution_instances: + evolution_store: + evolution_mongodb_data: + evolution_mongodb_configdb: + evolution_redis: + +networks: + evolution-net: + external: true + \ No newline at end of file From b2ccf965bb75e6933df5e870c7fd3e3d2b38f60a Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 15:14:46 -0300 Subject: [PATCH 8/9] fix: added docker-compose files example --- .gitignore | 2 ++ CHANGELOG.md | 1 + 2 files changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index e707b33e..69d60b7a 100644 --- a/.gitignore +++ b/.gitignore @@ -16,6 +16,8 @@ lerna-debug.log* /docker-compose-data /docker-data +docker-compose.yaml + # Package /yarn.lock /package-lock.json diff --git a/CHANGELOG.md b/CHANGELOG.md index 2a864fe5..5da5cdb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ * Now when deleting the instance, the data referring to it in mongodb is also deleted * It is now validated if the instance name contains uppercase and special characters * For compatibility reasons, container mode has been removed +* Added docker-compose files example # 1.3.1 (2023-07-20 07:48) From 683fe4c3db06c6899cdb0b6bad235d91ea07612b Mon Sep 17 00:00:00 2001 From: Davidson Gomes Date: Fri, 21 Jul 2023 16:00:58 -0300 Subject: [PATCH 9/9] fix: added docker-compose files example --- Docker/redis/docker-compose.yaml | 7 ------- docker-compose.yaml.example.complete | 9 --------- 2 files changed, 16 deletions(-) diff --git a/Docker/redis/docker-compose.yaml b/Docker/redis/docker-compose.yaml index 6409b851..b0bb5985 100644 --- a/Docker/redis/docker-compose.yaml +++ b/Docker/redis/docker-compose.yaml @@ -12,13 +12,6 @@ services: - evolution_redis:/data ports: - 6379:6379 - - rebrow: - image: marian/rebrow - ports: - - 5001:5001 - links: - - redis volumes: evolution_redis: diff --git a/docker-compose.yaml.example.complete b/docker-compose.yaml.example.complete index 23f316c8..4e453b51 100644 --- a/docker-compose.yaml.example.complete +++ b/docker-compose.yaml.example.complete @@ -66,15 +66,6 @@ services: ports: - 6379:6379 - rebrow: - image: marian/rebrow - networks: - - evolution-net - ports: - - 5001:5001 - links: - - redis - volumes: evolution_instances: evolution_store: