diff --git a/.env.example b/.env.example new file mode 100644 index 00000000..32f4a764 --- /dev/null +++ b/.env.example @@ -0,0 +1,137 @@ +SERVER_TYPE=http +SERVER_PORT=8080 +SERVER_URL=http://localhost:8080 + +CORS_ORIGIN=* +CORS_METHODS=GET,POST,PUT,DELETE +CORS_CREDENTIALS=true + +LOG_LEVEL=ERROR,WARN,DEBUG,INFO,LOG,VERBOSE,DARK,WEBHOOKS +LOG_COLOR=true +LOG_BAILEYS=error + +DEL_INSTANCE=false +DEL_TEMP_INSTANCES=false + +PROVIDER_ENABLED=true +PROVIDER_HOST=127.0.0.1 +PROVIDER_PORT=5656 +PROVIDER_PREFIX=evolution + +STORE_MESSAGES=true +STORE_MESSAGE_UP=true +STORE_CONTACTS=true +STORE_CHATS=true + +CLEAN_STORE_CLEANING_INTERVAL=7200 +CLEAN_STORE_MESSAGES=true +CLEAN_STORE_MESSAGE_UP=true +CLEAN_STORE_CONTACTS=true +CLEAN_STORE_CHATS=true + +DATABASE_ENABLED=false +DATABASE_PROVIDER=mongodb # postgresql, mysql, mongodb +DATABASE_CONNECTION_URI='mongodb://root:root@mongodb:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true' +DATABASE_CONNECTION_DB_PREFIX_NAME=evolution +DATABASE_SAVE_DATA_INSTANCE=true +DATABASE_SAVE_DATA_NEW_MESSAGE=true +DATABASE_SAVE_MESSAGE_UPDATE=true +DATABASE_SAVE_DATA_CONTACTS=true +DATABASE_SAVE_DATA_CHATS=true + +RABBITMQ_ENABLED=false +RABBITMQ_URI=amqp://localhost +RABBITMQ_EXCHANGE_NAME=evolution +RABBITMQ_GLOBAL_ENABLED=false +RABBITMQ_EVENTS_APPLICATION_STARTUP=false +RABBITMQ_EVENTS_INSTANCE_CREATE=false +RABBITMQ_EVENTS_INSTANCE_DELETE=false +RABBITMQ_EVENTS_QRCODE_UPDATED=false +RABBITMQ_EVENTS_MESSAGES_SET=false +RABBITMQ_EVENTS_MESSAGES_UPSERT=false +RABBITMQ_EVENTS_MESSAGES_UPDATE=false +RABBITMQ_EVENTS_MESSAGES_DELETE=false +RABBITMQ_EVENTS_SEND_MESSAGE=false +RABBITMQ_EVENTS_CONTACTS_SET=false +RABBITMQ_EVENTS_CONTACTS_UPSERT=false +RABBITMQ_EVENTS_CONTACTS_UPDATE=false +RABBITMQ_EVENTS_PRESENCE_UPDATE=false +RABBITMQ_EVENTS_CHATS_SET=false +RABBITMQ_EVENTS_CHATS_UPSERT=false +RABBITMQ_EVENTS_CHATS_UPDATE=false +RABBITMQ_EVENTS_CHATS_DELETE=false +RABBITMQ_EVENTS_GROUPS_UPSERT=false +RABBITMQ_EVENTS_GROUP_UPDATE=false +RABBITMQ_EVENTS_GROUP_PARTICIPANTS_UPDATE=false +RABBITMQ_EVENTS_CONNECTION_UPDATE=false +RABBITMQ_EVENTS_CALL=false +RABBITMQ_EVENTS_TYPEBOT_START=false +RABBITMQ_EVENTS_TYPEBOT_CHANGE_STATUS=false + +SQS_ENABLED=false +SQS_ACCESS_KEY_ID= +SQS_SECRET_ACCESS_KEY= +SQS_ACCOUNT_ID= +SQS_REGION= + +WEBSOCKET_ENABLED=false +WEBSOCKET_GLOBAL_EVENTS=false + +WA_BUSINESS_TOKEN_WEBHOOK=evolution +WA_BUSINESS_URL=https://graph.facebook.com +WA_BUSINESS_VERSION=v18.0 +WA_BUSINESS_LANGUAGE=pt_BR + +WEBHOOK_GLOBAL_URL='' +WEBHOOK_GLOBAL_ENABLED=false +WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS=false +WEBHOOK_EVENTS_APPLICATION_STARTUP=false +WEBHOOK_EVENTS_QRCODE_UPDATED=true +WEBHOOK_EVENTS_MESSAGES_SET=true +WEBHOOK_EVENTS_MESSAGES_UPSERT=true +WEBHOOK_EVENTS_MESSAGES_UPDATE=true +WEBHOOK_EVENTS_MESSAGES_DELETE=true +WEBHOOK_EVENTS_SEND_MESSAGE=true +WEBHOOK_EVENTS_CONTACTS_SET=true +WEBHOOK_EVENTS_CONTACTS_UPSERT=true +WEBHOOK_EVENTS_CONTACTS_UPDATE=true +WEBHOOK_EVENTS_PRESENCE_UPDATE=true +WEBHOOK_EVENTS_CHATS_SET=true +WEBHOOK_EVENTS_CHATS_UPSERT=true +WEBHOOK_EVENTS_CHATS_UPDATE=true +WEBHOOK_EVENTS_CHATS_DELETE=true +WEBHOOK_EVENTS_GROUPS_UPSERT=true +WEBHOOK_EVENTS_GROUPS_UPDATE=true +WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true +WEBHOOK_EVENTS_CONNECTION_UPDATE=true +WEBHOOK_EVENTS_LABELS_EDIT=true +WEBHOOK_EVENTS_LABELS_ASSOCIATION=true +WEBHOOK_EVENTS_CALL=true +WEBHOOK_EVENTS_TYPEBOT_START=false +WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS=false +WEBHOOK_EVENTS_ERRORS=false +WEBHOOK_EVENTS_ERRORS_WEBHOOK= + +CONFIG_SESSION_PHONE_CLIENT=Evolution API +CONFIG_SESSION_PHONE_NAME=Chrome +CONFIG_SESSION_PHONE_VERSION='2,2413,1' + +QRCODE_LIMIT=30 +QRCODE_COLOR='#175197' + +TYPEBOT_API_VERSION=latest + +CHATWOOT_MESSAGE_DELETE=false +CHATWOOT_MESSAGE_READ=false +CHATWOOT_IMPORT_DATABASE_CONNECTION_URI=postgresql://user:pass@host:5432/dbname +CHATWOOT_IMPORT_PLACEHOLDER_MEDIA_MESSAGE=false + +CACHE_REDIS_ENABLED=true +CACHE_REDIS_URI=redis://localhost:6379/6 +CACHE_REDIS_PREFIX_KEY=evolution +CACHE_REDIS_SAVE_INSTANCES=false +CACHE_LOCAL_ENABLED=true + +AUTHENTICATION_API_KEY=429683C4C977415CAAFCCE10F7D57E11 +AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true +LANGUAGE=en diff --git a/Docker/.env.example b/Docker/.env.example index 1af5e1cd..4d1c7736 100644 --- a/Docker/.env.example +++ b/Docker/.env.example @@ -116,13 +116,9 @@ WEBHOOK_EVENTS_CONNECTION_UPDATE=true WEBHOOK_EVENTS_LABELS_EDIT=true WEBHOOK_EVENTS_LABELS_ASSOCIATION=true WEBHOOK_EVENTS_CALL=true -# This event fires every time a new token is requested via the refresh route -WEBHOOK_EVENTS_NEW_JWT_TOKEN=false # This events is used with Typebot WEBHOOK_EVENTS_TYPEBOT_START=false WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS=false -# This event is used with Chama AI -WEBHOOK_EVENTS_CHAMA_AI_ACTION=false # This event is used to send errors WEBHOOK_EVENTS_ERRORS=false WEBHOOK_EVENTS_ERRORS_WEBHOOK= @@ -157,18 +153,9 @@ CACHE_REDIS_SAVE_INSTANCES=false CACHE_LOCAL_ENABLED=false CACHE_LOCAL_TTL=604800 -# Defines an authentication type for the api -# 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 ## 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_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='L=0YWt]b2w[WF>#>:&E`' LANGUAGE=en # pt-BR, en diff --git a/Docker/evolution-api-all-services/.env.example b/Docker/evolution-api-all-services/.env.example index d08fee49..82aaffde 100644 --- a/Docker/evolution-api-all-services/.env.example +++ b/Docker/evolution-api-all-services/.env.example @@ -75,8 +75,6 @@ WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE=true WEBHOOK_EVENTS_CONNECTION_UPDATE=true WEBHOOK_EVENTS_LABELS_EDIT=true WEBHOOK_EVENTS_LABELS_ASSOCIATION=true -# This event fires every time a new token is requested via the refresh route -WEBHOOK_EVENTS_NEW_JWT_TOKEN=false # Name that will be displayed on smartphone connection CONFIG_SESSION_PHONE_CLIENT='Evolution API' @@ -94,26 +92,9 @@ CACHE_REDIS_SAVE_INSTANCES=false CACHE_LOCAL_ENABLED=false CACHE_LOCAL_TTL=604800 -# Defines an authentication type for the api -# 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' ## 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_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' # 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/Dockerfile b/Dockerfile index 9db93aeb..605785eb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -133,13 +133,9 @@ ENV WEBHOOK_EVENTS_LABELS_EDIT=true ENV WEBHOOK_EVENTS_LABELS_ASSOCIATION=true ENV WEBHOOK_EVENTS_CALL=true -ENV WEBHOOK_EVENTS_NEW_JWT_TOKEN=false - ENV WEBHOOK_EVENTS_TYPEBOT_START=false ENV WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS=false -ENV WEBHOOK_EVENTS_CHAMA_AI_ACTION=false - ENV WEBHOOK_EVENTS_ERRORS=false ENV WEBHOOK_EVENTS_ERRORS_WEBHOOK= @@ -164,9 +160,6 @@ ENV AUTHENTICATION_TYPE=apikey ENV AUTHENTICATION_API_KEY=B6D711FCDE4D4FD5936544120E713976 ENV AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES=true -ENV AUTHENTICATION_JWT_EXPIRIN_IN=0 -ENV AUTHENTICATION_JWT_SECRET='L=0YWt]b2w[WF>#>:&E`' - ENV AUTHENTICATION_INSTANCE_MODE=server ENV AUTHENTICATION_INSTANCE_NAME=evolution diff --git a/package.json b/package.json index bcd187d5..2433ea8f 100644 --- a/package.json +++ b/package.json @@ -42,19 +42,21 @@ "homepage": "https://github.com/EvolutionAPI/evolution-api#readme", "dependencies": { "@adiwajshing/keyed-db": "^0.2.4", + "@aws-sdk/client-sqs": "^3.569.0", "@ffmpeg-installer/ffmpeg": "^1.1.0", "@figuro/chatwoot-sdk": "^1.1.16", "@hapi/boom": "^10.0.1", + "@prisma/client": "^5.15.0", "@sentry/node": "^7.59.2", - "amqplib": "^0.10.3", - "@aws-sdk/client-sqs": "^3.569.0", - "axios": "^1.6.5", "@whiskeysockets/baileys": "6.7.4", + "amqplib": "^0.10.3", + "axios": "^1.6.5", "class-validator": "^0.14.1", "compression": "^1.7.4", "cors": "^2.8.5", "cross-env": "^7.0.3", "dayjs": "^1.11.7", + "dotenv": "^16.4.5", "eventemitter2": "^6.4.9", "evolution-manager": "^0.4.13", "exiftool-vendored": "^22.0.0", @@ -85,7 +87,6 @@ "sharp": "^0.32.2", "socket.io": "^4.7.1", "socks-proxy-agent": "^8.0.1", - "swagger-ui-express": "^5.0.0", "uuid": "^9.0.0", "xml2js": "^0.6.2", "yamljs": "^0.3.0" diff --git a/prisma/schema.prisma b/prisma/schema.prisma new file mode 100644 index 00000000..e4937bdf --- /dev/null +++ b/prisma/schema.prisma @@ -0,0 +1,285 @@ +// This is your Prisma schema file, +// learn more about it in the docs: https://pris.ly/d/prisma-schema + +// Looking for ways to speed up your queries, or scale easily with your serverless or edge functions? +// Try Prisma Accelerate: https://pris.ly/cli/accelerate-init + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_CONNECTION_URI") +} + +enum InstanceConnectionStatus { + open + close + connecting +} + +enum DeviceMessage { + ios + android + web + unknown + desktop +} + +enum TypebotSessionStatus { + open + closed + paused +} + +model Instance { + id Int @id @default(autoincrement()) + name String @unique @db.VarChar(255) + description String? @db.VarChar(255) + connectionStatus InstanceConnectionStatus @default(open) + ownerJid String? @db.VarChar(100) + profilePicUrl String? @db.VarChar(500) + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime? @updatedAt @db.Date + Auth Auth? + Chat Chat[] + Contact Contact[] + Message Message[] + Webhook Webhook? + Chatwoot Chatwoot? + Integration Integration? + Label Label[] + Proxy Proxy? + Setting Setting? + Rabbitmq Rabbitmq? + Sqs Sqs? + Websocket Websocket? + Typebot Typebot? + Session Session? +} + +model Session { + id Int @id @unique @default(autoincrement()) + sessionId String @unique @default(cuid()) + creds String? @db.Text + createdAt DateTime @default(now()) + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique @db.Integer + + @@map("sessions") +} + +model Auth { + id Int @id @default(autoincrement()) + apiKey String @unique + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime? @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique @db.Integer +} + +model Chat { + id Int @id @default(autoincrement()) + lastMsgTimestamp DateTime? @db.Timestamp + labels Json? @db.JsonB + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime? @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int +} + +model Contact { + id Int @id @default(autoincrement()) + remoteJid String @db.VarChar(100) + pushName String? @db.VarChar(100) + profilePicUrl String? @db.VarChar(500) + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime? @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int +} + +model Message { + id Int @id @default(autoincrement()) + keyId String @db.VarChar(100) + keyRemoteJid String @db.VarChar(100) + keyFromMe Boolean @db.Boolean + keyParticipant String? @db.VarChar(100) + pushName String? @db.VarChar(100) + participant String? @db.VarChar(100) + messageType String @db.VarChar(100) + message Json @db.JsonB + source DeviceMessage + messageTimestamp Int @db.Integer + chatwootMessageId Int? @db.Integer + chatwootInboxId Int? @db.Integer + chatwootConversationId Int? @db.Integer + chatwootContactInboxSourceId String? @db.VarChar(100) + chatwotIsRead Boolean? @db.Boolean + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int + typebotSessionId Int? + MessageUpdate MessageUpdate[] + TypebotSession TypebotSession? @relation(fields: [typebotSessionId], references: [id]) + + @@index([keyId], name: "keyId") +} + +model MessageUpdate { + id Int @id @default(autoincrement()) + remoteJid String @db.VarChar(100) + fromMe Boolean @db.Boolean + participant String? @db.VarChar(100) + dateTime DateTime @db.Date + status String @db.VarChar(30) + Message Message @relation(fields: [messageId], references: [id], onDelete: Cascade) + messageId Int +} + +model Webhook { + id Int @id @default(autoincrement()) + url String @db.VarChar(500) + enabled Boolean? @default(true) @db.Boolean + events Json? @db.JsonB + webhook_by_events Boolean? @default(false) @db.Boolean + webhook_base64 Boolean? @default(false) @db.Boolean + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique +} + +model Chatwoot { + id Int @id @default(autoincrement()) + enabled Boolean? @default(true) @db.Boolean + account_id String? @db.VarChar(100) + token String? @db.VarChar(100) + url String? @db.VarChar(500) + name_inbox String? @db.VarChar(100) + sign_msg Boolean? @default(false) @db.Boolean + sign_delimiter String? @db.VarChar(100) + number String? @db.VarChar(100) + reopen_conversation Boolean? @default(false) @db.Boolean + conversation_pending Boolean? @default(false) @db.Boolean + merge_brazil_contacts Boolean? @default(false) @db.Boolean + import_contacts Boolean? @default(false) @db.Boolean + import_messages Boolean? @default(false) @db.Boolean + days_limit_import_messages Int? @db.Integer + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique +} + +model Integration { + id Int @id @default(autoincrement()) + integration String @db.VarChar(100) + number String? @db.VarChar(100) + token String? @db.VarChar(100) + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique +} + +model Label { + id Int @id @default(autoincrement()) + labelId String? @unique @db.VarChar(100) + name String @db.VarChar(100) + color String @db.VarChar(100) + predefinedId String? @db.VarChar(100) + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int +} + +model Proxy { + id Int @id @default(autoincrement()) + enabled Boolean? @default(true) @db.Boolean + proxyHost String? @db.VarChar(100) + proxyPort Int? @db.Integer + proxyUsername String? @db.VarChar(100) + proxyPassword String? @db.VarChar(100) + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique +} + +model Setting { + id Int @id @default(autoincrement()) + rejectCall Boolean? @default(false) @db.Boolean + msgCall String? @db.VarChar(100) + groupsIgnore Boolean? @default(false) @db.Boolean + alwaysOnline Boolean? @default(false) @db.Boolean + readMessages Boolean? @default(false) @db.Boolean + readStatus Boolean? @default(false) @db.Boolean + syncFullHistory Boolean? @default(false) @db.Boolean + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique +} + +model Rabbitmq { + id Int @id @default(autoincrement()) + enabled Boolean? @default(false) @db.Boolean + events Json? @db.JsonB + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique +} + +model Sqs { + id Int @id @default(autoincrement()) + enabled Boolean? @default(false) @db.Boolean + events Json? @db.JsonB + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique +} + +model Websocket { + id Int @id @default(autoincrement()) + enabled Boolean? @default(false) @db.Boolean + events Json? @db.JsonB + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique +} + +model Typebot { + id Int @id @default(autoincrement()) + enabled Boolean? @default(true) @db.Boolean + url String @db.VarChar(500) + typebot String @db.VarChar(100) + expire Int? @db.Integer + keywordFinish String? @db.VarChar(100) + delayMessage Int? @db.Integer + unknownMessage String? @db.VarChar(100) + listeningFromMe Boolean? @default(false) @db.Boolean + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime? @updatedAt @db.Date + Instance Instance @relation(fields: [instanceId], references: [id], onDelete: Cascade) + instanceId Int @unique + sessions TypebotSession[] +} + +model TypebotSession { + id Int @id @default(autoincrement()) + remoteJid String @db.VarChar(100) + pushName String? @db.VarChar(100) + sessionId String @db.VarChar(100) + status String @db.VarChar(100) + prefilledVariables Json? @db.JsonB + createdAt DateTime? @default(now()) @db.Date + updatedAt DateTime @updatedAt @db.Date + Typebot Typebot @relation(fields: [typebotId], references: [id], onDelete: Cascade) + typebotId Int + Message Message[] +} diff --git a/src/api/controllers/chat.controller.ts b/src/api/controllers/chat.controller.ts index 9d22a8f0..c0a87e2e 100644 --- a/src/api/controllers/chat.controller.ts +++ b/src/api/controllers/chat.controller.ts @@ -16,9 +16,9 @@ import { WhatsAppNumberDto, } from '../dto/chat.dto'; import { InstanceDto } from '../dto/instance.dto'; -import { ContactQuery } from '../repository/contact.repository'; -import { MessageQuery } from '../repository/message.repository'; -import { MessageUpQuery } from '../repository/messageUp.repository'; +import { ContactQuery } from '../repository/mongodb/contact.repository'; +import { MessageQuery } from '../repository/mongodb/message.repository'; +import { MessageUpQuery } from '../repository/mongodb/messageUp.repository'; import { WAMonitoringService } from '../services/monitor.service'; const logger = new Logger('ChatController'); diff --git a/src/api/controllers/instance.controller.ts b/src/api/controllers/instance.controller.ts index bc3ace61..1a2700e7 100644 --- a/src/api/controllers/instance.controller.ts +++ b/src/api/controllers/instance.controller.ts @@ -13,8 +13,9 @@ import { SqsService } from '../integrations/sqs/services/sqs.service'; import { TypebotService } from '../integrations/typebot/services/typebot.service'; import { WebsocketService } from '../integrations/websocket/services/websocket.service'; import { ProviderFiles } from '../provider/sessions'; -import { RepositoryBroker } from '../repository/repository.manager'; -import { AuthService, OldToken } from '../services/auth.service'; +import { MongodbRepository } from '../repository/mongodb/repository.manager'; +import { PrismaRepository } from '../repository/prisma/repository.service'; +import { AuthService } from '../services/auth.service'; import { CacheService } from '../services/cache.service'; import { BaileysStartupService } from '../services/channels/whatsapp.baileys.service'; import { BusinessStartupService } from '../services/channels/whatsapp.business.service'; @@ -29,7 +30,8 @@ export class InstanceController { constructor( private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService, - private readonly repository: RepositoryBroker, + private readonly mongodbRepository: MongodbRepository, + private readonly prismaRepository: PrismaRepository, private readonly eventEmitter: EventEmitter2, private readonly authService: AuthService, private readonly webhookService: WebhookService, @@ -109,7 +111,8 @@ export class InstanceController { instance = new BusinessStartupService( this.configService, this.eventEmitter, - this.repository, + this.mongodbRepository, + this.prismaRepository, this.cache, this.chatwootCache, this.baileysCache, @@ -119,7 +122,8 @@ export class InstanceController { instance = new BaileysStartupService( this.configService, this.eventEmitter, - this.repository, + this.mongodbRepository, + this.prismaRepository, this.cache, this.chatwootCache, this.baileysCache, @@ -188,10 +192,8 @@ export class InstanceController { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ]; } else { newEvents = events; @@ -240,10 +242,8 @@ export class InstanceController { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ]; } else { newEvents = websocket_events; @@ -289,10 +289,8 @@ export class InstanceController { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ]; } else { newEvents = rabbitmq_events; @@ -338,10 +336,8 @@ export class InstanceController { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ]; } else { newEvents = sqs_events; @@ -690,7 +686,7 @@ export class InstanceController { let arrayReturn = false; if (env.KEY !== key) { - const instanceByKey = await this.repository.auth.findByKey(key); + const instanceByKey = await this.mongodbRepository.auth.findByKey(key); if (instanceByKey) { name = instanceByKey._id; arrayReturn = true; @@ -755,7 +751,7 @@ export class InstanceController { try { this.waMonitor.waInstances[instanceName]?.sendDataWebhook(Events.INSTANCE_DELETE, { instanceName, - instanceId: (await this.repository.auth.find(instanceName))?.instanceId, + instanceId: (await this.mongodbRepository.auth.find(instanceName))?.instanceId, }); } catch (error) { this.logger.error(error); @@ -768,9 +764,4 @@ export class InstanceController { throw new BadRequestException(error.toString()); } } - - public async refreshToken(_: InstanceDto, oldToken: OldToken) { - this.logger.verbose('requested refreshToken'); - return await this.authService.refreshToken(oldToken); - } } diff --git a/src/api/controllers/webhook.controller.ts b/src/api/controllers/webhook.controller.ts index 660d9235..6285cb6c 100644 --- a/src/api/controllers/webhook.controller.ts +++ b/src/api/controllers/webhook.controller.ts @@ -50,10 +50,8 @@ export class WebhookController { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ]; } diff --git a/src/api/guards/auth.guard.ts b/src/api/guards/auth.guard.ts index 4ed0898b..e236b563 100644 --- a/src/api/guards/auth.guard.ts +++ b/src/api/guards/auth.guard.ts @@ -1,60 +1,13 @@ -import { isJWT } from 'class-validator'; import { NextFunction, Request, Response } from 'express'; -import jwt from 'jsonwebtoken'; -import { name } from '../../../package.json'; import { Auth, configService, Database } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { ForbiddenException, UnauthorizedException } from '../../exceptions'; import { InstanceDto } from '../dto/instance.dto'; -import { repository } from '../server.module'; -import { JwtPayload } from '../services/auth.service'; +import { mongodbRepository } from '../server.module'; const logger = new Logger('GUARD'); -async function jwtGuard(req: Request, res: Response, next: NextFunction) { - const key = req.get('apikey'); - - if (key && configService.get('AUTHENTICATION').API_KEY.KEY !== key) { - throw new UnauthorizedException(); - } - - if (configService.get('AUTHENTICATION').API_KEY.KEY === key) { - return next(); - } - - if ((req.originalUrl.includes('/instance/create') || req.originalUrl.includes('/instance/fetchInstances')) && !key) { - throw new ForbiddenException('Missing global api key', 'The global api key must be set'); - } - - const jwtOpts = configService.get('AUTHENTICATION').JWT; - try { - const [bearer, token] = req.get('authorization').split(' '); - - if (bearer.toLowerCase() !== 'bearer') { - throw new UnauthorizedException(); - } - - if (!isJWT(token)) { - throw new UnauthorizedException(); - } - - const param = req.params as unknown as InstanceDto; - const decode = jwt.verify(token, jwtOpts.SECRET, { - ignoreExpiration: jwtOpts.EXPIRIN_IN === 0, - }) as JwtPayload; - - if (param.instanceName !== decode.instanceName || name !== decode.apiName) { - throw new UnauthorizedException(); - } - - return next(); - } catch (error) { - logger.error(error); - throw new UnauthorizedException(); - } -} - async function apikey(req: Request, _: Response, next: NextFunction) { const env = configService.get('AUTHENTICATION').API_KEY; const key = req.get('apikey'); @@ -75,13 +28,13 @@ async function apikey(req: Request, _: Response, next: NextFunction) { try { if (param?.instanceName) { - const instanceKey = await repository.auth.find(param.instanceName); + const instanceKey = await mongodbRepository.auth.find(param.instanceName); if (instanceKey?.apikey === key) { return next(); } } else { if (req.originalUrl.includes('/instance/fetchInstances') && db.ENABLED) { - const instanceByKey = await repository.auth.findByKey(key); + const instanceByKey = await mongodbRepository.auth.findByKey(key); if (instanceByKey) { return next(); } @@ -94,4 +47,4 @@ async function apikey(req: Request, _: Response, next: NextFunction) { throw new UnauthorizedException(); } -export const authGuard = { jwt: jwtGuard, apikey }; +export const authGuard = { apikey }; diff --git a/src/api/guards/instance.guard.ts b/src/api/guards/instance.guard.ts index 84cabb12..5902bd4f 100644 --- a/src/api/guards/instance.guard.ts +++ b/src/api/guards/instance.guard.ts @@ -10,7 +10,7 @@ import { InternalServerErrorException, NotFoundException, } from '../../exceptions'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; import { InstanceDto } from '../dto/instance.dto'; import { cache, waMonitor } from '../server.module'; @@ -28,7 +28,7 @@ async function getInstance(instanceName: string) { } if (db.ENABLED) { - const collection = dbserver + const collection = mongodbServer .getClient() .db(db.CONNECTION.DB_PREFIX_NAME + '-instances') .collection(instanceName); diff --git a/src/api/integrations/chamaai/controllers/chamaai.controller.ts b/src/api/integrations/chamaai/controllers/chamaai.controller.ts deleted file mode 100644 index 91b33cd1..00000000 --- a/src/api/integrations/chamaai/controllers/chamaai.controller.ts +++ /dev/null @@ -1,29 +0,0 @@ -import { Logger } from '../../../../config/logger.config'; -import { InstanceDto } from '../../../dto/instance.dto'; -import { ChamaaiDto } from '../dto/chamaai.dto'; -import { ChamaaiService } from '../services/chamaai.service'; - -const logger = new Logger('ChamaaiController'); - -export class ChamaaiController { - constructor(private readonly chamaaiService: ChamaaiService) {} - - public async createChamaai(instance: InstanceDto, data: ChamaaiDto) { - logger.verbose('requested createChamaai from ' + instance.instanceName + ' instance'); - - if (!data.enabled) { - logger.verbose('chamaai disabled'); - data.url = ''; - data.token = ''; - data.waNumber = ''; - data.answerByAudio = false; - } - - return this.chamaaiService.create(instance, data); - } - - public async findChamaai(instance: InstanceDto) { - logger.verbose('requested findChamaai from ' + instance.instanceName + ' instance'); - return this.chamaaiService.find(instance); - } -} diff --git a/src/api/integrations/chamaai/dto/chamaai.dto.ts b/src/api/integrations/chamaai/dto/chamaai.dto.ts deleted file mode 100644 index 2c71a07d..00000000 --- a/src/api/integrations/chamaai/dto/chamaai.dto.ts +++ /dev/null @@ -1,7 +0,0 @@ -export class ChamaaiDto { - enabled: boolean; - url: string; - token: string; - waNumber: string; - answerByAudio: boolean; -} diff --git a/src/api/integrations/chamaai/models/chamaai.model.ts b/src/api/integrations/chamaai/models/chamaai.model.ts deleted file mode 100644 index ef4f252b..00000000 --- a/src/api/integrations/chamaai/models/chamaai.model.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Schema } from 'mongoose'; - -import { dbserver } from '../../../../libs/db.connect'; - -export class ChamaaiRaw { - _id?: string; - enabled?: boolean; - url?: string; - token?: string; - waNumber?: string; - answerByAudio?: boolean; -} - -const chamaaiSchema = new Schema({ - _id: { type: String, _id: true }, - enabled: { type: Boolean, required: true }, - url: { type: String, required: true }, - token: { type: String, required: true }, - waNumber: { type: String, required: true }, - answerByAudio: { type: Boolean, required: true }, -}); - -export const ChamaaiModel = dbserver?.model(ChamaaiRaw.name, chamaaiSchema, 'chamaai'); -export type IChamaaiModel = typeof ChamaaiModel; diff --git a/src/api/integrations/chamaai/repository/chamaai.repository.ts b/src/api/integrations/chamaai/repository/chamaai.repository.ts deleted file mode 100644 index 17ff8dcd..00000000 --- a/src/api/integrations/chamaai/repository/chamaai.repository.ts +++ /dev/null @@ -1,62 +0,0 @@ -import { readFileSync } from 'fs'; -import { join } from 'path'; - -import { ConfigService } from '../../../../config/env.config'; -import { Logger } from '../../../../config/logger.config'; -import { IInsert, Repository } from '../../../abstract/abstract.repository'; -import { ChamaaiRaw, IChamaaiModel } from '../../../models'; - -export class ChamaaiRepository extends Repository { - constructor(private readonly chamaaiModel: IChamaaiModel, private readonly configService: ConfigService) { - super(configService); - } - - private readonly logger = new Logger('ChamaaiRepository'); - - public async create(data: ChamaaiRaw, instance: string): Promise { - try { - this.logger.verbose('creating chamaai'); - if (this.dbSettings.ENABLED) { - this.logger.verbose('saving chamaai to db'); - const insert = await this.chamaaiModel.replaceOne({ _id: instance }, { ...data }, { upsert: true }); - - this.logger.verbose('chamaai saved to db: ' + insert.modifiedCount + ' chamaai'); - return { insertCount: insert.modifiedCount }; - } - - this.logger.verbose('saving chamaai to store'); - - this.writeStore({ - path: join(this.storePath, 'chamaai'), - fileName: instance, - data, - }); - - this.logger.verbose('chamaai saved to store in path: ' + join(this.storePath, 'chamaai') + '/' + instance); - - this.logger.verbose('chamaai created'); - return { insertCount: 1 }; - } catch (error) { - return error; - } - } - - public async find(instance: string): Promise { - try { - this.logger.verbose('finding chamaai'); - if (this.dbSettings.ENABLED) { - this.logger.verbose('finding chamaai in db'); - return await this.chamaaiModel.findOne({ _id: instance }); - } - - this.logger.verbose('finding chamaai in store'); - return JSON.parse( - readFileSync(join(this.storePath, 'chamaai', instance + '.json'), { - encoding: 'utf-8', - }), - ) as ChamaaiRaw; - } catch (error) { - return {}; - } - } -} diff --git a/src/api/integrations/chamaai/routes/chamaai.router.ts b/src/api/integrations/chamaai/routes/chamaai.router.ts deleted file mode 100644 index 3ade4b43..00000000 --- a/src/api/integrations/chamaai/routes/chamaai.router.ts +++ /dev/null @@ -1,52 +0,0 @@ -import { RequestHandler, Router } from 'express'; - -import { Logger } from '../../../../config/logger.config'; -import { chamaaiSchema, instanceNameSchema } from '../../../../validate/validate.schema'; -import { RouterBroker } from '../../../abstract/abstract.router'; -import { InstanceDto } from '../../../dto/instance.dto'; -import { HttpStatus } from '../../../routes/index.router'; -import { chamaaiController } from '../../../server.module'; -import { ChamaaiDto } from '../dto/chamaai.dto'; - -const logger = new Logger('ChamaaiRouter'); - -export class ChamaaiRouter extends RouterBroker { - constructor(...guards: RequestHandler[]) { - super(); - this.router - .post(this.routerPath('set'), ...guards, async (req, res) => { - logger.verbose('request received in setChamaai'); - logger.verbose('request body: '); - logger.verbose(req.body); - - logger.verbose('request query: '); - logger.verbose(req.query); - const response = await this.dataValidate({ - request: req, - schema: chamaaiSchema, - ClassRef: ChamaaiDto, - execute: (instance, data) => chamaaiController.createChamaai(instance, data), - }); - - res.status(HttpStatus.CREATED).json(response); - }) - .get(this.routerPath('find'), ...guards, async (req, res) => { - logger.verbose('request received in findChamaai'); - logger.verbose('request body: '); - logger.verbose(req.body); - - logger.verbose('request query: '); - logger.verbose(req.query); - const response = await this.dataValidate({ - request: req, - schema: instanceNameSchema, - ClassRef: InstanceDto, - execute: (instance) => chamaaiController.findChamaai(instance), - }); - - res.status(HttpStatus.OK).json(response); - }); - } - - public readonly router = Router(); -} diff --git a/src/api/integrations/chamaai/services/chamaai.service.ts b/src/api/integrations/chamaai/services/chamaai.service.ts deleted file mode 100644 index 911732a8..00000000 --- a/src/api/integrations/chamaai/services/chamaai.service.ts +++ /dev/null @@ -1,230 +0,0 @@ -import axios from 'axios'; -import { writeFileSync } from 'fs'; -import path from 'path'; - -import { ConfigService, HttpServer } from '../../../../config/env.config'; -import { Logger } from '../../../../config/logger.config'; -import { InstanceDto } from '../../../dto/instance.dto'; -import { ChamaaiRaw } from '../../../models'; -import { WAMonitoringService } from '../../../services/monitor.service'; -import { Events } from '../../../types/wa.types'; -import { ChamaaiDto } from '../dto/chamaai.dto'; - -export class ChamaaiService { - constructor(private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService) {} - - private readonly logger = new Logger(ChamaaiService.name); - - public create(instance: InstanceDto, data: ChamaaiDto) { - this.logger.verbose('create chamaai: ' + instance.instanceName); - this.waMonitor.waInstances[instance.instanceName].setChamaai(data); - - return { chamaai: { ...instance, chamaai: data } }; - } - - public async find(instance: InstanceDto): Promise { - try { - this.logger.verbose('find chamaai: ' + instance.instanceName); - const result = await this.waMonitor.waInstances[instance.instanceName].findChamaai(); - - if (Object.keys(result).length === 0) { - throw new Error('Chamaai not found'); - } - - return result; - } catch (error) { - return { enabled: false, url: '', token: '', waNumber: '', answerByAudio: false }; - } - } - - private getTypeMessage(msg: any) { - this.logger.verbose('get type message'); - - const types = { - conversation: msg.conversation, - extendedTextMessage: msg.extendedTextMessage?.text, - }; - - this.logger.verbose('type message: ' + types); - - return types; - } - - private getMessageContent(types: any) { - this.logger.verbose('get message content'); - const typeKey = Object.keys(types).find((key) => types[key] !== undefined); - - const result = typeKey ? types[typeKey] : undefined; - - this.logger.verbose('message content: ' + result); - - return result; - } - - private getConversationMessage(msg: any) { - this.logger.verbose('get conversation message'); - - const types = this.getTypeMessage(msg); - - const messageContent = this.getMessageContent(types); - - this.logger.verbose('conversation message: ' + messageContent); - - return messageContent; - } - - private calculateTypingTime(text: string) { - const wordsPerMinute = 100; - - const wordCount = text.split(' ').length; - const typingTimeInMinutes = wordCount / wordsPerMinute; - const typingTimeInMilliseconds = typingTimeInMinutes * 60; - return typingTimeInMilliseconds; - } - - private convertToMilliseconds(count: number) { - const averageCharactersPerSecond = 15; - const characterCount = count; - const speakingTimeInSeconds = characterCount / averageCharactersPerSecond; - return speakingTimeInSeconds; - } - - private getRegexPatterns() { - const patternsToCheck = [ - '.*atend.*humano.*', - '.*falar.*com.*um.*humano.*', - '.*fala.*humano.*', - '.*atend.*humano.*', - '.*fala.*atend.*', - '.*preciso.*ajuda.*', - '.*quero.*suporte.*', - '.*preciso.*assiste.*', - '.*ajuda.*atend.*', - '.*chama.*atendente.*', - '.*suporte.*urgente.*', - '.*atend.*por.*favor.*', - '.*quero.*falar.*com.*alguém.*', - '.*falar.*com.*um.*humano.*', - '.*transfer.*humano.*', - '.*transfer.*atend.*', - '.*equipe.*humano.*', - '.*suporte.*humano.*', - ]; - - const regexPatterns = patternsToCheck.map((pattern) => new RegExp(pattern, 'iu')); - return regexPatterns; - } - - public async sendChamaai(instance: InstanceDto, remoteJid: string, msg: any) { - const content = this.getConversationMessage(msg.message); - const msgType = msg.messageType; - const find = await this.find(instance); - const url = find.url; - const token = find.token; - const waNumber = find.waNumber; - const answerByAudio = find.answerByAudio; - - if (!content && msgType !== 'audioMessage') { - return; - } - - let data; - let endpoint; - - if (msgType === 'audioMessage') { - const downloadBase64 = await this.waMonitor.waInstances[instance.instanceName].getBase64FromMediaMessage({ - message: { - ...msg, - }, - }); - - const random = Math.random().toString(36).substring(7); - const nameFile = `${random}.ogg`; - - const fileData = Buffer.from(downloadBase64.base64, 'base64'); - - const fileName = `${path.join( - this.waMonitor.waInstances[instance.instanceName].storePath, - 'temp', - `${nameFile}`, - )}`; - - writeFileSync(fileName, fileData, 'utf8'); - - const urlServer = this.configService.get('SERVER').URL; - - const url = `${urlServer}/store/temp/${nameFile}`; - - data = { - waNumber: waNumber, - audioUrl: url, - queryNumber: remoteJid.split('@')[0], - answerByAudio: answerByAudio, - }; - endpoint = 'processMessageAudio'; - } else { - data = { - waNumber: waNumber, - question: content, - queryNumber: remoteJid.split('@')[0], - answerByAudio: answerByAudio, - }; - endpoint = 'processMessageText'; - } - - const request = await axios.post(`${url}/${endpoint}`, data, { - headers: { - Authorization: `${token}`, - }, - }); - - const answer = request.data?.answer; - - const type = request.data?.type; - - const characterCount = request.data?.characterCount; - - if (answer) { - if (type === 'text') { - this.waMonitor.waInstances[instance.instanceName].textMessage({ - number: remoteJid.split('@')[0], - options: { - delay: this.calculateTypingTime(answer) * 1000 || 1000, - presence: 'composing', - linkPreview: false, - quoted: { - key: msg.key, - message: msg.message, - }, - }, - textMessage: { - text: answer, - }, - }); - } - - if (type === 'audio') { - this.waMonitor.waInstances[instance.instanceName].audioWhatsapp({ - number: remoteJid.split('@')[0], - options: { - delay: characterCount ? this.convertToMilliseconds(characterCount) * 1000 || 1000 : 1000, - presence: 'recording', - encoding: true, - }, - audioMessage: { - audio: answer, - }, - }); - } - - if (this.getRegexPatterns().some((pattern) => pattern.test(answer))) { - this.waMonitor.waInstances[instance.instanceName].sendDataWebhook(Events.CHAMA_AI_ACTION, { - remoteJid: remoteJid, - message: msg, - answer: answer, - action: 'transfer', - }); - } - } - } -} diff --git a/src/api/integrations/chamaai/validate/chamaai.schema.ts b/src/api/integrations/chamaai/validate/chamaai.schema.ts deleted file mode 100644 index 0b80104f..00000000 --- a/src/api/integrations/chamaai/validate/chamaai.schema.ts +++ /dev/null @@ -1,35 +0,0 @@ -import { JSONSchema7 } from 'json-schema'; -import { v4 } from 'uuid'; - -const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => { - const properties = {}; - propertyNames.forEach( - (property) => - (properties[property] = { - minLength: 1, - description: `The "${property}" cannot be empty`, - }), - ); - return { - if: { - propertyNames: { - enum: [...propertyNames], - }, - }, - then: { properties }, - }; -}; - -export const chamaaiSchema: JSONSchema7 = { - $id: v4(), - type: 'object', - properties: { - enabled: { type: 'boolean', enum: [true, false] }, - url: { type: 'string' }, - token: { type: 'string' }, - waNumber: { type: 'string' }, - answerByAudio: { type: 'boolean', enum: [true, false] }, - }, - required: ['enabled', 'url', 'token', 'waNumber', 'answerByAudio'], - ...isNotEmpty('enabled', 'url', 'token', 'waNumber', 'answerByAudio'), -}; diff --git a/src/api/integrations/chatwoot/controllers/chatwoot.controller.ts b/src/api/integrations/chatwoot/controllers/chatwoot.controller.ts index c6799ea5..afecc79a 100644 --- a/src/api/integrations/chatwoot/controllers/chatwoot.controller.ts +++ b/src/api/integrations/chatwoot/controllers/chatwoot.controller.ts @@ -5,7 +5,8 @@ import { ConfigService, HttpServer } from '../../../../config/env.config'; import { Logger } from '../../../../config/logger.config'; import { BadRequestException } from '../../../../exceptions'; import { InstanceDto } from '../../../dto/instance.dto'; -import { RepositoryBroker } from '../../../repository/repository.manager'; +import { MongodbRepository } from '../../../repository/mongodb/repository.manager'; +import { PrismaRepository } from '../../../repository/prisma/repository.service'; import { waMonitor } from '../../../server.module'; import { CacheService } from '../../../services/cache.service'; import { ChatwootDto } from '../dto/chatwoot.dto'; @@ -17,7 +18,8 @@ export class ChatwootController { constructor( private readonly chatwootService: ChatwootService, private readonly configService: ConfigService, - private readonly repository: RepositoryBroker, + private readonly mongodbRepository: MongodbRepository, + private readonly prismaRepository: PrismaRepository, ) {} public async createChatwoot(instance: InstanceDto, data: ChatwootDto) { @@ -105,7 +107,13 @@ export class ChatwootController { logger.verbose('requested receiveWebhook from ' + instance.instanceName + ' instance'); const chatwootCache = new CacheService(new CacheEngine(this.configService, ChatwootService.name).getEngine()); - const chatwootService = new ChatwootService(waMonitor, this.configService, this.repository, chatwootCache); + const chatwootService = new ChatwootService( + waMonitor, + this.configService, + this.mongodbRepository, + this.prismaRepository, + chatwootCache, + ); return chatwootService.receiveWebhook(instance, data); } diff --git a/src/api/integrations/chatwoot/models/chatwoot.model.ts b/src/api/integrations/chatwoot/models/chatwoot.model.ts index 423fbb10..41a19388 100644 --- a/src/api/integrations/chatwoot/models/chatwoot.model.ts +++ b/src/api/integrations/chatwoot/models/chatwoot.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../../../libs/db.connect'; +import { mongodbServer } from '../../../../libs/mongodb.connect'; export class ChatwootRaw { _id?: string; @@ -38,5 +38,5 @@ const chatwootSchema = new Schema({ days_limit_import_messages: { type: Number, required: true }, }); -export const ChatwootModel = dbserver?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot'); +export const ChatwootModel = mongodbServer?.model(ChatwootRaw.name, chatwootSchema, 'chatwoot'); export type IChatwootModel = typeof ChatwootModel; diff --git a/src/api/integrations/chatwoot/services/chatwoot.service.ts b/src/api/integrations/chatwoot/services/chatwoot.service.ts index 9a30dc12..48e06557 100644 --- a/src/api/integrations/chatwoot/services/chatwoot.service.ts +++ b/src/api/integrations/chatwoot/services/chatwoot.service.ts @@ -23,7 +23,8 @@ import { ICache } from '../../../abstract/abstract.cache'; import { InstanceDto } from '../../../dto/instance.dto'; import { Options, Quoted, SendAudioDto, SendMediaDto, SendTextDto } from '../../../dto/sendMessage.dto'; import { ChatwootRaw, ContactRaw, MessageRaw } from '../../../models'; -import { RepositoryBroker } from '../../../repository/repository.manager'; +import { MongodbRepository } from '../../../repository/mongodb/repository.manager'; +import { PrismaRepository } from '../../../repository/prisma/repository.service'; import { WAMonitoringService } from '../../../services/monitor.service'; import { Events } from '../../../types/wa.types'; import { ChatwootDto } from '../dto/chatwoot.dto'; @@ -37,7 +38,8 @@ export class ChatwootService { constructor( private readonly waMonitor: WAMonitoringService, private readonly configService: ConfigService, - private readonly repository: RepositoryBroker, + private readonly mongodbRepository: MongodbRepository, + private readonly prismaRepository: PrismaRepository, private readonly cache: ICache, ) {} @@ -444,8 +446,7 @@ export class ChatwootService { const searchableFields = this.getSearchableFields(); // eslint-disable-next-line prettier/prettier - if(contacts.length === 2 && this.getClientCwConfig().merge_brazil_contacts && query.startsWith('+55')){ - + if (contacts.length === 2 && this.getClientCwConfig().merge_brazil_contacts && query.startsWith('+55')) { const contact = this.mergeBrazilianContacts(contacts); if (contact) { return contact; @@ -1194,7 +1195,7 @@ export class ChatwootService { this.logger.verbose('check if is a message deletion'); if (body.event === 'message_updated' && body.content_attributes?.deleted) { - const message = await this.repository.message.find({ + const message = await this.mongodbRepository.message.find({ where: { owner: instance.instanceName, chatwoot: { @@ -1208,7 +1209,7 @@ export class ChatwootService { await waInstance?.client.sendMessage(message[0].key.remoteJid, { delete: message[0].key }); this.logger.verbose('deleting message in repository. Message id: ' + message[0].key.id); - this.repository.message.delete({ + this.mongodbRepository.message.delete({ where: { owner: instance.instanceName, chatwoot: { @@ -1423,7 +1424,7 @@ export class ChatwootService { const chatwootRead = this.configService.get('CHATWOOT').MESSAGE_READ; if (chatwootRead) { - const lastMessage = await this.repository.message.find({ + const lastMessage = await this.mongodbRepository.message.find({ where: { key: { fromMe: false, @@ -1448,7 +1449,7 @@ export class ChatwootService { isRead: true, }, })); - this.repository.message.update(updateMessage, instance.instanceName, true); + this.mongodbRepository.message.update(updateMessage, instance.instanceName, true); } } } @@ -1490,11 +1491,11 @@ export class ChatwootService { } message.chatwoot = chatwootMessageIds; - this.repository.message.update([message], instance.instanceName, true); + this.mongodbRepository.message.update([message], instance.instanceName, true); } private async getMessageByKeyId(instance: InstanceDto, keyId: string): Promise { - const messages = await this.repository.message.find({ + const messages = await this.mongodbRepository.message.find({ where: { key: { id: keyId, @@ -1532,7 +1533,7 @@ export class ChatwootService { private async getQuotedMessage(msg: any, instance: InstanceDto): Promise { if (msg?.content_attributes?.in_reply_to) { - const message = await this.repository.message.find({ + const message = await this.mongodbRepository.message.find({ where: { chatwoot: { messageId: msg?.content_attributes?.in_reply_to, @@ -2132,7 +2133,7 @@ export class ChatwootService { const message = await this.getMessageByKeyId(instance, body.key.id); if (message?.chatwoot?.messageId && message?.chatwoot?.conversationId) { this.logger.verbose('deleting message in repository. Message id: ' + body.key.id); - this.repository.message.delete({ + this.mongodbRepository.message.delete({ where: { key: { id: body.key.id, @@ -2381,7 +2382,7 @@ export class ChatwootService { ); const contactsWithProfilePicture = ( - await this.repository.contact.find({ + await this.mongodbRepository.contact.find({ where: { owner: instance.instanceName, id: { diff --git a/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts b/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts index 0b10e954..82a0ba53 100644 --- a/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts +++ b/src/api/integrations/rabbitmq/controllers/rabbitmq.controller.ts @@ -41,10 +41,8 @@ export class RabbitmqController { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ]; } diff --git a/src/api/integrations/rabbitmq/models/rabbitmq.model.ts b/src/api/integrations/rabbitmq/models/rabbitmq.model.ts index ba0ac1af..7119b22b 100644 --- a/src/api/integrations/rabbitmq/models/rabbitmq.model.ts +++ b/src/api/integrations/rabbitmq/models/rabbitmq.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../../../libs/db.connect'; +import { mongodbServer } from '../../../../libs/mongodb.connect'; export class RabbitmqRaw { _id?: string; @@ -14,5 +14,5 @@ const rabbitmqSchema = new Schema({ events: { type: [String], required: true }, }); -export const RabbitmqModel = dbserver?.model(RabbitmqRaw.name, rabbitmqSchema, 'rabbitmq'); +export const RabbitmqModel = mongodbServer?.model(RabbitmqRaw.name, rabbitmqSchema, 'rabbitmq'); export type IRabbitmqModel = typeof RabbitmqModel; diff --git a/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts b/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts index 7a786bb4..b2450286 100644 --- a/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts +++ b/src/api/integrations/rabbitmq/validate/rabbitmq.schema.ts @@ -53,10 +53,8 @@ export const rabbitmqSchema: JSONSchema7 = { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ], }, }, diff --git a/src/api/integrations/sqs/controllers/sqs.controller.ts b/src/api/integrations/sqs/controllers/sqs.controller.ts index d6dd346b..8905d4de 100644 --- a/src/api/integrations/sqs/controllers/sqs.controller.ts +++ b/src/api/integrations/sqs/controllers/sqs.controller.ts @@ -41,10 +41,8 @@ export class SqsController { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ]; } diff --git a/src/api/integrations/sqs/models/sqs.model.ts b/src/api/integrations/sqs/models/sqs.model.ts index c3ac4fb1..9ac2cbb2 100644 --- a/src/api/integrations/sqs/models/sqs.model.ts +++ b/src/api/integrations/sqs/models/sqs.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../../../libs/db.connect'; +import { mongodbServer } from '../../../../libs/mongodb.connect'; export class SqsRaw { _id?: string; @@ -14,5 +14,5 @@ const sqsSchema = new Schema({ events: { type: [String], required: true }, }); -export const SqsModel = dbserver?.model(SqsRaw.name, sqsSchema, 'sqs'); +export const SqsModel = mongodbServer?.model(SqsRaw.name, sqsSchema, 'sqs'); export type ISqsModel = typeof SqsModel; diff --git a/src/api/integrations/sqs/validate/sqs.schema.ts b/src/api/integrations/sqs/validate/sqs.schema.ts index 54379c29..254ae00f 100644 --- a/src/api/integrations/sqs/validate/sqs.schema.ts +++ b/src/api/integrations/sqs/validate/sqs.schema.ts @@ -53,10 +53,8 @@ export const sqsSchema: JSONSchema7 = { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ], }, }, diff --git a/src/api/integrations/typebot/models/typebot.model.ts b/src/api/integrations/typebot/models/typebot.model.ts index 9060b6e0..852c2922 100644 --- a/src/api/integrations/typebot/models/typebot.model.ts +++ b/src/api/integrations/typebot/models/typebot.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../../../libs/db.connect'; +import { mongodbServer } from '../../../../libs/mongodb.connect'; class Session { remoteJid?: string; @@ -54,5 +54,5 @@ const typebotSchema = new Schema({ ], }); -export const TypebotModel = dbserver?.model(TypebotRaw.name, typebotSchema, 'typebot'); +export const TypebotModel = mongodbServer?.model(TypebotRaw.name, typebotSchema, 'typebot'); export type ITypebotModel = typeof TypebotModel; diff --git a/src/api/integrations/websocket/controllers/websocket.controller.ts b/src/api/integrations/websocket/controllers/websocket.controller.ts index 2d207b74..d2f2664d 100644 --- a/src/api/integrations/websocket/controllers/websocket.controller.ts +++ b/src/api/integrations/websocket/controllers/websocket.controller.ts @@ -41,10 +41,8 @@ export class WebsocketController { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ]; } diff --git a/src/api/integrations/websocket/models/websocket.model.ts b/src/api/integrations/websocket/models/websocket.model.ts index 9e824597..c28bd956 100644 --- a/src/api/integrations/websocket/models/websocket.model.ts +++ b/src/api/integrations/websocket/models/websocket.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../../../libs/db.connect'; +import { mongodbServer } from '../../../../libs/mongodb.connect'; export class WebsocketRaw { _id?: string; @@ -14,5 +14,5 @@ const websocketSchema = new Schema({ events: { type: [String], required: true }, }); -export const WebsocketModel = dbserver?.model(WebsocketRaw.name, websocketSchema, 'websocket'); +export const WebsocketModel = mongodbServer?.model(WebsocketRaw.name, websocketSchema, 'websocket'); export type IWebsocketModel = typeof WebsocketModel; diff --git a/src/api/integrations/websocket/validate/websocket.schema.ts b/src/api/integrations/websocket/validate/websocket.schema.ts index ddddeee2..f2cdb6ed 100644 --- a/src/api/integrations/websocket/validate/websocket.schema.ts +++ b/src/api/integrations/websocket/validate/websocket.schema.ts @@ -53,10 +53,8 @@ export const websocketSchema: JSONSchema7 = { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ], }, }, diff --git a/src/api/models/auth.model.ts b/src/api/models/auth.model.ts index 9ae5537f..fd84fd45 100644 --- a/src/api/models/auth.model.ts +++ b/src/api/models/auth.model.ts @@ -1,20 +1,18 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; export class AuthRaw { _id?: string; - jwt?: string; apikey?: string; instanceId?: string; } const authSchema = new Schema({ _id: { type: String, _id: true }, - jwt: { type: String, minlength: 1 }, apikey: { type: String, minlength: 1 }, instanceId: { type: String, minlength: 1 }, }); -export const AuthModel = dbserver?.model(AuthRaw.name, authSchema, 'authentication'); +export const AuthModel = mongodbServer?.model(AuthRaw.name, authSchema, 'authentication'); export type IAuthModel = typeof AuthModel; diff --git a/src/api/models/chat.model.ts b/src/api/models/chat.model.ts index 9e713a13..0d575e4a 100644 --- a/src/api/models/chat.model.ts +++ b/src/api/models/chat.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; export class ChatRaw { _id?: string; @@ -22,5 +22,5 @@ const chatSchema = new Schema({ labels: { type: [String], default: [] }, }); -export const ChatModel = dbserver?.model(ChatRaw.name, chatSchema, 'chats'); +export const ChatModel = mongodbServer?.model(ChatRaw.name, chatSchema, 'chats'); export type IChatModel = typeof ChatModel; diff --git a/src/api/models/contact.model.ts b/src/api/models/contact.model.ts index a915bb19..b1bdd9fb 100644 --- a/src/api/models/contact.model.ts +++ b/src/api/models/contact.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; export class ContactRaw { _id?: string; @@ -23,5 +23,5 @@ const contactSchema = new Schema({ owner: { type: String, required: true, minlength: 1 }, }); -export const ContactModel = dbserver?.model(ContactRaw.name, contactSchema, 'contacts'); +export const ContactModel = mongodbServer?.model(ContactRaw.name, contactSchema, 'contacts'); export type IContactModel = typeof ContactModel; diff --git a/src/api/models/index.ts b/src/api/models/index.ts index 42399bc5..2a94c737 100644 --- a/src/api/models/index.ts +++ b/src/api/models/index.ts @@ -1,4 +1,3 @@ -export * from '../integrations/chamaai/models/chamaai.model'; export * from '../integrations/chatwoot/models/chatwoot.model'; export * from '../integrations/rabbitmq/models/rabbitmq.model'; export * from '../integrations/sqs/models/sqs.model'; diff --git a/src/api/models/integration.model.ts b/src/api/models/integration.model.ts index 3bacd7ee..6ff519fb 100644 --- a/src/api/models/integration.model.ts +++ b/src/api/models/integration.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; export class IntegrationRaw { _id?: string; @@ -16,5 +16,5 @@ const integrationSchema = new Schema({ token: { type: String, required: true }, }); -export const IntegrationModel = dbserver?.model(IntegrationRaw.name, integrationSchema, 'integration'); +export const IntegrationModel = mongodbServer?.model(IntegrationRaw.name, integrationSchema, 'integration'); export type IntegrationModel = typeof IntegrationModel; diff --git a/src/api/models/label.model.ts b/src/api/models/label.model.ts index 99c39909..e6931e27 100644 --- a/src/api/models/label.model.ts +++ b/src/api/models/label.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; export class LabelRaw { _id?: string; @@ -25,5 +25,5 @@ const labelSchema = new Schema({ predefinedId: { type: String }, }); -export const LabelModel = dbserver?.model(LabelRaw.name, labelSchema, 'labels'); +export const LabelModel = mongodbServer?.model(LabelRaw.name, labelSchema, 'labels'); export type ILabelModel = typeof LabelModel; diff --git a/src/api/models/message.model.ts b/src/api/models/message.model.ts index 95cb5513..7214c5a7 100644 --- a/src/api/models/message.model.ts +++ b/src/api/models/message.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; import { wa } from '../types/wa.types'; class Key { @@ -72,7 +72,7 @@ messageSchema.index({ 'key.id': 1 }); messageSchema.index({ 'key.id': 1, owner: 1 }); messageSchema.index({ owner: 1 }); -export const MessageModel = dbserver?.model(MessageRaw.name, messageSchema, 'messages'); +export const MessageModel = mongodbServer?.model(MessageRaw.name, messageSchema, 'messages'); export type IMessageModel = typeof MessageModel; export class MessageUpdateRaw { @@ -98,5 +98,5 @@ const messageUpdateSchema = new Schema({ owner: { type: String, required: true, min: 1 }, }); -export const MessageUpModel = dbserver?.model(MessageUpdateRaw.name, messageUpdateSchema, 'messageUpdate'); +export const MessageUpModel = mongodbServer?.model(MessageUpdateRaw.name, messageUpdateSchema, 'messageUpdate'); export type IMessageUpModel = typeof MessageUpModel; diff --git a/src/api/models/proxy.model.ts b/src/api/models/proxy.model.ts index 4096f58f..ed4580f4 100644 --- a/src/api/models/proxy.model.ts +++ b/src/api/models/proxy.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; class Proxy { host?: string; @@ -28,5 +28,5 @@ const proxySchema = new Schema({ }, }); -export const ProxyModel = dbserver?.model(ProxyRaw.name, proxySchema, 'proxy'); +export const ProxyModel = mongodbServer?.model(ProxyRaw.name, proxySchema, 'proxy'); export type IProxyModel = typeof ProxyModel; diff --git a/src/api/models/settings.model.ts b/src/api/models/settings.model.ts index 64c032ed..05f073c5 100644 --- a/src/api/models/settings.model.ts +++ b/src/api/models/settings.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; export class SettingsRaw { _id?: string; @@ -24,5 +24,5 @@ const settingsSchema = new Schema({ sync_full_history: { type: Boolean, required: true }, }); -export const SettingsModel = dbserver?.model(SettingsRaw.name, settingsSchema, 'settings'); +export const SettingsModel = mongodbServer?.model(SettingsRaw.name, settingsSchema, 'settings'); export type ISettingsModel = typeof SettingsModel; diff --git a/src/api/models/webhook.model.ts b/src/api/models/webhook.model.ts index 9a1bb43d..5c970392 100644 --- a/src/api/models/webhook.model.ts +++ b/src/api/models/webhook.model.ts @@ -1,6 +1,6 @@ import { Schema } from 'mongoose'; -import { dbserver } from '../../libs/db.connect'; +import { mongodbServer } from '../../libs/mongodb.connect'; export class WebhookRaw { _id?: string; @@ -20,5 +20,5 @@ const webhookSchema = new Schema({ webhook_base64: { type: Boolean, required: true }, }); -export const WebhookModel = dbserver?.model(WebhookRaw.name, webhookSchema, 'webhook'); +export const WebhookModel = mongodbServer?.model(WebhookRaw.name, webhookSchema, 'webhook'); export type IWebhookModel = typeof WebhookModel; diff --git a/src/api/repository/auth.repository.ts b/src/api/repository/mongodb/auth.repository.ts similarity index 85% rename from src/api/repository/auth.repository.ts rename to src/api/repository/mongodb/auth.repository.ts index 6a296902..94ea80f7 100644 --- a/src/api/repository/auth.repository.ts +++ b/src/api/repository/mongodb/auth.repository.ts @@ -1,11 +1,11 @@ import { opendirSync, readFileSync } from 'fs'; import { join } from 'path'; -import { Auth, ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { AUTH_DIR } from '../../config/path.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { AuthRaw, IAuthModel, IntegrationModel } from '../models'; +import { Auth, ConfigService } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { AUTH_DIR } from '../../../config/path.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { AuthRaw, IAuthModel, IntegrationModel } from '../../models'; export class AuthRepository extends Repository { constructor( @@ -35,11 +35,11 @@ export class AuthRepository extends Repository { this.logger.verbose('saving auth to store'); this.writeStore({ - path: join(AUTH_DIR, this.auth.TYPE), + path: join(AUTH_DIR, 'apikey'), fileName: instance, data, }); - this.logger.verbose('auth saved to store in path: ' + join(AUTH_DIR, this.auth.TYPE) + '/' + instance); + this.logger.verbose('auth saved to store in path: ' + join(AUTH_DIR, 'apikey') + '/' + instance); this.logger.verbose('auth created'); return { insertCount: 1 }; @@ -59,7 +59,7 @@ export class AuthRepository extends Repository { this.logger.verbose('finding auth in store'); return JSON.parse( - readFileSync(join(AUTH_DIR, this.auth.TYPE, instance + '.json'), { + readFileSync(join(AUTH_DIR, 'apikey', instance + '.json'), { encoding: 'utf-8', }), ) as AuthRaw; @@ -92,14 +92,14 @@ export class AuthRepository extends Repository { this.logger.verbose('listing auth in store'); const auths: AuthRaw[] = []; - const openDir = opendirSync(join(AUTH_DIR, this.auth.TYPE), { + const openDir = opendirSync(join(AUTH_DIR, 'apikey'), { encoding: 'utf-8', }); for await (const dirent of openDir) { if (dirent.isFile()) { auths.push( JSON.parse( - readFileSync(join(AUTH_DIR, this.auth.TYPE, dirent.name), { + readFileSync(join(AUTH_DIR, 'apikey', dirent.name), { encoding: 'utf-8', }), ), diff --git a/src/api/repository/chat.repository.ts b/src/api/repository/mongodb/chat.repository.ts similarity index 94% rename from src/api/repository/chat.repository.ts rename to src/api/repository/mongodb/chat.repository.ts index 5f2f50ce..087b9674 100644 --- a/src/api/repository/chat.repository.ts +++ b/src/api/repository/mongodb/chat.repository.ts @@ -1,10 +1,10 @@ import { opendirSync, readFileSync, rmSync } from 'fs'; import { join } from 'path'; -import { ConfigService, StoreConf } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ChatRaw, ChatRawSelect, IChatModel } from '../models'; +import { ConfigService, StoreConf } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { ChatRaw, ChatRawSelect, IChatModel } from '../../models'; export class ChatQuery { select?: ChatRawSelect; diff --git a/src/api/repository/contact.repository.ts b/src/api/repository/mongodb/contact.repository.ts similarity index 96% rename from src/api/repository/contact.repository.ts rename to src/api/repository/mongodb/contact.repository.ts index d26ada35..d6b4df0d 100644 --- a/src/api/repository/contact.repository.ts +++ b/src/api/repository/mongodb/contact.repository.ts @@ -1,10 +1,10 @@ import { opendirSync, readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService, StoreConf } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ContactRaw, ContactRawSelect, IContactModel } from '../models'; +import { ConfigService, StoreConf } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { ContactRaw, ContactRawSelect, IContactModel } from '../../models'; export class ContactQuery { select?: ContactRawSelect; diff --git a/src/api/repository/mongodb/instance.repository.ts b/src/api/repository/mongodb/instance.repository.ts new file mode 100644 index 00000000..94ea80f7 --- /dev/null +++ b/src/api/repository/mongodb/instance.repository.ts @@ -0,0 +1,149 @@ +import { opendirSync, readFileSync } from 'fs'; +import { join } from 'path'; + +import { Auth, ConfigService } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { AUTH_DIR } from '../../../config/path.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { AuthRaw, IAuthModel, IntegrationModel } from '../../models'; + +export class AuthRepository extends Repository { + constructor( + private readonly authModel: IAuthModel, + private readonly integrationModel: IntegrationModel, + readonly configService: ConfigService, + ) { + super(configService); + this.auth = configService.get('AUTHENTICATION'); + } + + private readonly auth: Auth; + private readonly logger = new Logger('AuthRepository'); + + public async create(data: AuthRaw, instance: string): Promise { + try { + this.logger.verbose('creating auth'); + + if (this.dbSettings.ENABLED) { + this.logger.verbose('saving auth to db'); + const insert = await this.authModel.replaceOne({ _id: instance }, { ...data }, { upsert: true }); + + this.logger.verbose('auth saved to db: ' + insert.modifiedCount + ' auth'); + return { insertCount: insert.modifiedCount }; + } + + this.logger.verbose('saving auth to store'); + + this.writeStore({ + path: join(AUTH_DIR, 'apikey'), + fileName: instance, + data, + }); + this.logger.verbose('auth saved to store in path: ' + join(AUTH_DIR, 'apikey') + '/' + instance); + + this.logger.verbose('auth created'); + return { insertCount: 1 }; + } catch (error) { + return { error } as any; + } + } + + public async find(instance: string): Promise { + try { + this.logger.verbose('finding auth'); + if (this.dbSettings.ENABLED) { + this.logger.verbose('finding auth in db'); + return await this.authModel.findOne({ _id: instance }); + } + + this.logger.verbose('finding auth in store'); + + return JSON.parse( + readFileSync(join(AUTH_DIR, 'apikey', instance + '.json'), { + encoding: 'utf-8', + }), + ) as AuthRaw; + } catch (error) { + return {}; + } + } + + public async findByKey(key: string): Promise { + try { + this.logger.verbose('finding auth'); + if (this.dbSettings.ENABLED) { + this.logger.verbose('finding auth in db'); + return await this.authModel.findOne({ apikey: key }); + } + + return {}; + } catch (error) { + return {}; + } + } + + public async list(): Promise { + try { + if (this.dbSettings.ENABLED) { + this.logger.verbose('listing auth in db'); + return await this.authModel.find(); + } + + this.logger.verbose('listing auth in store'); + + const auths: AuthRaw[] = []; + const openDir = opendirSync(join(AUTH_DIR, 'apikey'), { + encoding: 'utf-8', + }); + for await (const dirent of openDir) { + if (dirent.isFile()) { + auths.push( + JSON.parse( + readFileSync(join(AUTH_DIR, 'apikey', dirent.name), { + encoding: 'utf-8', + }), + ), + ); + } + } + + return auths; + } catch (error) { + return []; + } + } + + public async findInstanceNameById(instanceId: string): Promise { + try { + this.logger.verbose('finding auth by instanceId'); + if (this.dbSettings.ENABLED) { + this.logger.verbose('finding auth in db'); + const response = await this.authModel.findOne({ instanceId }); + + return response._id; + } + + this.logger.verbose('finding auth in store is not supported'); + } catch (error) { + return null; + } + } + + public async findInstanceNameByNumber(number: string): Promise { + try { + this.logger.verbose('finding auth by number'); + if (this.dbSettings.ENABLED) { + this.logger.verbose('finding auth in db'); + const instance = await this.integrationModel.findOne({ number }); + + const response = await this.authModel.findOne({ _id: instance._id }); + + return response._id; + } + + this.logger.verbose('finding auth in store is not supported'); + } catch (error) { + return null; + } + } +} diff --git a/src/api/repository/integration.repository.ts b/src/api/repository/mongodb/integration.repository.ts similarity index 88% rename from src/api/repository/integration.repository.ts rename to src/api/repository/mongodb/integration.repository.ts index 09cb8a9a..cb7f9fa5 100644 --- a/src/api/repository/integration.repository.ts +++ b/src/api/repository/mongodb/integration.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { IntegrationModel, IntegrationRaw } from '../models'; +import { ConfigService } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { IntegrationModel, IntegrationRaw } from '../../models'; export class IntegrationRepository extends Repository { constructor(private readonly integrationModel: IntegrationModel, private readonly configService: ConfigService) { diff --git a/src/api/repository/label.repository.ts b/src/api/repository/mongodb/label.repository.ts similarity index 92% rename from src/api/repository/label.repository.ts rename to src/api/repository/mongodb/label.repository.ts index 7bbb0e58..73ba5bf9 100644 --- a/src/api/repository/label.repository.ts +++ b/src/api/repository/mongodb/label.repository.ts @@ -1,10 +1,10 @@ import { opendirSync, readFileSync, rmSync } from 'fs'; import { join } from 'path'; -import { ConfigService, StoreConf } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ILabelModel, LabelRaw, LabelRawSelect } from '../models'; +import { ConfigService, StoreConf } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { ILabelModel, LabelRaw, LabelRawSelect } from '../../models'; export class LabelQuery { select?: LabelRawSelect; diff --git a/src/api/repository/message.repository.ts b/src/api/repository/mongodb/message.repository.ts similarity index 97% rename from src/api/repository/message.repository.ts rename to src/api/repository/mongodb/message.repository.ts index 9802bfae..ae0bcfc5 100644 --- a/src/api/repository/message.repository.ts +++ b/src/api/repository/mongodb/message.repository.ts @@ -1,10 +1,10 @@ import { opendirSync, readFileSync, rmSync } from 'fs'; import { join } from 'path'; -import { ConfigService, StoreConf } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { IMessageModel, MessageRaw, MessageRawSelect } from '../models'; +import { ConfigService, StoreConf } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { IMessageModel, MessageRaw, MessageRawSelect } from '../../models'; export class MessageQuery { select?: MessageRawSelect; diff --git a/src/api/repository/messageUp.repository.ts b/src/api/repository/mongodb/messageUp.repository.ts similarity index 93% rename from src/api/repository/messageUp.repository.ts rename to src/api/repository/mongodb/messageUp.repository.ts index b97bf59b..8c967e8f 100644 --- a/src/api/repository/messageUp.repository.ts +++ b/src/api/repository/mongodb/messageUp.repository.ts @@ -1,10 +1,10 @@ import { opendirSync, readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService, StoreConf } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { IMessageUpModel, MessageUpdateRaw } from '../models'; +import { ConfigService, StoreConf } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { IMessageUpModel, MessageUpdateRaw } from '../../models'; export class MessageUpQuery { where: MessageUpdateRaw; diff --git a/src/api/repository/proxy.repository.ts b/src/api/repository/mongodb/proxy.repository.ts similarity index 87% rename from src/api/repository/proxy.repository.ts rename to src/api/repository/mongodb/proxy.repository.ts index 169e798f..e624da16 100644 --- a/src/api/repository/proxy.repository.ts +++ b/src/api/repository/mongodb/proxy.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { IProxyModel, ProxyRaw } from '../models'; +import { ConfigService } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { IProxyModel, ProxyRaw } from '../../models'; export class ProxyRepository extends Repository { constructor(private readonly proxyModel: IProxyModel, private readonly configService: ConfigService) { diff --git a/src/api/repository/repository.manager.ts b/src/api/repository/mongodb/repository.manager.ts similarity index 83% rename from src/api/repository/repository.manager.ts rename to src/api/repository/mongodb/repository.manager.ts index 5fd9e55e..410f9509 100644 --- a/src/api/repository/repository.manager.ts +++ b/src/api/repository/mongodb/repository.manager.ts @@ -1,15 +1,15 @@ +import { PrismaClient } from '@prisma/client'; import fs from 'fs'; import { MongoClient } from 'mongodb'; import { join } from 'path'; -import { Auth, ConfigService, Database } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { ChamaaiRepository } from '../integrations/chamaai/repository/chamaai.repository'; -import { ChatwootRepository } from '../integrations/chatwoot/repository/chatwoot.repository'; -import { RabbitmqRepository } from '../integrations/rabbitmq/repository/rabbitmq.repository'; -import { SqsRepository } from '../integrations/sqs/repository/sqs.repository'; -import { TypebotRepository } from '../integrations/typebot/repository/typebot.repository'; -import { WebsocketRepository } from '../integrations/websocket/repository/websocket.repository'; +import { ConfigService, Database } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { ChatwootRepository } from '../../integrations/chatwoot/repository/chatwoot.repository'; +import { RabbitmqRepository } from '../../integrations/rabbitmq/repository/rabbitmq.repository'; +import { SqsRepository } from '../../integrations/sqs/repository/sqs.repository'; +import { TypebotRepository } from '../../integrations/typebot/repository/typebot.repository'; +import { WebsocketRepository } from '../../integrations/websocket/repository/websocket.repository'; import { AuthRepository } from './auth.repository'; import { ChatRepository } from './chat.repository'; import { ContactRepository } from './contact.repository'; @@ -20,7 +20,7 @@ import { MessageUpRepository } from './messageUp.repository'; import { ProxyRepository } from './proxy.repository'; import { SettingsRepository } from './settings.repository'; import { WebhookRepository } from './webhook.repository'; -export class RepositoryBroker { +export class MongodbRepository { constructor( public readonly message: MessageRepository, public readonly chat: ChatRepository, @@ -34,22 +34,28 @@ export class RepositoryBroker { public readonly sqs: SqsRepository, public readonly typebot: TypebotRepository, public readonly proxy: ProxyRepository, - public readonly chamaai: ChamaaiRepository, public readonly integration: IntegrationRepository, public readonly auth: AuthRepository, public readonly labels: LabelRepository, private configService: ConfigService, - dbServer?: MongoClient, + mongodbServer?: MongoClient, + prismaServer?: PrismaClient, ) { - this.dbClient = dbServer; + this.mongodbClient = mongodbServer; + this.prismaClient = prismaServer; this.__init_repo_without_db__(); } - private dbClient?: MongoClient; + private mongodbClient?: MongoClient; + private prismaClient?: PrismaClient; private readonly logger = new Logger('RepositoryBroker'); - public get dbServer() { - return this.dbClient; + public get mongodbServer() { + return this.mongodbClient; + } + + public get prismaServer() { + return this.prismaClient; } private __init_repo_without_db__() { @@ -59,7 +65,7 @@ export class RepositoryBroker { this.logger.verbose('creating store path: ' + storePath); try { - const authDir = join(storePath, 'auth', this.configService.get('AUTHENTICATION').TYPE); + const authDir = join(storePath, 'auth', 'apikey'); const chatsDir = join(storePath, 'chats'); const contactsDir = join(storePath, 'contacts'); const messagesDir = join(storePath, 'messages'); @@ -72,7 +78,6 @@ export class RepositoryBroker { const sqsDir = join(storePath, 'sqs'); const typebotDir = join(storePath, 'typebot'); const proxyDir = join(storePath, 'proxy'); - const chamaaiDir = join(storePath, 'chamaai'); const integrationDir = join(storePath, 'integration'); const tempDir = join(storePath, 'temp'); @@ -128,10 +133,6 @@ export class RepositoryBroker { this.logger.verbose('creating proxy dir: ' + proxyDir); fs.mkdirSync(proxyDir, { recursive: true }); } - if (!fs.existsSync(chamaaiDir)) { - this.logger.verbose('creating chamaai dir: ' + chamaaiDir); - fs.mkdirSync(chamaaiDir, { recursive: true }); - } if (!fs.existsSync(integrationDir)) { this.logger.verbose('creating integration dir: ' + integrationDir); fs.mkdirSync(integrationDir, { recursive: true }); diff --git a/src/api/repository/settings.repository.ts b/src/api/repository/mongodb/settings.repository.ts similarity index 87% rename from src/api/repository/settings.repository.ts rename to src/api/repository/mongodb/settings.repository.ts index 4d09d79f..d2000f48 100644 --- a/src/api/repository/settings.repository.ts +++ b/src/api/repository/mongodb/settings.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { ISettingsModel, SettingsRaw } from '../models'; +import { ConfigService } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { ISettingsModel, SettingsRaw } from '../../models'; export class SettingsRepository extends Repository { constructor(private readonly settingsModel: ISettingsModel, private readonly configService: ConfigService) { diff --git a/src/api/repository/webhook.repository.ts b/src/api/repository/mongodb/webhook.repository.ts similarity index 87% rename from src/api/repository/webhook.repository.ts rename to src/api/repository/mongodb/webhook.repository.ts index 10074516..fc2b6560 100644 --- a/src/api/repository/webhook.repository.ts +++ b/src/api/repository/mongodb/webhook.repository.ts @@ -1,10 +1,10 @@ import { readFileSync } from 'fs'; import { join } from 'path'; -import { ConfigService } from '../../config/env.config'; -import { Logger } from '../../config/logger.config'; -import { IInsert, Repository } from '../abstract/abstract.repository'; -import { IWebhookModel, WebhookRaw } from '../models'; +import { ConfigService } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; +import { IInsert, Repository } from '../../abstract/abstract.repository'; +import { IWebhookModel, WebhookRaw } from '../../models'; export class WebhookRepository extends Repository { constructor(private readonly webhookModel: IWebhookModel, private readonly configService: ConfigService) { diff --git a/src/api/repository/prisma/repository.service.ts b/src/api/repository/prisma/repository.service.ts new file mode 100644 index 00000000..c3724f5a --- /dev/null +++ b/src/api/repository/prisma/repository.service.ts @@ -0,0 +1,29 @@ +import { PrismaClient } from '@prisma/client'; + +import { ConfigService } from '../../../config/env.config'; +import { Logger } from '../../../config/logger.config'; + +export class Query { + where?: T; + sort?: 'asc' | 'desc'; + page?: number; + offset?: number; +} + +export class PrismaRepository extends PrismaClient { + constructor(private readonly configService: ConfigService) { + super(); + } + + private readonly logger = new Logger(PrismaRepository.name); + + public async onModuleInit() { + await this.$connect(); + this.logger.info('Repository:Prisma - ON'); + } + + public async onModuleDestroy() { + await this.$disconnect(); + this.logger.warn('Repository:Prisma - OFF'); + } +} diff --git a/src/api/routes/chat.router.ts b/src/api/routes/chat.router.ts index 4debf3d1..6ccbe89a 100644 --- a/src/api/routes/chat.router.ts +++ b/src/api/routes/chat.router.ts @@ -37,9 +37,9 @@ import { WhatsAppNumberDto, } from '../dto/chat.dto'; import { InstanceDto } from '../dto/instance.dto'; -import { ContactQuery } from '../repository/contact.repository'; -import { MessageQuery } from '../repository/message.repository'; -import { MessageUpQuery } from '../repository/messageUp.repository'; +import { ContactQuery } from '../repository/mongodb/contact.repository'; +import { MessageQuery } from '../repository/mongodb/message.repository'; +import { MessageUpQuery } from '../repository/mongodb/messageUp.repository'; import { chatController } from '../server.module'; import { HttpStatus } from './index.router'; diff --git a/src/api/routes/index.router.ts b/src/api/routes/index.router.ts index 3b26671f..59007b58 100644 --- a/src/api/routes/index.router.ts +++ b/src/api/routes/index.router.ts @@ -1,10 +1,9 @@ import { Router } from 'express'; import fs from 'fs'; -import { Auth, configService } from '../../config/env.config'; +import { configService } from '../../config/env.config'; import { authGuard } from '../guards/auth.guard'; import { instanceExistsGuard, instanceLoggedGuard } from '../guards/instance.guard'; -import { ChamaaiRouter } from '../integrations/chamaai/routes/chamaai.router'; import { ChatwootRouter } from '../integrations/chatwoot/routes/chatwoot.router'; import { RabbitmqRouter } from '../integrations/rabbitmq/routes/rabbitmq.router'; import { SqsRouter } from '../integrations/sqs/routes/sqs.router'; @@ -31,9 +30,8 @@ enum HttpStatus { } const router = Router(); -const authType = configService.get('AUTHENTICATION').TYPE; const serverConfig = configService.get('SERVER'); -const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard[authType]]; +const guards = [instanceExistsGuard, instanceLoggedGuard, authGuard['apikey']]; const packageJson = JSON.parse(fs.readFileSync('./package.json', 'utf8')); @@ -45,7 +43,6 @@ router status: HttpStatus.OK, message: 'Welcome to the Evolution API, it is working!', version: packageJson.version, - swagger: !serverConfig.DISABLE_DOCS ? `${req.protocol}://${req.get('host')}/docs` : undefined, manager: !serverConfig.DISABLE_MANAGER ? `${req.protocol}://${req.get('host')}/manager` : undefined, documentation: `https://doc.evolution-api.com`, }); @@ -62,7 +59,6 @@ router .use('/sqs', new SqsRouter(...guards).router) .use('/typebot', new TypebotRouter(...guards).router) .use('/proxy', new ProxyRouter(...guards).router) - .use('/chamaai', new ChamaaiRouter(...guards).router) .use('/label', new LabelRouter(...guards).router); export { HttpStatus, router }; diff --git a/src/api/routes/instance.router.ts b/src/api/routes/instance.router.ts index d2cc6fb8..a150c15a 100644 --- a/src/api/routes/instance.router.ts +++ b/src/api/routes/instance.router.ts @@ -1,13 +1,12 @@ import { RequestHandler, Router } from 'express'; -import { Auth, ConfigService, Database } from '../../config/env.config'; +import { ConfigService, Database } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; -import { dbserver } from '../../libs/db.connect'; -import { instanceNameSchema, oldTokenSchema, presenceOnlySchema } from '../../validate/validate.schema'; +import { mongodbServer } from '../../libs/mongodb.connect'; +import { instanceNameSchema, presenceOnlySchema } from '../../validate/validate.schema'; import { RouterBroker } from '../abstract/abstract.router'; import { InstanceDto, SetPresenceDto } from '../dto/instance.dto'; import { instanceController } from '../server.module'; -import { OldToken } from '../services/auth.service'; import { HttpStatus } from './index.router'; const logger = new Logger('InstanceRouter'); @@ -15,7 +14,6 @@ const logger = new Logger('InstanceRouter'); export class InstanceRouter extends RouterBroker { constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) { super(); - const auth = configService.get('AUTHENTICATION'); this.router .post('/create', ...guards, async (req, res) => { logger.verbose('request received in createInstance'); @@ -165,25 +163,6 @@ export class InstanceRouter extends RouterBroker { return res.status(HttpStatus.OK).json(response); }); - if (auth.TYPE === 'jwt') { - this.router.put('/refreshToken', async (req, res) => { - logger.verbose('request received in refreshToken'); - logger.verbose('request body: '); - logger.verbose(req.body); - - logger.verbose('request query: '); - logger.verbose(req.query); - const response = await this.dataValidate({ - request: req, - schema: oldTokenSchema, - ClassRef: OldToken, - execute: (_, data) => instanceController.refreshToken(_, data), - }); - - return res.status(HttpStatus.CREATED).json(response); - }); - } - this.router.delete('/deleteDatabase', async (req, res) => { logger.verbose('request received in deleteDatabase'); logger.verbose('request body: '); @@ -194,7 +173,7 @@ export class InstanceRouter extends RouterBroker { const db = this.configService.get('DATABASE'); if (db.ENABLED) { try { - await dbserver.dropDatabase(); + await mongodbServer.dropDatabase(); return res .status(HttpStatus.CREATED) .json({ status: 'SUCCESS', error: false, response: { message: 'database deleted' } }); diff --git a/src/api/server.module.ts b/src/api/server.module.ts index 9b469ea7..85f5c15d 100644 --- a/src/api/server.module.ts +++ b/src/api/server.module.ts @@ -2,7 +2,8 @@ import { CacheEngine } from '../cache/cacheengine'; import { configService } from '../config/env.config'; import { eventEmitter } from '../config/event.config'; import { Logger } from '../config/logger.config'; -import { dbserver } from '../libs/db.connect'; +import { mongodbServer } from '../libs/mongodb.connect'; +import { prismaServer } from '../libs/prisma.connect'; import { ChatController } from './controllers/chat.controller'; import { GroupController } from './controllers/group.controller'; import { InstanceController } from './controllers/instance.controller'; @@ -11,9 +12,6 @@ import { ProxyController } from './controllers/proxy.controller'; import { SendMessageController } from './controllers/sendMessage.controller'; import { SettingsController } from './controllers/settings.controller'; import { WebhookController } from './controllers/webhook.controller'; -import { ChamaaiController } from './integrations/chamaai/controllers/chamaai.controller'; -import { ChamaaiRepository } from './integrations/chamaai/repository/chamaai.repository'; -import { ChamaaiService } from './integrations/chamaai/services/chamaai.service'; import { ChatwootController } from './integrations/chatwoot/controllers/chatwoot.controller'; import { ChatwootRepository } from './integrations/chatwoot/repository/chatwoot.repository'; import { ChatwootService } from './integrations/chatwoot/services/chatwoot.service'; @@ -31,7 +29,6 @@ import { WebsocketRepository } from './integrations/websocket/repository/websock import { WebsocketService } from './integrations/websocket/services/websocket.service'; import { AuthModel, - ChamaaiModel, ChatModel, ChatwootModel, ContactModel, @@ -48,17 +45,18 @@ import { } from './models'; import { LabelModel } from './models/label.model'; import { ProviderFiles } from './provider/sessions'; -import { AuthRepository } from './repository/auth.repository'; -import { ChatRepository } from './repository/chat.repository'; -import { ContactRepository } from './repository/contact.repository'; -import { IntegrationRepository } from './repository/integration.repository'; -import { LabelRepository } from './repository/label.repository'; -import { MessageRepository } from './repository/message.repository'; -import { MessageUpRepository } from './repository/messageUp.repository'; -import { ProxyRepository } from './repository/proxy.repository'; -import { RepositoryBroker } from './repository/repository.manager'; -import { SettingsRepository } from './repository/settings.repository'; -import { WebhookRepository } from './repository/webhook.repository'; +import { AuthRepository } from './repository/mongodb/auth.repository'; +import { ChatRepository } from './repository/mongodb/chat.repository'; +import { ContactRepository } from './repository/mongodb/contact.repository'; +import { IntegrationRepository } from './repository/mongodb/integration.repository'; +import { LabelRepository } from './repository/mongodb/label.repository'; +import { MessageRepository } from './repository/mongodb/message.repository'; +import { MessageUpRepository } from './repository/mongodb/messageUp.repository'; +import { ProxyRepository } from './repository/mongodb/proxy.repository'; +import { MongodbRepository } from './repository/mongodb/repository.manager'; +import { SettingsRepository } from './repository/mongodb/settings.repository'; +import { WebhookRepository } from './repository/mongodb/webhook.repository'; +import { PrismaRepository } from './repository/prisma/repository.service'; import { AuthService } from './services/auth.service'; import { CacheService } from './services/cache.service'; import { IntegrationService } from './services/integration.service'; @@ -77,7 +75,6 @@ const typebotRepository = new TypebotRepository(TypebotModel, configService); const webhookRepository = new WebhookRepository(WebhookModel, configService); const websocketRepository = new WebsocketRepository(WebsocketModel, configService); const proxyRepository = new ProxyRepository(ProxyModel, configService); -const chamaaiRepository = new ChamaaiRepository(ChamaaiModel, configService); const rabbitmqRepository = new RabbitmqRepository(RabbitmqModel, configService); const sqsRepository = new SqsRepository(SqsModel, configService); const integrationRepository = new IntegrationRepository(IntegrationModel, configService); @@ -86,7 +83,7 @@ const settingsRepository = new SettingsRepository(SettingsModel, configService); const authRepository = new AuthRepository(AuthModel, IntegrationModel, configService); const labelRepository = new LabelRepository(LabelModel, configService); -export const repository = new RepositoryBroker( +export const mongodbRepository = new MongodbRepository( messageRepository, chatRepository, contactRepository, @@ -99,14 +96,16 @@ export const repository = new RepositoryBroker( sqsRepository, typebotRepository, proxyRepository, - chamaaiRepository, integrationRepository, authRepository, labelRepository, configService, - dbserver?.getClient(), + mongodbServer.getClient(), + prismaServer, ); +export const prismaRepository = new PrismaRepository(configService); + export const cache = new CacheService(new CacheEngine(configService, 'instance').getEngine()); const chatwootCache = new CacheService(new CacheEngine(configService, ChatwootService.name).getEngine()); const baileysCache = new CacheService(new CacheEngine(configService, 'baileys').getEngine()); @@ -115,14 +114,15 @@ const providerFiles = new ProviderFiles(configService); export const waMonitor = new WAMonitoringService( eventEmitter, configService, - repository, + mongodbRepository, + prismaRepository, cache, chatwootCache, baileysCache, providerFiles, ); -const authService = new AuthService(configService, waMonitor, repository); +const authService = new AuthService(waMonitor, mongodbRepository, prismaRepository); const typebotService = new TypebotService(waMonitor, configService, eventEmitter); export const typebotController = new TypebotController(typebotService); @@ -136,9 +136,6 @@ export const websocketController = new WebsocketController(websocketService); const proxyService = new ProxyService(waMonitor); export const proxyController = new ProxyController(proxyService, waMonitor); -const chamaaiService = new ChamaaiService(waMonitor, configService); -export const chamaaiController = new ChamaaiController(chamaaiService); - const rabbitmqService = new RabbitmqService(waMonitor); export const rabbitmqController = new RabbitmqController(rabbitmqService); @@ -147,8 +144,19 @@ export const sqsController = new SqsController(sqsService); const integrationService = new IntegrationService(waMonitor); -const chatwootService = new ChatwootService(waMonitor, configService, repository, chatwootCache); -export const chatwootController = new ChatwootController(chatwootService, configService, repository); +const chatwootService = new ChatwootService( + waMonitor, + configService, + mongodbRepository, + prismaRepository, + chatwootCache, +); +export const chatwootController = new ChatwootController( + chatwootService, + configService, + mongodbRepository, + prismaRepository, +); const settingsService = new SettingsService(waMonitor); export const settingsController = new SettingsController(settingsService); @@ -156,7 +164,8 @@ export const settingsController = new SettingsController(settingsService); export const instanceController = new InstanceController( waMonitor, configService, - repository, + mongodbRepository, + prismaRepository, eventEmitter, authService, webhookService, diff --git a/src/api/services/auth.service.ts b/src/api/services/auth.service.ts index 45a43551..40be497d 100644 --- a/src/api/services/auth.service.ts +++ b/src/api/services/auth.service.ts @@ -1,75 +1,30 @@ -import axios from 'axios'; -import { isJWT } from 'class-validator'; -import { sign, verify } from 'jsonwebtoken'; import { v4 } from 'uuid'; -import { name as apiName } from '../../../package.json'; -import { Auth, ConfigService, Webhook } from '../../config/env.config'; import { Logger } from '../../config/logger.config'; import { BadRequestException } from '../../exceptions'; import { InstanceDto } from '../dto/instance.dto'; -import { RepositoryBroker } from '../repository/repository.manager'; +import { MongodbRepository } from '../repository/mongodb/repository.manager'; +import { PrismaRepository } from '../repository/prisma/repository.service'; import { WAMonitoringService } from './monitor.service'; -export type JwtPayload = { - instanceName: string; - apiName: string; - jwt?: string; - apikey?: string; - tokenId: string; -}; - -export class OldToken { - oldToken: string; -} - export class AuthService { constructor( - private readonly configService: ConfigService, private readonly waMonitor: WAMonitoringService, - private readonly repository: RepositoryBroker, + private readonly mongodbRepository: MongodbRepository, + private readonly prismaRepository: PrismaRepository, ) {} private readonly logger = new Logger(AuthService.name); - private async jwt(instance: InstanceDto) { - const jwtOpts = this.configService.get('AUTHENTICATION').JWT; - const token = sign( - { - instanceName: instance.instanceName, - apiName, - tokenId: v4(), - }, - jwtOpts.SECRET, - { expiresIn: jwtOpts.EXPIRIN_IN, encoding: 'utf8', subject: 'g-t' }, - ); - - this.logger.verbose('JWT token created: ' + token); - - const auth = await this.repository.auth.create( - { jwt: token, instanceId: instance.instanceId }, - instance.instanceName, - ); - - this.logger.verbose('JWT token saved in database'); - - if (auth['error']) { - this.logger.error({ - localError: AuthService.name + '.jwt', - error: auth['error'], - }); - throw new BadRequestException('Authentication error', auth['error']?.toString()); - } - - return { jwt: token }; - } - private async apikey(instance: InstanceDto, token?: string) { const apikey = token ? token : v4().toUpperCase(); this.logger.verbose(token ? 'APIKEY defined: ' + apikey : 'APIKEY created: ' + apikey); - const auth = await this.repository.auth.create({ apikey, instanceId: instance.instanceId }, instance.instanceName); + const auth = await this.mongodbRepository.auth.create( + { apikey, instanceId: instance.instanceId }, + instance.instanceName, + ); this.logger.verbose('APIKEY saved in database'); @@ -101,80 +56,8 @@ export class AuthService { } public async generateHash(instance: InstanceDto, token?: string) { - const options = this.configService.get('AUTHENTICATION'); + this.logger.verbose('generating hash apiKey to instance: ' + instance.instanceName); - this.logger.verbose('generating hash ' + options.TYPE + ' to instance: ' + instance.instanceName); - - return (await this[options.TYPE](instance, token)) as { jwt: string } | { apikey: string }; - } - - public async refreshToken({ oldToken }: OldToken) { - this.logger.verbose('refreshing token'); - - if (!isJWT(oldToken)) { - throw new BadRequestException('Invalid "oldToken"'); - } - - try { - const jwtOpts = this.configService.get('AUTHENTICATION').JWT; - - this.logger.verbose('checking oldToken'); - - const decode = verify(oldToken, jwtOpts.SECRET, { - ignoreExpiration: true, - }) as Pick; - - this.logger.verbose('checking token in database'); - - const tokenStore = await this.repository.auth.find(decode.instanceName); - - const decodeTokenStore = verify(tokenStore.jwt, jwtOpts.SECRET, { - ignoreExpiration: true, - }) as Pick; - - this.logger.verbose('checking tokenId'); - - if (decode.tokenId !== decodeTokenStore.tokenId) { - throw new BadRequestException('Invalid "oldToken"'); - } - - this.logger.verbose('generating new token'); - - const token = { - jwt: (await this.jwt({ instanceName: decode.instanceName })).jwt, - instanceName: decode.instanceName, - }; - - try { - this.logger.verbose('checking webhook'); - const webhook = await this.repository.webhook.find(decode.instanceName); - if (webhook?.enabled && this.configService.get('WEBHOOK').EVENTS.NEW_JWT_TOKEN) { - this.logger.verbose('sending webhook'); - - const httpService = axios.create({ baseURL: webhook.url }); - await httpService.post( - '', - { - event: 'new.jwt', - instance: decode.instanceName, - data: token, - }, - { params: { owner: this.waMonitor.waInstances[decode.instanceName].wuid } }, - ); - } - } catch (error) { - this.logger.error(error); - } - - this.logger.verbose('token refreshed'); - - return token; - } catch (error) { - this.logger.error({ - localError: AuthService.name + '.refreshToken', - error, - }); - throw new BadRequestException('Invalid "oldToken"'); - } + return (await this.apikey(instance, token)) as { apikey: string }; } } diff --git a/src/api/services/channel.service.ts b/src/api/services/channel.service.ts index ac4eae90..1560d589 100644 --- a/src/api/services/channel.service.ts +++ b/src/api/services/channel.service.ts @@ -21,7 +21,6 @@ import { import { Logger } from '../../config/logger.config'; import { ROOT_DIR } from '../../config/path.config'; import { NotFoundException } from '../../exceptions'; -import { ChamaaiService } from '../integrations/chamaai/services/chamaai.service'; import { ChatwootRaw } from '../integrations/chatwoot/models/chatwoot.model'; import { ChatwootService } from '../integrations/chatwoot/services/chatwoot.service'; import { getAMQP, removeQueues } from '../integrations/rabbitmq/libs/amqp.server'; @@ -29,12 +28,13 @@ import { getSQS, removeQueues as removeQueuesSQS } from '../integrations/sqs/lib import { TypebotService } from '../integrations/typebot/services/typebot.service'; import { getIO } from '../integrations/websocket/libs/socket.server'; import { WebsocketRaw } from '../integrations/websocket/models/websocket.model'; -import { ChamaaiRaw, IntegrationRaw, ProxyRaw, RabbitmqRaw, SettingsRaw, SqsRaw, TypebotRaw } from '../models'; +import { IntegrationRaw, ProxyRaw, RabbitmqRaw, SettingsRaw, SqsRaw, TypebotRaw } from '../models'; import { WebhookRaw } from '../models/webhook.model'; -import { ContactQuery } from '../repository/contact.repository'; -import { MessageQuery } from '../repository/message.repository'; -import { MessageUpQuery } from '../repository/messageUp.repository'; -import { RepositoryBroker } from '../repository/repository.manager'; +import { ContactQuery } from '../repository/mongodb/contact.repository'; +import { MessageQuery } from '../repository/mongodb/message.repository'; +import { MessageUpQuery } from '../repository/mongodb/messageUp.repository'; +import { MongodbRepository } from '../repository/mongodb/repository.manager'; +import { PrismaRepository } from '../repository/prisma/repository.service'; import { waMonitor } from '../server.module'; import { Events, wa } from '../types/wa.types'; import { CacheService } from './cache.service'; @@ -43,7 +43,8 @@ export class ChannelStartupService { constructor( public readonly configService: ConfigService, public readonly eventEmitter: EventEmitter2, - public readonly repository: RepositoryBroker, + public readonly mongodbRepository: MongodbRepository, + public readonly prismaRepository: PrismaRepository, public readonly chatwootCache: CacheService, ) { this.logger.verbose('ChannelStartupService initialized'); @@ -60,17 +61,20 @@ export class ChannelStartupService { public readonly localSqs: wa.LocalSqs = {}; public readonly localTypebot: wa.LocalTypebot = {}; public readonly localProxy: wa.LocalProxy = {}; - public readonly localChamaai: wa.LocalChamaai = {}; public readonly localIntegration: wa.LocalIntegration = {}; public readonly localSettings: wa.LocalSettings = {}; public readonly storePath = join(ROOT_DIR, 'store'); - public chatwootService = new ChatwootService(waMonitor, this.configService, this.repository, this.chatwootCache); + public chatwootService = new ChatwootService( + waMonitor, + this.configService, + this.mongodbRepository, + this.prismaRepository, + this.chatwootCache, + ); public typebotService = new TypebotService(waMonitor, this.configService, this.eventEmitter); - public chamaaiService = new ChamaaiService(waMonitor, this.configService); - public set instanceName(name: string) { this.logger.setInstance(name); @@ -112,7 +116,7 @@ export class ChannelStartupService { public async loadIntegration() { this.logger.verbose('Loading webhook'); - const data = await this.repository.integration.find(this.instanceName); + const data = await this.mongodbRepository.integration.find(this.instanceName); this.localIntegration.integration = data?.integration; this.logger.verbose(`Integration: ${this.localIntegration.integration}`); @@ -127,7 +131,7 @@ export class ChannelStartupService { public async setIntegration(data: IntegrationRaw) { this.logger.verbose('Setting integration'); - await this.repository.integration.create(data, this.instanceName); + await this.mongodbRepository.integration.create(data, this.instanceName); this.logger.verbose(`Integration: ${data.integration}`); this.logger.verbose(`Integration number: ${data.number}`); this.logger.verbose(`Integration token: ${data.token}`); @@ -139,10 +143,13 @@ export class ChannelStartupService { this.logger.verbose('Finding integration'); let data: any; - data = await this.repository.integration.find(this.instanceName); + data = await this.mongodbRepository.integration.find(this.instanceName); if (!data) { - this.repository.integration.create({ integration: 'WHATSAPP-BAILEYS', number: '', token: '' }, this.instanceName); + this.mongodbRepository.integration.create( + { integration: 'WHATSAPP-BAILEYS', number: '', token: '' }, + this.instanceName, + ); data = { integration: 'WHATSAPP-BAILEYS', number: '', token: '' }; } @@ -159,7 +166,7 @@ export class ChannelStartupService { public async loadSettings() { this.logger.verbose('Loading settings'); - const data = await this.repository.settings.find(this.instanceName); + const data = await this.mongodbRepository.settings.find(this.instanceName); this.localSettings.reject_call = data?.reject_call; this.logger.verbose(`Settings reject_call: ${this.localSettings.reject_call}`); @@ -186,7 +193,7 @@ export class ChannelStartupService { public async setSettings(data: SettingsRaw) { this.logger.verbose('Setting settings'); - await this.repository.settings.create(data, this.instanceName); + await this.mongodbRepository.settings.create(data, this.instanceName); this.logger.verbose(`Settings reject_call: ${data.reject_call}`); this.logger.verbose(`Settings msg_call: ${data.msg_call}`); this.logger.verbose(`Settings groups_ignore: ${data.groups_ignore}`); @@ -200,7 +207,7 @@ export class ChannelStartupService { public async findSettings() { this.logger.verbose('Finding settings'); - const data = await this.repository.settings.find(this.instanceName); + const data = await this.mongodbRepository.settings.find(this.instanceName); if (!data) { this.logger.verbose('Settings not found'); @@ -227,7 +234,7 @@ export class ChannelStartupService { public async loadWebhook() { this.logger.verbose('Loading webhook'); - const data = await this.repository.webhook.find(this.instanceName); + const data = await this.mongodbRepository.webhook.find(this.instanceName); this.localWebhook.url = data?.url; this.logger.verbose(`Webhook url: ${this.localWebhook.url}`); @@ -248,7 +255,7 @@ export class ChannelStartupService { public async setWebhook(data: WebhookRaw) { this.logger.verbose('Setting webhook'); - await this.repository.webhook.create(data, this.instanceName); + await this.mongodbRepository.webhook.create(data, this.instanceName); this.logger.verbose(`Webhook url: ${data.url}`); this.logger.verbose(`Webhook events: ${data.events}`); Object.assign(this.localWebhook, data); @@ -257,7 +264,7 @@ export class ChannelStartupService { public async findWebhook() { this.logger.verbose('Finding webhook'); - const data = await this.repository.webhook.find(this.instanceName); + const data = await this.mongodbRepository.webhook.find(this.instanceName); if (!data) { this.logger.verbose('Webhook not found'); @@ -278,7 +285,7 @@ export class ChannelStartupService { public async loadChatwoot() { this.logger.verbose('Loading chatwoot'); - const data = await this.repository.chatwoot.find(this.instanceName); + const data = await this.mongodbRepository.chatwoot.find(this.instanceName); this.localChatwoot.enabled = data?.enabled; this.logger.verbose(`Chatwoot enabled: ${this.localChatwoot.enabled}`); @@ -323,7 +330,7 @@ export class ChannelStartupService { public async setChatwoot(data: ChatwootRaw) { this.logger.verbose('Setting chatwoot'); - await this.repository.chatwoot.create(data, this.instanceName); + await this.mongodbRepository.chatwoot.create(data, this.instanceName); this.logger.verbose(`Chatwoot account id: ${data.account_id}`); this.logger.verbose(`Chatwoot token: ${data.token}`); this.logger.verbose(`Chatwoot url: ${data.url}`); @@ -346,7 +353,7 @@ export class ChannelStartupService { public async findChatwoot() { this.logger.verbose('Finding chatwoot'); - const data = await this.repository.chatwoot.find(this.instanceName); + const data = await this.mongodbRepository.chatwoot.find(this.instanceName); if (!data) { this.logger.verbose('Chatwoot not found'); @@ -393,7 +400,7 @@ export class ChannelStartupService { public async loadWebsocket() { this.logger.verbose('Loading websocket'); - const data = await this.repository.websocket.find(this.instanceName); + const data = await this.mongodbRepository.websocket.find(this.instanceName); this.localWebsocket.enabled = data?.enabled; this.logger.verbose(`Websocket enabled: ${this.localWebsocket.enabled}`); @@ -406,7 +413,7 @@ export class ChannelStartupService { public async setWebsocket(data: WebsocketRaw) { this.logger.verbose('Setting websocket'); - await this.repository.websocket.create(data, this.instanceName); + await this.mongodbRepository.websocket.create(data, this.instanceName); this.logger.verbose(`Websocket events: ${data.events}`); Object.assign(this.localWebsocket, data); this.logger.verbose('Websocket set'); @@ -414,7 +421,7 @@ export class ChannelStartupService { public async findWebsocket() { this.logger.verbose('Finding websocket'); - const data = await this.repository.websocket.find(this.instanceName); + const data = await this.mongodbRepository.websocket.find(this.instanceName); if (!data) { this.logger.verbose('Websocket not found'); @@ -430,7 +437,7 @@ export class ChannelStartupService { public async loadRabbitmq() { this.logger.verbose('Loading rabbitmq'); - const data = await this.repository.rabbitmq.find(this.instanceName); + const data = await this.mongodbRepository.rabbitmq.find(this.instanceName); this.localRabbitmq.enabled = data?.enabled; this.logger.verbose(`Rabbitmq enabled: ${this.localRabbitmq.enabled}`); @@ -443,7 +450,7 @@ export class ChannelStartupService { public async setRabbitmq(data: RabbitmqRaw) { this.logger.verbose('Setting rabbitmq'); - await this.repository.rabbitmq.create(data, this.instanceName); + await this.mongodbRepository.rabbitmq.create(data, this.instanceName); this.logger.verbose(`Rabbitmq events: ${data.events}`); Object.assign(this.localRabbitmq, data); this.logger.verbose('Rabbitmq set'); @@ -451,7 +458,7 @@ export class ChannelStartupService { public async findRabbitmq() { this.logger.verbose('Finding rabbitmq'); - const data = await this.repository.rabbitmq.find(this.instanceName); + const data = await this.mongodbRepository.rabbitmq.find(this.instanceName); if (!data) { this.logger.verbose('Rabbitmq not found'); @@ -475,7 +482,7 @@ export class ChannelStartupService { public async loadSqs() { this.logger.verbose('Loading sqs'); - const data = await this.repository.sqs.find(this.instanceName); + const data = await this.mongodbRepository.sqs.find(this.instanceName); this.localSqs.enabled = data?.enabled; this.logger.verbose(`Sqs enabled: ${this.localSqs.enabled}`); @@ -488,7 +495,7 @@ export class ChannelStartupService { public async setSqs(data: SqsRaw) { this.logger.verbose('Setting sqs'); - await this.repository.sqs.create(data, this.instanceName); + await this.mongodbRepository.sqs.create(data, this.instanceName); this.logger.verbose(`Sqs events: ${data.events}`); Object.assign(this.localSqs, data); this.logger.verbose('Sqs set'); @@ -496,7 +503,7 @@ export class ChannelStartupService { public async findSqs() { this.logger.verbose('Finding sqs'); - const data = await this.repository.sqs.find(this.instanceName); + const data = await this.mongodbRepository.sqs.find(this.instanceName); if (!data) { this.logger.verbose('Sqs not found'); @@ -520,7 +527,7 @@ export class ChannelStartupService { public async loadTypebot() { this.logger.verbose('Loading typebot'); - const data = await this.repository.typebot.find(this.instanceName); + const data = await this.mongodbRepository.typebot.find(this.instanceName); this.localTypebot.enabled = data?.enabled; this.logger.verbose(`Typebot enabled: ${this.localTypebot.enabled}`); @@ -553,7 +560,7 @@ export class ChannelStartupService { public async setTypebot(data: TypebotRaw) { this.logger.verbose('Setting typebot'); - await this.repository.typebot.create(data, this.instanceName); + await this.mongodbRepository.typebot.create(data, this.instanceName); this.logger.verbose(`Typebot typebot: ${data.typebot}`); this.logger.verbose(`Typebot expire: ${data.expire}`); this.logger.verbose(`Typebot keyword_finish: ${data.keyword_finish}`); @@ -566,7 +573,7 @@ export class ChannelStartupService { public async findTypebot() { this.logger.verbose('Finding typebot'); - const data = await this.repository.typebot.find(this.instanceName); + const data = await this.mongodbRepository.typebot.find(this.instanceName); if (!data) { this.logger.verbose('Typebot not found'); @@ -588,7 +595,7 @@ export class ChannelStartupService { public async loadProxy() { this.logger.verbose('Loading proxy'); - const data = await this.repository.proxy.find(this.instanceName); + const data = await this.mongodbRepository.proxy.find(this.instanceName); this.localProxy.enabled = data?.enabled; this.logger.verbose(`Proxy enabled: ${this.localProxy.enabled}`); @@ -601,7 +608,7 @@ export class ChannelStartupService { public async setProxy(data: ProxyRaw) { this.logger.verbose('Setting proxy'); - await this.repository.proxy.create(data, this.instanceName); + await this.mongodbRepository.proxy.create(data, this.instanceName); this.logger.verbose(`Proxy proxy: ${data.proxy}`); Object.assign(this.localProxy, data); this.logger.verbose('Proxy set'); @@ -609,7 +616,7 @@ export class ChannelStartupService { public async findProxy() { this.logger.verbose('Finding proxy'); - const data = await this.repository.proxy.find(this.instanceName); + const data = await this.mongodbRepository.proxy.find(this.instanceName); if (!data) { this.logger.verbose('Proxy not found'); @@ -622,70 +629,6 @@ export class ChannelStartupService { }; } - public async loadChamaai() { - this.logger.verbose('Loading chamaai'); - const data = await this.repository.chamaai.find(this.instanceName); - - this.localChamaai.enabled = data?.enabled; - this.logger.verbose(`Chamaai enabled: ${this.localChamaai.enabled}`); - - this.localChamaai.url = data?.url; - this.logger.verbose(`Chamaai url: ${this.localChamaai.url}`); - - this.localChamaai.token = data?.token; - this.logger.verbose(`Chamaai token: ${this.localChamaai.token}`); - - this.localChamaai.waNumber = data?.waNumber; - this.logger.verbose(`Chamaai waNumber: ${this.localChamaai.waNumber}`); - - this.localChamaai.answerByAudio = data?.answerByAudio; - this.logger.verbose(`Chamaai answerByAudio: ${this.localChamaai.answerByAudio}`); - - this.logger.verbose('Chamaai loaded'); - } - - public async setChamaai(data: ChamaaiRaw) { - this.logger.verbose('Setting chamaai'); - await this.repository.chamaai.create(data, this.instanceName); - this.logger.verbose(`Chamaai url: ${data.url}`); - this.logger.verbose(`Chamaai token: ${data.token}`); - this.logger.verbose(`Chamaai waNumber: ${data.waNumber}`); - this.logger.verbose(`Chamaai answerByAudio: ${data.answerByAudio}`); - - Object.assign(this.localChamaai, data); - this.logger.verbose('Chamaai set'); - } - - public async findChamaai() { - this.logger.verbose('Finding chamaai'); - const data = await this.repository.chamaai.find(this.instanceName); - - if (!data) { - this.logger.verbose('Chamaai not found'); - throw new NotFoundException('Chamaai not found'); - } - - return { - enabled: data.enabled, - url: data.url, - token: data.token, - waNumber: data.waNumber, - answerByAudio: data.answerByAudio, - }; - } - - private assertExchangeAsync = (channel, exchangeName, exchangeType, options) => { - return new Promise((resolve, reject) => { - channel.assertExchange(exchangeName, exchangeType, options, (error, ok) => { - if (error) { - reject(error); - } else { - resolve(ok); - } - }); - }); - }; - public async sendDataWebhook(event: Events, data: T, local = true) { const webhookGlobal = this.configService.get('WEBHOOK'); const webhookLocal = this.localWebhook.events; @@ -703,7 +646,7 @@ export class ChannelStartupService { const now = localISOTime; const expose = this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES; - const tokenStore = await this.repository.auth.find(this.instanceName); + const tokenStore = await this.mongodbRepository.auth.find(this.instanceName); const instanceApikey = tokenStore?.apikey || 'Apikey not found'; if (rabbitmqEnabled) { @@ -1238,7 +1181,7 @@ export class ChannelStartupService { }, }; } - return await this.repository.contact.find(query); + return await this.mongodbRepository.contact.find(query); } public async fetchMessages(query: MessageQuery) { @@ -1256,7 +1199,7 @@ export class ChannelStartupService { limit: query?.limit, }; } - return await this.repository.message.find(query); + return await this.mongodbRepository.message.find(query); } public async fetchStatusMessage(query: MessageUpQuery) { @@ -1274,11 +1217,11 @@ export class ChannelStartupService { limit: query?.limit, }; } - return await this.repository.messageUpdate.find(query); + return await this.mongodbRepository.messageUpdate.find(query); } public async fetchChats() { this.logger.verbose('Fetching chats'); - return await this.repository.chat.find({ where: { owner: this.instance.name } }); + return await this.mongodbRepository.chat.find({ where: { owner: this.instance.name } }); } } diff --git a/src/api/services/channels/whatsapp.baileys.service.ts b/src/api/services/channels/whatsapp.baileys.service.ts index 902eefde..6d83acf9 100644 --- a/src/api/services/channels/whatsapp.baileys.service.ts +++ b/src/api/services/channels/whatsapp.baileys.service.ts @@ -68,9 +68,10 @@ import { } from '../../../config/env.config'; import { INSTANCE_DIR } from '../../../config/path.config'; import { BadRequestException, InternalServerErrorException, NotFoundException } from '../../../exceptions'; -import { dbserver } from '../../../libs/db.connect'; +import { mongodbServer } from '../../../libs/mongodb.connect'; import { makeProxyAgent } from '../../../utils/makeProxyAgent'; -import { useMultiFileAuthStateDb } from '../../../utils/use-multi-file-auth-state-db'; +import { useMultiFileAuthStateMongoDb } from '../../../utils/use-multi-file-auth-state-mongodb'; +import useMultiFileAuthStatePrisma from '../../../utils/use-multi-file-auth-state-prisma'; import { AuthStateProvider } from '../../../utils/use-multi-file-auth-state-provider-files'; import { useMultiFileAuthStateRedisDb } from '../../../utils/use-multi-file-auth-state-redis-db'; import { @@ -126,7 +127,8 @@ import { ChatRaw } from '../../models/chat.model'; import { ContactRaw } from '../../models/contact.model'; import { MessageRaw, MessageUpdateRaw } from '../../models/message.model'; import { ProviderFiles } from '../../provider/sessions'; -import { RepositoryBroker } from '../../repository/repository.manager'; +import { MongodbRepository } from '../../repository/mongodb/repository.manager'; +import { PrismaRepository } from '../../repository/prisma/repository.service'; import { waMonitor } from '../../server.module'; import { Events, MessageSubtype, TypeMediaMessage, wa } from '../../types/wa.types'; import { CacheService } from './../cache.service'; @@ -138,13 +140,14 @@ export class BaileysStartupService extends ChannelStartupService { constructor( public readonly configService: ConfigService, public readonly eventEmitter: EventEmitter2, - public readonly repository: RepositoryBroker, + public readonly mongoRepository: MongodbRepository, + public readonly prismaRepository: PrismaRepository, public readonly cache: CacheService, public readonly chatwootCache: CacheService, public readonly baileysCache: CacheService, private readonly providerFiles: ProviderFiles, ) { - super(configService, eventEmitter, repository, chatwootCache); + super(configService, eventEmitter, mongoRepository, prismaRepository, chatwootCache); this.logger.verbose('BaileysStartupService initialized'); this.cleanStore(); this.instance.qrcode = { count: 0 }; @@ -224,7 +227,7 @@ export class BaileysStartupService extends ChannelStartupService { this.logger.verbose('Profile name not found, trying to get from database'); if (this.configService.get('DATABASE').ENABLED) { this.logger.verbose('Database enabled, trying to get from database'); - const collection = dbserver + const collection = mongodbServer .getClient() .db(this.configService.get('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances') .collection(this.instanceName); @@ -460,7 +463,7 @@ export class BaileysStartupService extends ChannelStartupService { private async getMessage(key: proto.IMessageKey, full = false) { this.logger.verbose('Getting message with key: ' + JSON.stringify(key)); try { - const webMessageInfo = (await this.repository.message.find({ + const webMessageInfo = (await this.mongodbRepository.message.find({ where: { owner: this.instance.name, key: { id: key.id } }, })) as unknown as proto.IWebMessageInfo[]; if (full) { @@ -510,7 +513,8 @@ export class BaileysStartupService extends ChannelStartupService { if (db.SAVE_DATA.INSTANCE && db.ENABLED) { this.logger.verbose('Database enabled'); - return await useMultiFileAuthStateDb(this.instance.name); + if (db.PROVIDER === 'mongodb') return await useMultiFileAuthStateMongoDb(this.instance.name); + else return await useMultiFileAuthStatePrisma(this.instance.name); } this.logger.verbose('Store file enabled'); @@ -528,7 +532,6 @@ export class BaileysStartupService extends ChannelStartupService { this.loadSqs(); this.loadTypebot(); this.loadProxy(); - this.loadChamaai(); this.instance.authState = await this.defineAuthState(); @@ -827,7 +830,7 @@ export class BaileysStartupService extends ChannelStartupService { this.logger.verbose('Event received: chats.upsert'); this.logger.verbose('Finding chats in database'); - const chatsRepository = await this.repository.chat.find({ + const chatsRepository = await this.mongodbRepository.chat.find({ where: { owner: this.instance.name }, }); @@ -845,7 +848,7 @@ export class BaileysStartupService extends ChannelStartupService { this.sendDataWebhook(Events.CHATS_UPSERT, chatsRaw); this.logger.verbose('Inserting chats in database'); - this.repository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); + this.mongodbRepository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); }, 'chats.update': async ( @@ -872,7 +875,7 @@ export class BaileysStartupService extends ChannelStartupService { this.logger.verbose('Deleting chats in database'); chats.forEach( async (chat) => - await this.repository.chat.delete({ + await this.mongodbRepository.chat.delete({ where: { owner: this.instance.name, id: chat }, }), ); @@ -890,7 +893,7 @@ export class BaileysStartupService extends ChannelStartupService { this.logger.verbose('Finding contacts in database'); const contactsRepository = new Set( ( - await this.repository.contact.find({ + await this.mongodbRepository.contact.find({ select: { id: 1, _id: 0 }, where: { owner: this.instance.name }, }) @@ -917,7 +920,7 @@ export class BaileysStartupService extends ChannelStartupService { if (contactsRaw.length > 0) this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); this.logger.verbose('Inserting contacts in database'); - this.repository.contact.insert(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); + this.mongodbRepository.contact.insert(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); if (this.localChatwoot.enabled && this.localChatwoot.import_contacts && contactsRaw.length) { this.chatwootService.addHistoryContacts({ instanceName: this.instance.name }, contactsRaw); @@ -939,7 +942,7 @@ export class BaileysStartupService extends ChannelStartupService { if (contactsRaw.length > 0) this.sendDataWebhook(Events.CONTACTS_UPSERT, contactsRaw); this.logger.verbose('Updating contacts in database'); - this.repository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); + this.mongodbRepository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); } catch (error) { this.logger.error(error); } @@ -963,7 +966,7 @@ export class BaileysStartupService extends ChannelStartupService { this.sendDataWebhook(Events.CONTACTS_UPDATE, contactsRaw); this.logger.verbose('Updating contacts in database'); - this.repository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); + this.mongodbRepository.contact.update(contactsRaw, this.instance.name, database.SAVE_DATA.CONTACTS); }, }; @@ -1004,7 +1007,7 @@ export class BaileysStartupService extends ChannelStartupService { const chatsRaw: ChatRaw[] = []; const chatsRepository = new Set( ( - await this.repository.chat.find({ + await this.mongodbRepository.chat.find({ select: { id: 1, _id: 0 }, where: { owner: this.instance.name }, }) @@ -1027,13 +1030,13 @@ export class BaileysStartupService extends ChannelStartupService { this.sendDataWebhook(Events.CHATS_SET, chatsRaw); this.logger.verbose('Inserting chats in database'); - this.repository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); + this.mongodbRepository.chat.insert(chatsRaw, this.instance.name, database.SAVE_DATA.CHATS); const messagesRaw: MessageRaw[] = []; const messagesRepository = new Set( chatwootImport.getRepositoryMessagesCache(instance) ?? ( - await this.repository.message.find({ + await this.mongodbRepository.message.find({ select: { key: { id: 1 }, _id: 0 }, where: { owner: this.instance.name }, }) @@ -1086,7 +1089,7 @@ export class BaileysStartupService extends ChannelStartupService { this.sendDataWebhook(Events.MESSAGES_SET, [...messagesRaw]); this.logger.verbose('Inserting messages in database'); - await this.repository.message.insert(messagesRaw, this.instance.name, database.SAVE_DATA.NEW_MESSAGE); + await this.mongodbRepository.message.insert(messagesRaw, this.instance.name, database.SAVE_DATA.NEW_MESSAGE); if (this.localChatwoot.enabled && this.localChatwoot.import_messages && messagesRaw.length > 0) { this.chatwootService.addHistoryMessages( @@ -1262,19 +1265,11 @@ export class BaileysStartupService extends ChannelStartupService { } } - if (this.localChamaai.enabled && messageRaw.key.fromMe === false && type === 'notify') { - await this.chamaaiService.sendChamaai( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, - ); - } - this.logger.verbose('Inserting message in database'); - await this.repository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE); + await this.mongodbRepository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE); this.logger.verbose('Verifying contact from message'); - const contact = await this.repository.contact.find({ + const contact = await this.mongodbRepository.contact.find({ where: { owner: this.instance.name, id: received.key.remoteJid }, }); @@ -1311,7 +1306,7 @@ export class BaileysStartupService extends ChannelStartupService { } this.logger.verbose('Updating contact in database'); - await this.repository.contact.update([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); + await this.mongodbRepository.contact.update([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); return; } @@ -1321,7 +1316,7 @@ export class BaileysStartupService extends ChannelStartupService { this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); this.logger.verbose('Inserting contact in database'); - this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); + this.mongodbRepository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); } } catch (error) { this.logger.error(error); @@ -1388,7 +1383,7 @@ export class BaileysStartupService extends ChannelStartupService { this.logger.verbose(message); this.logger.verbose('Inserting message in database'); - await this.repository.messageUpdate.insert( + await this.mongodbRepository.messageUpdate.insert( [message], this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE, @@ -1419,7 +1414,7 @@ export class BaileysStartupService extends ChannelStartupService { this.sendDataWebhook(Events.MESSAGES_UPDATE, message); this.logger.verbose('Inserting message in database'); - this.repository.messageUpdate.insert([message], this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE); + this.mongodbRepository.messageUpdate.insert([message], this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE); } } }, @@ -1462,14 +1457,14 @@ export class BaileysStartupService extends ChannelStartupService { [Events.LABELS_EDIT]: async (label: Label, database: Database) => { this.logger.verbose('Event received: labels.edit'); this.logger.verbose('Finding labels in database'); - const labelsRepository = await this.repository.labels.find({ + const labelsRepository = await this.mongodbRepository.labels.find({ where: { owner: this.instance.name }, }); const savedLabel = labelsRepository.find((l) => l.id === label.id); if (label.deleted && savedLabel) { this.logger.verbose('Sending data to webhook in event LABELS_EDIT'); - await this.repository.labels.delete({ + await this.mongodbRepository.labels.delete({ where: { owner: this.instance.name, id: label.id }, }); this.sendDataWebhook(Events.LABELS_EDIT, { ...label, instance: this.instance.name }); @@ -1479,7 +1474,7 @@ export class BaileysStartupService extends ChannelStartupService { const labelName = label.name.replace(/[^\x20-\x7E]/g, ''); if (!savedLabel || savedLabel.color !== label.color || savedLabel.name !== labelName) { this.logger.verbose('Sending data to webhook in event LABELS_EDIT'); - await this.repository.labels.insert( + await this.mongodbRepository.labels.insert( { color: label.color, name: labelName, @@ -1502,7 +1497,7 @@ export class BaileysStartupService extends ChannelStartupService { // Atualiza labels nos chats if (database.ENABLED && database.SAVE_DATA.CHATS) { - const chats = await this.repository.chat.find({ + const chats = await this.mongodbRepository.chat.find({ where: { owner: this.instance.name, }, @@ -1515,7 +1510,7 @@ export class BaileysStartupService extends ChannelStartupService { } else if (data.type === 'add') { labels = [...labels, data.association.labelId]; } - await this.repository.chat.update( + await this.mongodbRepository.chat.update( [{ id: chat.id, owner: this.instance.name, labels }], this.instance.name, database.SAVE_DATA.CHATS, @@ -2041,7 +2036,7 @@ export class BaileysStartupService extends ChannelStartupService { } this.logger.verbose('Inserting message in database'); - await this.repository.message.insert( + await this.mongodbRepository.message.insert( [messageRaw], this.instance.name, this.configService.get('DATABASE').SAVE_DATA.NEW_MESSAGE, @@ -2160,7 +2155,7 @@ export class BaileysStartupService extends ChannelStartupService { this.logger.verbose('All contacts defined as true'); this.logger.verbose('Getting contacts from database'); - const contacts = await this.repository.contact.find({ + const contacts = await this.mongodbRepository.contact.find({ where: { owner: this.instance.name }, }); @@ -2686,7 +2681,7 @@ export class BaileysStartupService extends ChannelStartupService { onWhatsapp.push(...groups); // USERS - const contacts: ContactRaw[] = await this.repository.contact.findManyById({ + const contacts: ContactRaw[] = await this.mongodbRepository.contact.findManyById({ owner: this.instance.name, ids: jids.users.map(({ jid }) => (jid.startsWith('+') ? jid.substring(1) : jid)), }); @@ -3177,7 +3172,7 @@ export class BaileysStartupService extends ChannelStartupService { public async fetchLabels(): Promise { this.logger.verbose('Fetching labels'); - const labels = await this.repository.labels.find({ + const labels = await this.mongodbRepository.labels.find({ where: { owner: this.instance.name, }, @@ -3485,7 +3480,7 @@ export class BaileysStartupService extends ChannelStartupService { this.logger.verbose('Fetching participants for group: ' + id.groupJid); try { const participants = (await this.client.groupMetadata(id.groupJid)).participants; - const contacts = await this.repository.contact.findManyById({ + const contacts = await this.mongodbRepository.contact.findManyById({ owner: this.instance.name, ids: participants.map((p) => p.id), }); diff --git a/src/api/services/channels/whatsapp.business.service.ts b/src/api/services/channels/whatsapp.business.service.ts index 86178659..3c4ab4e0 100644 --- a/src/api/services/channels/whatsapp.business.service.ts +++ b/src/api/services/channels/whatsapp.business.service.ts @@ -24,7 +24,8 @@ import { } from '../../dto/sendMessage.dto'; import { ContactRaw, MessageRaw, MessageUpdateRaw, SettingsRaw } from '../../models'; import { ProviderFiles } from '../../provider/sessions'; -import { RepositoryBroker } from '../../repository/repository.manager'; +import { MongodbRepository } from '../../repository/mongodb/repository.manager'; +import { PrismaRepository } from '../../repository/prisma/repository.service'; import { Events, wa } from '../../types/wa.types'; import { CacheService } from './../cache.service'; import { ChannelStartupService } from './../channel.service'; @@ -33,13 +34,14 @@ export class BusinessStartupService extends ChannelStartupService { constructor( public readonly configService: ConfigService, public readonly eventEmitter: EventEmitter2, - public readonly repository: RepositoryBroker, + public readonly mongodbRepository: MongodbRepository, + public readonly prismaRepository: PrismaRepository, public readonly cache: CacheService, public readonly chatwootCache: CacheService, public readonly baileysCache: CacheService, private readonly providerFiles: ProviderFiles, ) { - super(configService, eventEmitter, repository, chatwootCache); + super(configService, eventEmitter, mongodbRepository, prismaRepository, chatwootCache); this.logger.verbose('BusinessStartupService initialized'); this.cleanStore(); } @@ -146,7 +148,6 @@ export class BusinessStartupService extends ChannelStartupService { this.loadRabbitmq(); this.loadSqs(); this.loadTypebot(); - this.loadChamaai(); this.logger.verbose('Creating socket'); @@ -442,19 +443,11 @@ export class BusinessStartupService extends ChannelStartupService { } } - if (this.localChamaai.enabled && messageRaw.key.fromMe === false && received?.message.type === 'notify') { - await this.chamaaiService.sendChamaai( - { instanceName: this.instance.name }, - messageRaw.key.remoteJid, - messageRaw, - ); - } - this.logger.verbose('Inserting message in database'); - await this.repository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE); + await this.mongodbRepository.message.insert([messageRaw], this.instance.name, database.SAVE_DATA.NEW_MESSAGE); this.logger.verbose('Verifying contact from message'); - const contact = await this.repository.contact.find({ + const contact = await this.mongodbRepository.contact.find({ where: { owner: this.instance.name, id: key.remoteJid }, }); @@ -491,7 +484,7 @@ export class BusinessStartupService extends ChannelStartupService { } this.logger.verbose('Updating contact in database'); - await this.repository.contact.update([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); + await this.mongodbRepository.contact.update([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); return; } @@ -501,7 +494,7 @@ export class BusinessStartupService extends ChannelStartupService { this.sendDataWebhook(Events.CONTACTS_UPSERT, contactRaw); this.logger.verbose('Inserting contact in database'); - this.repository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); + this.mongodbRepository.contact.insert([contactRaw], this.instance.name, database.SAVE_DATA.CONTACTS); } this.logger.verbose('Event received: messages.update'); if (received.statuses) { @@ -536,7 +529,7 @@ export class BusinessStartupService extends ChannelStartupService { this.logger.verbose(message); this.logger.verbose('Inserting message in database'); - await this.repository.messageUpdate.insert( + await this.mongodbRepository.messageUpdate.insert( [message], this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE, @@ -566,7 +559,11 @@ export class BusinessStartupService extends ChannelStartupService { this.sendDataWebhook(Events.MESSAGES_UPDATE, message); this.logger.verbose('Inserting message in database'); - this.repository.messageUpdate.insert([message], this.instance.name, database.SAVE_DATA.MESSAGE_UPDATE); + this.mongodbRepository.messageUpdate.insert( + [message], + this.instance.name, + database.SAVE_DATA.MESSAGE_UPDATE, + ); } } } @@ -871,7 +868,7 @@ export class BusinessStartupService extends ChannelStartupService { } this.logger.verbose('Inserting message in database'); - await this.repository.message.insert( + await this.mongodbRepository.message.insert( [messageRaw], this.instance.name, this.configService.get('DATABASE').SAVE_DATA.NEW_MESSAGE, diff --git a/src/api/services/monitor.service.ts b/src/api/services/monitor.service.ts index 101b005e..524cb8b4 100644 --- a/src/api/services/monitor.service.ts +++ b/src/api/services/monitor.service.ts @@ -19,7 +19,6 @@ import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config'; import { NotFoundException } from '../../exceptions'; import { AuthModel, - ChamaaiModel, ChatwootModel, ContactModel, LabelModel, @@ -31,7 +30,8 @@ import { WebsocketModel, } from '../models'; import { ProviderFiles } from '../provider/sessions'; -import { RepositoryBroker } from '../repository/repository.manager'; +import { MongodbRepository } from '../repository/mongodb/repository.manager'; +import { PrismaRepository } from '../repository/prisma/repository.service'; import { Integration } from '../types/wa.types'; import { CacheService } from './cache.service'; import { BaileysStartupService } from './channels/whatsapp.baileys.service'; @@ -41,7 +41,8 @@ export class WAMonitoringService { constructor( private readonly eventEmitter: EventEmitter2, private readonly configService: ConfigService, - private readonly repository: RepositoryBroker, + private readonly monogodbRepository: MongodbRepository, + private readonly primaRepository: PrismaRepository, private readonly cache: CacheService, private readonly chatwootCache: CacheService, private readonly baileysCache: CacheService, @@ -56,7 +57,7 @@ export class WAMonitoringService { Object.assign(this.redis, configService.get('CACHE')); this.dbInstance = this.db.ENABLED - ? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances') + ? this.monogodbRepository.mongodbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances') : undefined; } @@ -135,7 +136,7 @@ export class WAMonitoringService { const instanceData = { instance: { instanceName: key, - instanceId: (await this.repository.auth.find(key))?.instanceId, + instanceId: (await this.monogodbRepository.auth.find(key))?.instanceId, owner: value.wuid, profileName: (await value.getProfileName()) || 'not loaded', profilePictureUrl: value.profilePictureUrl, @@ -147,7 +148,7 @@ export class WAMonitoringService { if (this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { instanceData.instance['serverUrl'] = this.configService.get('SERVER').URL; - instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey; + instanceData.instance['apikey'] = (await this.monogodbRepository.auth.find(key))?.apikey; instanceData.instance['chatwoot'] = chatwoot; @@ -161,7 +162,7 @@ export class WAMonitoringService { const instanceData = { instance: { instanceName: key, - instanceId: (await this.repository.auth.find(key))?.instanceId, + instanceId: (await this.monogodbRepository.auth.find(key))?.instanceId, status: value.connectionStatus.state, }, }; @@ -169,7 +170,7 @@ export class WAMonitoringService { if (this.configService.get('AUTHENTICATION').EXPOSE_IN_FETCH_INSTANCES) { instanceData.instance['serverUrl'] = this.configService.get('SERVER').URL; - instanceData.instance['apikey'] = (await this.repository.auth.find(key))?.apikey; + instanceData.instance['apikey'] = (await this.monogodbRepository.auth.find(key))?.apikey; instanceData.instance['chatwoot'] = chatwoot; @@ -193,12 +194,12 @@ export class WAMonitoringService { this.logger.verbose('get instance info'); let instanceName: string; if (instanceId) { - instanceName = await this.repository.auth.findInstanceNameById(instanceId); + instanceName = await this.monogodbRepository.auth.findInstanceNameById(instanceId); if (!instanceName) { throw new NotFoundException(`Instance "${instanceId}" not found`); } } else if (number) { - instanceName = await this.repository.auth.findInstanceNameByNumber(number); + instanceName = await this.monogodbRepository.auth.findInstanceNameByNumber(number); if (!instanceName) { throw new NotFoundException(`Instance "${number}" not found`); } @@ -254,7 +255,7 @@ export class WAMonitoringService { if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { this.logger.verbose('cleaning up instance in database: ' + instanceName); - await this.repository.dbServer.connect(); + await this.monogodbRepository.mongodbServer.connect(); const collections: any[] = await this.dbInstance.collections(); if (collections.length > 0) { await this.dbInstance.dropCollection(instanceName); @@ -291,7 +292,6 @@ 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 + '*')}`); - execSync(`rm -rf ${join(STORE_DIR, 'chamaai', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'proxy', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'rabbitmq', instanceName + '*')}`); execSync(`rm -rf ${join(STORE_DIR, 'typebot', instanceName + '*')}`); @@ -307,7 +307,6 @@ export class WAMonitoringService { await AuthModel.deleteMany({ _id: instanceName }); await WebhookModel.deleteMany({ _id: instanceName }); await ChatwootModel.deleteMany({ _id: instanceName }); - await ChamaaiModel.deleteMany({ _id: instanceName }); await ProxyModel.deleteMany({ _id: instanceName }); await RabbitmqModel.deleteMany({ _id: instanceName }); await TypebotModel.deleteMany({ _id: instanceName }); @@ -328,7 +327,8 @@ export class WAMonitoringService { } else if (this.redis.REDIS.ENABLED && this.redis.REDIS.SAVE_INSTANCES) { await this.loadInstancesFromRedis(); } else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { - await this.loadInstancesFromDatabase(); + if (this.db.PROVIDER === 'mongodb') await this.loadInstancesFromDatabaseMongoDB(); + else if (this.db.PROVIDER === 'postgresql') await this.loadInstancesFromDatabasePostgres(); } else { await this.loadInstancesFromFiles(); } @@ -343,7 +343,7 @@ export class WAMonitoringService { try { const msgParsed = JSON.parse(JSON.stringify(data)); if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) { - await this.repository.dbServer.connect(); + await this.monogodbRepository.mongodbServer.connect(); await this.dbInstance.collection(data.instanceName).replaceOne({ _id: 'integration' }, msgParsed, { upsert: true, }); @@ -358,14 +358,15 @@ export class WAMonitoringService { } private async setInstance(name: string) { - const integration = await this.repository.integration.find(name); + const integration = await this.monogodbRepository.integration.find(name); let instance: BaileysStartupService | BusinessStartupService; if (integration && integration.integration === Integration.WHATSAPP_BUSINESS) { instance = new BusinessStartupService( this.configService, this.eventEmitter, - this.repository, + this.monogodbRepository, + this.primaRepository, this.cache, this.chatwootCache, this.baileysCache, @@ -377,7 +378,8 @@ export class WAMonitoringService { instance = new BaileysStartupService( this.configService, this.eventEmitter, - this.repository, + this.monogodbRepository, + this.primaRepository, this.cache, this.chatwootCache, this.baileysCache, @@ -410,9 +412,9 @@ export class WAMonitoringService { } } - private async loadInstancesFromDatabase() { + private async loadInstancesFromDatabaseMongoDB() { this.logger.verbose('Database enabled'); - await this.repository.dbServer.connect(); + await this.monogodbRepository.mongodbServer.connect(); const collections: any[] = await this.dbInstance.collections(); await this.deleteTempInstances(collections); if (collections.length > 0) { @@ -423,6 +425,20 @@ export class WAMonitoringService { } } + private async loadInstancesFromDatabasePostgres() { + this.logger.verbose('Database enabled'); + await this.primaRepository.onModuleInit(); + + const instances = await this.primaRepository.instance.findMany(); + + if (instances.length === 0) { + this.logger.verbose('No instances found'); + return; + } + + await Promise.all(instances.map(async (instance) => this.setInstance(instance.name))); + } + private async loadInstancesFromProvider() { this.logger.verbose('Provider in files enabled'); const [instances] = await this.providerFiles.allInstances(); @@ -523,7 +539,7 @@ export class WAMonitoringService { return; } this.logger.verbose('Cleaning up temp instances'); - const auths = await this.repository.auth.list(); + const auths = await this.monogodbRepository.auth.list(); if (auths.length === 0) { this.logger.verbose('No temp instances found'); return; diff --git a/src/api/types/wa.types.ts b/src/api/types/wa.types.ts index 9c33ac6f..c9ffc5d9 100644 --- a/src/api/types/wa.types.ts +++ b/src/api/types/wa.types.ts @@ -27,7 +27,6 @@ export enum Events { CALL = 'call', TYPEBOT_START = 'typebot.start', TYPEBOT_CHANGE_STATUS = 'typebot.change-status', - CHAMA_AI_ACTION = 'chama-ai.action', LABELS_EDIT = 'labels.edit', LABELS_ASSOCIATION = 'labels.association', CREDS_UPDATE = 'creds.update', @@ -131,14 +130,6 @@ export declare namespace wa { proxy?: Proxy; }; - export type LocalChamaai = { - enabled?: boolean; - url?: string; - token?: string; - waNumber?: string; - answerByAudio?: boolean; - }; - export type LocalIntegration = { integration?: string; number?: string; diff --git a/src/config/env.config.ts b/src/config/env.config.ts index ddd5ce9f..d878d03a 100644 --- a/src/config/env.config.ts +++ b/src/config/env.config.ts @@ -1,7 +1,7 @@ import { isBooleanString } from 'class-validator'; -import { readFileSync } from 'fs'; -import { load } from 'js-yaml'; -import { join } from 'path'; +import dotenv from 'dotenv'; + +dotenv.config(); export type HttpServer = { TYPE: 'http' | 'https'; @@ -67,6 +67,7 @@ export type DBConnection = { export type Database = { CONNECTION: DBConnection; ENABLED: boolean; + PROVIDER: string; SAVE_DATA: SaveData; }; @@ -95,7 +96,6 @@ export type EventsRabbitmq = { GROUP_UPDATE: boolean; GROUP_PARTICIPANTS_UPDATE: boolean; CALL: boolean; - NEW_JWT_TOKEN: boolean; TYPEBOT_START: boolean; TYPEBOT_CHANGE_STATUS: boolean; }; @@ -153,22 +153,17 @@ export type EventsWebhook = { GROUP_UPDATE: boolean; GROUP_PARTICIPANTS_UPDATE: boolean; CALL: boolean; - NEW_JWT_TOKEN: boolean; TYPEBOT_START: boolean; TYPEBOT_CHANGE_STATUS: boolean; - CHAMA_AI_ACTION: boolean; ERRORS: boolean; ERRORS_WEBHOOK: string; }; export type ApiKey = { KEY: string }; -export type Jwt = { EXPIRIN_IN: number; SECRET: string }; export type Auth = { API_KEY: ApiKey; EXPOSE_IN_FETCH_INSTANCES: boolean; - JWT: Jwt; - TYPE: 'jwt' | 'apikey'; }; export type DelInstance = number | boolean; @@ -252,7 +247,7 @@ export class ConfigService { } private loadEnv() { - this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess(); + this.env = this.envProcess(); this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD'; if (process.env?.DOCKER_ENV === 'true') { this.env.SERVER.TYPE = process.env.SERVER_TYPE as 'http' | 'http'; @@ -260,10 +255,6 @@ export class ConfigService { } } - private envYaml(): Env { - return load(readFileSync(join(process.cwd(), 'src', 'env.yml'), { encoding: 'utf-8' })) as Env; - } - private envProcess(): Env { return { SERVER: { @@ -310,6 +301,7 @@ export class ConfigService { DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME || 'evolution', }, ENABLED: process.env?.DATABASE_ENABLED === 'true', + PROVIDER: process.env.DATABASE_PROVIDER || 'mongodb', SAVE_DATA: { INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true', NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true', @@ -349,7 +341,6 @@ export class ConfigService { GROUP_UPDATE: process.env?.RABBITMQ_EVENTS_GROUPS_UPDATE === 'true', GROUP_PARTICIPANTS_UPDATE: process.env?.RABBITMQ_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true', CALL: process.env?.RABBITMQ_EVENTS_CALL === 'true', - NEW_JWT_TOKEN: process.env?.RABBITMQ_EVENTS_NEW_JWT_TOKEN === 'true', TYPEBOT_START: process.env?.RABBITMQ_EVENTS_TYPEBOT_START === 'true', TYPEBOT_CHANGE_STATUS: process.env?.RABBITMQ_EVENTS_TYPEBOT_CHANGE_STATUS === 'true', }, @@ -423,10 +414,8 @@ export class ConfigService { GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true', GROUP_PARTICIPANTS_UPDATE: process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true', CALL: process.env?.WEBHOOK_EVENTS_CALL === 'true', - NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true', TYPEBOT_START: process.env?.WEBHOOK_EVENTS_TYPEBOT_START === 'true', TYPEBOT_CHANGE_STATUS: process.env?.WEBHOOK_EVENTS_TYPEBOT_CHANGE_STATUS === 'true', - CHAMA_AI_ACTION: process.env?.WEBHOOK_EVENTS_CHAMA_AI_ACTION === 'true', ERRORS: process.env?.WEBHOOK_EVENTS_ERRORS === 'true', ERRORS_WEBHOOK: process.env?.WEBHOOK_EVENTS_ERRORS_WEBHOOK || '', }, @@ -470,17 +459,10 @@ export class ConfigService { }, }, AUTHENTICATION: { - TYPE: process.env.AUTHENTICATION_TYPE as 'apikey', API_KEY: { KEY: process.env.AUTHENTICATION_API_KEY || 'BQYHJGJHJ', }, EXPOSE_IN_FETCH_INSTANCES: process.env?.AUTHENTICATION_EXPOSE_IN_FETCH_INSTANCES === 'true', - JWT: { - EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN) - ? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN) - : 3600, - SECRET: process.env.AUTHENTICATION_JWT_SECRET || 'L=0YWt]b2w[WF>#>:&E`', - }, }, }; } diff --git a/src/dev-env.yml b/src/dev-env.yml deleted file mode 100644 index 42573ef3..00000000 --- a/src/dev-env.yml +++ /dev/null @@ -1,236 +0,0 @@ -# ⚠️ -# ⚠️ ALL SETTINGS DEFINED IN THIS FILE ARE APPLIED TO ALL INSTANCES. -# ⚠️ - -# ⚠️ RENAME THIS FILE TO env.yml - -# Choose the server type for the application -SERVER: - TYPE: http # https - PORT: 8080 # 443 - URL: localhost - DISABLE_MANAGER: false - DISABLE_DOCS: false - -CORS: - ORIGIN: - - "*" - # - yourdomain.com - METHODS: - - POST - - GET - - PUT - - DELETE - CREDENTIALS: true - -# Install ssl certificate and replace string with domain name -# Access: https://certbot.eff.org/instructions?ws=other&os=ubuntufocal -SSL_CONF: - PRIVKEY: /etc/letsencrypt/live//privkey.pem - FULLCHAIN: /etc/letsencrypt/live//fullchain.pem - -# Determine the logs to be displayed -LOG: - LEVEL: - - ERROR - - WARN - - DEBUG - - INFO - - LOG - - VERBOSE - - DARK - - WEBHOOKS - COLOR: true - BAILEYS: error # fatal | error | warn | info | debug | trace - -# Determine how long the instance should be deleted from memory in case of no connection. -# Default time: 5 minutes -# If you don't even want an expiration, enter the value false -DEL_INSTANCE: false # or false -DEL_TEMP_INSTANCES: true # Delete instances with status closed on start - -# Seesion Files Providers -# Provider responsible for managing credentials files and WhatsApp sessions. -PROVIDER: - ENABLED: true - HOST: 127.0.0.1 - PORT: 5656 - PREFIX: evolution - -# Temporary data storage -STORE: - MESSAGES: true - MESSAGE_UP: true - CONTACTS: true - CHATS: true - -CLEAN_STORE: - CLEANING_INTERVAL: 7200 # 7200 seconds === 2h - MESSAGES: true - MESSAGE_UP: true - CONTACTS: true - CHATS: true - -# Permanent data storage -DATABASE: - ENABLED: false - CONNECTION: - URI: "mongodb://root:root@localhost:27017/?authSource=admin&readPreference=primary&ssl=false&directConnection=true" - DB_PREFIX_NAME: evolution - # Choose the data you want to save in the application's database or store - SAVE_DATA: - INSTANCE: false - NEW_MESSAGE: false - MESSAGE_UPDATE: false - CONTACTS: false - CHATS: false - -RABBITMQ: - ENABLED: false - URI: "amqp://guest:guest@localhost:5672" - EXCHANGE_NAME: evolution_exchange - GLOBAL_ENABLED: true - EVENTS: - APPLICATION_STARTUP: false - INSTANCE_CREATE: false - INSTANCE_DELETE: false - QRCODE_UPDATED: false - MESSAGES_SET: false - MESSAGES_UPSERT: true - MESSAGES_UPDATE: true - MESSAGES_DELETE: false - SEND_MESSAGE: false - CONTACTS_SET: false - CONTACTS_UPSERT: false - CONTACTS_UPDATE: false - PRESENCE_UPDATE: false - CHATS_SET: false - CHATS_UPSERT: false - CHATS_UPDATE: false - CHATS_DELETE: false - GROUPS_UPSERT: true - GROUP_UPDATE: true - GROUP_PARTICIPANTS_UPDATE: true - CONNECTION_UPDATE: true - CALL: false - # This events is used with Typebot - TYPEBOT_START: false - TYPEBOT_CHANGE_STATUS: false - -SQS: - ENABLED: true - ACCESS_KEY_ID: "" - SECRET_ACCESS_KEY: "" - ACCOUNT_ID: "" - REGION: "us-east-1" - -WEBSOCKET: - ENABLED: false - GLOBAL_EVENTS: false - -WA_BUSINESS: - TOKEN_WEBHOOK: evolution - URL: https://graph.facebook.com - VERSION: v18.0 - LANGUAGE: pt_BR - -# Global Webhook Settings -# Each instance's Webhook URL and events will be requested at the time it is created -WEBHOOK: - # Define a global webhook that will listen for enabled events from all instances - GLOBAL: - URL: - ENABLED: false - # With this option activated, you work with a url per webhook event, respecting the global url and the name of each event - WEBHOOK_BY_EVENTS: false - # Automatically maps webhook paths - # Set the events you want to hear - EVENTS: - APPLICATION_STARTUP: false - QRCODE_UPDATED: true - MESSAGES_SET: true - MESSAGES_UPSERT: true - MESSAGES_UPDATE: true - MESSAGES_DELETE: true - SEND_MESSAGE: true - CONTACTS_SET: true - CONTACTS_UPSERT: true - CONTACTS_UPDATE: true - PRESENCE_UPDATE: true - CHATS_SET: true - CHATS_UPSERT: true - CHATS_UPDATE: true - CHATS_DELETE: true - GROUPS_UPSERT: true - GROUP_UPDATE: true - GROUP_PARTICIPANTS_UPDATE: true - CONNECTION_UPDATE: true - LABELS_EDIT: true - LABELS_ASSOCIATION: true - CALL: true - # This event fires every time a new token is requested via the refresh route - NEW_JWT_TOKEN: false - # This events is used with Typebot - TYPEBOT_START: false - TYPEBOT_CHANGE_STATUS: false - # This event is used with Chama AI - CHAMA_AI_ACTION: false - # This event is used to send errors to the webhook - ERRORS: false - ERRORS_WEBHOOK: - -CONFIG_SESSION_PHONE: - # Name that will be displayed on smartphone connection - CLIENT: "Evolution API" - NAME: Chrome # Chrome | Firefox | Edge | Opera | Safari - -# Set qrcode display limit -QRCODE: - LIMIT: 30 - COLOR: "#198754" - -TYPEBOT: - API_VERSION: "old" # old | latest - KEEP_OPEN: false - -CHATWOOT: - # If you leave this option as false, when deleting the message for everyone on WhatsApp, it will not be deleted on Chatwoot. - MESSAGE_DELETE: true # false | true - # If you leave this option as true, when sending a message in Chatwoot, the client's last message will be marked as read on WhatsApp. - MESSAGE_READ: false # false | true - IMPORT: - # This db connection is used to import messages from whatsapp to chatwoot database - DATABASE: - CONNECTION: - URI: "postgres://user:password@hostname:port/dbname?sslmode=disable" - PLACEHOLDER_MEDIA_MESSAGE: true - -# Cache to optimize application performance -CACHE: - REDIS: - ENABLED: false - URI: "redis://localhost:6379" - PREFIX_KEY: "evolution" - TTL: 604800 - SAVE_INSTANCES: false - LOCAL: - ENABLED: false - TTL: 86400 - -# Defines an authentication type for the api -# 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 -AUTHENTICATION: - TYPE: apikey # jwt or apikey - # Define a global apikey to access all instances - API_KEY: - # OBS: This key must be inserted in the request header to create an instance. - KEY: B6D711FCDE4D4FD5936544120E713976 - # Expose the api key on return from fetch instances - EXPOSE_IN_FETCH_INSTANCES: true - # 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` - -LANGUAGE: "pt-BR" # pt-BR, en diff --git a/src/docs/swagger.conf.ts b/src/docs/swagger.conf.ts deleted file mode 100644 index 7ce42bae..00000000 --- a/src/docs/swagger.conf.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { Router } from 'express'; -import { join } from 'path'; -import swaggerUi from 'swagger-ui-express'; -import YAML from 'yamljs'; - -const document = YAML.load(join(process.cwd(), 'src', 'docs', 'swagger.yaml')); - -const router = Router(); - -export const swaggerRouter = router.use('/docs', swaggerUi.serve).get( - '/docs', - swaggerUi.setup(document, { - customCssUrl: '/css/dark-theme-swagger.css', - customSiteTitle: 'Evolution API', - customfavIcon: '/images/logo.svg', - }), -); diff --git a/src/docs/swagger.yaml b/src/docs/swagger.yaml deleted file mode 100644 index 59b252d3..00000000 --- a/src/docs/swagger.yaml +++ /dev/null @@ -1,2777 +0,0 @@ -openapi: 3.0.0 -info: - title: Evolution API - description: | -
-
- -
- - [![Whatsapp Group](https://img.shields.io/badge/Group-WhatsApp-%2322BC18)](https://evolution-api.com/whatsapp) - [![Discord Community](https://img.shields.io/badge/Discord-Community-blue)](https://evolution-api.com/discord) - [![Postman Collection](https://img.shields.io/badge/Postman-Collection-orange)](https://evolution-api.com/postman) - [![Documentation](https://img.shields.io/badge/Documentation-Official-green)](https://doc.evolution-api.com) - [![License](https://img.shields.io/badge/license-GPL--3.0-orange)](./LICENSE) - [![Support](https://img.shields.io/badge/Donation-picpay-green)](https://app.picpay.com/user/davidsongomes1998) - [![Support](https://img.shields.io/badge/Buy%20me-coffe-orange)](https://bmc.link/evolutionapi) - -
- -
- - - This project is based on the [evolution](https://github.com/code-chat-br/whatsapp-api). The original project is an implementation of [Baileys](https://github.com/WhiskeySockets/Baileys), serving as a Restful API service that controls WhatsApp functions.
- The code allows the creation of multiservice chats, service bots, or any other system that utilizes WhatsApp. The documentation provides instructions on how to set up and use the project, as well as additional information about its features and configuration options. -
- - [![Run in Postman](https://run.pstmn.io/button.svg)](https://god.gw.postman.com/run-collection/26869335-5546d063-156b-4529-915f-909dd628c090?action=collection%2Ffork&source=rip_markdown&collection-url=entityId%3D26869335-5546d063-156b-4529-915f-909dd628c090%26entityType%3Dcollection%26workspaceId%3D339a4ee7-378b-45c9-b5b8-fd2c0a9c2442) - version: 1.8.0 - contact: - name: DavidsonGomes - email: contato@agenciadgcode.com - url: https://img.shields.io/badge/license-GPL--3.0-orange - license: - name: GNU General Public License v3.0 - url: https://github.com/EvolutionAPI/evolution-api/blob/main/LICENSE -servers: [] -components: - securitySchemes: - apikeyAuth: - type: apiKey - in: header - name: apikey - bearerAuth: - type: http - scheme: bearer - bearerFormat: JWT -security: - - bearerAuth: [] -tags: - - name: Instance Controller - - name: Send Message Controller - - name: Chat Controller - - name: Group Controller - - name: Label Controller - - name: Profile Settings - - name: JWT - - name: Settings - - name: Webhook - - name: Websocket - - name: RabbitMQ - - name: Chatwoot - - name: Typebot - - name: Proxy - - name: Chama AI -paths: - /instance/create: - post: - tags: - - Instance Controller - summary: Create Instance - requestBody: - content: - application/json: - schema: - type: object - properties: - instanceName: - type: string - description: Name of the instance (optional). - token: - type: string - description: Token of the instance (optional). - qrcode: - type: boolean - description: QR Code of the instance (optional). - example: - instanceName: "exampleInstance" - token: "87F3F7D0-4B8A-45D0-8618-7399E4AD6469" - qrcode: true - security: - - apikeyAuth: [] - responses: - "200": - description: Successful response - content: - application/json: {} - /instance/fetchInstances: - get: - tags: - - Instance Controller - summary: Fetch Instances - security: - - apikeyAuth: [] - parameters: - - name: instanceName - in: query - schema: - type: string - description: Retrieve one or all instances (optional). - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: array - items: - type: object - properties: - instance: - type: object - properties: - instanceName: - type: string - owner: - type: string - profileName: - type: string - profilePictureUrl: - type: string - profileStatus: - type: string - status: - type: string - serverUrl: - type: string - apikey: - type: string - /instance/connect/{instanceName}: - get: - tags: - - Instance Controller - summary: Instance Connect - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: Connect to your instance. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: object - properties: - code: - type: string - base64: - type: string - description: The QR Code as a string. - /instance/restart/{instanceName}: - put: - tags: - - Instance Controller - summary: Instance Restart - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: Connect to your instance. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /instance/connectionState/{instanceName}: - get: - tags: - - Instance Controller - summary: Connection Status - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: Check the connection state of your instance. - example: "evolution" - security: - - apikeyAuth: [] - responses: - "200": - description: Successful response - content: - application/json: {} - /instance/logout/{instanceName}: - delete: - tags: - - Instance Controller - summary: Logout Instance - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: Logout from your instance. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /instance/delete/{instanceName}: - delete: - tags: - - Instance Controller - summary: Delete Instance - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: Delete your instance. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /message/sendText/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send a text message to a specified instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: The recipient's phone number. - example: "1234567890" - textMessage: - type: object - properties: - text: - type: string - description: The content of the text message. - example: "Hello, World!" - options: - type: object - properties: - delay: - type: integer - description: Delay time before sending the message. - presence: - type: string - enum: ["composing", "recording", "paused"] - description: Indicates the sender's action/status. - linkPreview: - type: boolean - description: Indicates whether to enable link preview. - quoted: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: The ID of the recipient of the original message. - fromMe: - type: boolean - description: Indicates if the message was sent from the user. - id: - type: string - description: The ID of the original message. - message: - type: object - properties: - conversation: - type: string - description: The content of the quoted message. - mentions: - type: object - properties: - everyone: - type: boolean - description: Indicates whether to mention everyone. - mentioned: - type: array - items: - type: string - description: The phone numbers of the users to be mentioned. - required: - - number - - textMessage - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the message should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendStatus/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send a status message. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - statusMessage: - type: object - properties: - type: - type: string - enum: ["text", "image", "video", "audio"] - description: Type of the status message. - content: - type: string - description: The content of the status message. - backgroundColor: - type: string - description: The background color of the status message. - font: - type: integer - enum: [1, 2, 3, 4, 5] - description: The font of the status message. - allContacts: - type: boolean - description: Indicates whether to send the status message to all contacts. - statusJidList: - type: array - items: - type: string - description: The phone numbers of the users to whom the status message should be sent. - required: - - type - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the status message should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendMedia/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send a media message (image, video, document, audio) to a specified instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: The recipient's phone number. - example: "1234567890" - mediaMessage: - type: object - properties: - mediatype: - type: string - enum: ["image", "document", "video", "audio"] - description: Type of the media content. - fileName: - type: string - description: Name of the media file (optional). - caption: - type: string - description: Caption to accompany the media. - media: - type: string - description: URL of the media content. - required: - - mediatype - - media - options: - type: object - properties: - delay: - type: integer - description: Delay time before sending the message. - presence: - type: string - enum: ["composing", "recording", "paused"] - description: Indicates the sender's action/status. - linkPreview: - type: boolean - description: Indicates whether to enable link preview. - quoted: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: The ID of the recipient of the original message. - fromMe: - type: boolean - description: Indicates if the message was sent from the user. - id: - type: string - description: The ID of the original message. - message: - type: object - properties: - conversation: - type: string - description: The content of the quoted message. - mentions: - type: object - properties: - everyone: - type: boolean - description: Indicates whether to mention everyone. - mentioned: - type: array - items: - type: string - description: The phone numbers of the users to be mentioned. - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the media message should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendWhatsAppAudio/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send an audio message via WhatsApp to a specified instance. - description: This endpoint allows users to share an audio message. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: The recipient's phone number. - example: "1234567890" - audioMessage: - type: object - properties: - audio: - type: string - description: URL of the audio file to be sent. - required: - - audio - options: - type: object - properties: - delay: - type: integer - description: Delay time before sending the message. - presence: - type: string - enum: ["composing", "recording", "paused"] - description: Indicates the sender's action/status. - linkPreview: - type: boolean - description: Indicates whether to enable link preview. - quoted: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: The ID of the recipient of the original message. - fromMe: - type: boolean - description: Indicates if the message was sent from the user. - id: - type: string - description: The ID of the original message. - message: - type: object - properties: - conversation: - type: string - description: The content of the quoted message. - mentions: - type: object - properties: - everyone: - type: boolean - description: Indicates whether to mention everyone. - mentioned: - type: array - items: - type: string - description: The phone numbers of the users to be mentioned. - required: - - number - - audioMessage.audio - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the audio should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendSticker/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send an sticker to a specified instance. - description: This endpoint allows users to share an sticker message. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: The recipient's phone number. - example: "1234567890" - stickerMessage: - type: object - properties: - image: - type: string - description: URL of the audio file to be sent. - required: - - image - options: - type: object - properties: - delay: - type: integer - description: Delay time before sending the message. - presence: - type: string - enum: ["composing", "recording", "paused"] - description: Indicates the sender's action/status. - required: - - number - - audioMessage.audio - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the audio should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendLocation/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send a location to a specified instance. - description: This endpoint allows users to share a location message. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: The recipient's phone number. - example: "1234567890" - locationMessage: - type: object - properties: - name: - type: string - description: Name or title of the location. - address: - type: string - description: Detailed address of the location. - latitude: - type: number - description: Latitude of the location. - format: float - longitude: - type: number - description: Longitude of the location. - format: float - options: - type: object - properties: - delay: - type: integer - description: Delay time before sending the message. - presence: - type: string - enum: ["composing", "recording", "paused"] - description: Indicates the sender's action/status. - linkPreview: - type: boolean - description: Indicates whether to enable link preview. - quoted: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: The ID of the recipient of the original message. - fromMe: - type: boolean - description: Indicates if the message was sent from the user. - id: - type: string - description: The ID of the original message. - message: - type: object - properties: - conversation: - type: string - description: The content of the quoted message. - mentions: - type: object - properties: - everyone: - type: boolean - description: Indicates whether to mention everyone. - mentioned: - type: array - items: - type: string - description: The phone numbers of the users to be mentioned. - required: - - number - - locationMessage.latitude - - locationMessage.longitude - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the location should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendContact/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send contact details to a specified instance. - description: This endpoint allows users to share one or multiple contact details. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: The recipient's phone number. - example: "1234567890" - contactMessage: - type: array - items: - type: object - properties: - fullName: - type: string - description: Full name of the contact. - wuid: - type: string - description: Unique identifier for the contact. - phoneNumber: - type: string - description: Phone number of the contact. - organization: - type: string - description: Organization of the contact. - email: - type: string - description: Email address of the contact. - url: - type: string - description: Url of the contact. - required: - - fullName - - wuid - - phoneNumber - options: - type: object - properties: - delay: - type: integer - description: Delay time before sending the message. - presence: - type: string - enum: ["composing", "recording", "paused"] - description: Indicates the sender's action/status. - linkPreview: - type: boolean - description: Indicates whether to enable link preview. - quoted: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: The ID of the recipient of the original message. - fromMe: - type: boolean - description: Indicates if the message was sent from the user. - id: - type: string - description: The ID of the original message. - message: - type: object - properties: - conversation: - type: string - description: The content of the quoted message. - mentions: - type: object - properties: - everyone: - type: boolean - description: Indicates whether to mention everyone. - mentioned: - type: array - items: - type: string - description: The phone numbers of the users to be mentioned. - required: - - number - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the contacts should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendReaction/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send a reaction to a specified instance. - description: This endpoint allows users to send a reaction to a message. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - reactionMessage: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: The ID of the recipient of the original message. - fromMe: - type: boolean - description: Indicates if the reaction was sent from the user. - id: - type: string - description: The ID of the original message. - reaction: - type: string - maxLength: 1 - description: Reaction character (e.g., emoji). - required: - - key.remoteJid - - key.fromMe - - key.id - - reaction - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendPoll/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send a poll to a specified instance. - description: This endpoint allows users to send a poll to a chat. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: The recipient's phone number. - example: "1234567890" - pollMessage: - type: object - properties: - name: - type: string - description: Name or title of the poll. - selectableCount: - type: integer - description: Number of selectable options. - values: - type: array - items: - type: string - description: The options of the poll. - options: - type: object - properties: - delay: - type: integer - description: Delay time before sending the message. - presence: - type: string - enum: ["composing", "recording", "paused"] - description: Indicates the sender's action/status. - linkPreview: - type: boolean - description: Indicates whether to enable link preview. - quoted: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: The ID of the recipient of the original message. - fromMe: - type: boolean - description: Indicates if the message was sent from the user. - id: - type: string - description: The ID of the original message. - message: - type: object - properties: - conversation: - type: string - description: The content of the quoted message. - mentions: - type: object - properties: - everyone: - type: boolean - description: Indicates whether to mention everyone. - mentioned: - type: array - items: - type: string - description: The phone numbers of the users to be mentioned. - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the poll should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /message/sendList/{instanceName}: - post: - tags: - - Send Message Controller - summary: Send a list to a specified instance. - description: This endpoint allows users to send a list to a chat. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - options: - type: object - properties: - delay: - type: integer - presence: - type: string - listMessage: - type: object - properties: - title: - type: string - description: - type: string - footerText: - type: string - nullable: true - buttonText: - type: string - sections: - type: array - items: - type: object - properties: - title: - type: string - rows: - type: array - items: - type: object - properties: - title: - type: string - description: - type: string - rowId: - type: string - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the poll should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /chat/whatsappNumbers/{instanceName}: - post: - tags: - - Chat Controller - summary: Provide a list of WhatsApp numbers associated with a given instance. - description: This endpoint returns information on the WhatsApp numbers associated with the specified instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - numbers: - type: array - items: - type: string - description: WhatsApp phone number. - example: - - "1234567890" - required: - - numbers - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/markMessageAsRead/{instanceName}: - put: - tags: - - Chat Controller - summary: Mark specific messages as read for a given instance. - description: This endpoint allows users to mark messages as read for a particular instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - read_messages: - type: array - items: - type: object - properties: - remoteJid: - type: string - description: ID of the recipient of the message. - fromMe: - type: boolean - description: Indicates if the message was sent from the user. - id: - type: string - description: Unique ID of the message. - required: - - remoteJid - - fromMe - - id - required: - - read_messages - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/archiveChat/{instanceName}: - put: - tags: - - Chat Controller - summary: Archive specific chats for a given instance. - description: This endpoint allows users to archive specific chats based on the last message. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - lastMessage: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: ID of the recipient of the last message. - fromMe: - type: boolean - description: Indicates if the last message was sent from the user. - id: - type: string - description: Unique ID of the last message. - required: - - remoteJid - - fromMe - - id - archive: - type: boolean - description: Indicates whether to archive the chat. - example: true - required: - - lastMessage - - archive - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/deleteMessageForEveryone/{instanceName}: - delete: - tags: - - Chat Controller - summary: Delete a message for everyone in a given instance. - description: This endpoint allows users to delete a message for everyone in the chat. - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/fetchProfilePictureUrl/{instanceName}: - post: - tags: - - Chat Controller - summary: Retrieve the profile picture URL of a specific number. - description: This endpoint fetches the profile picture URL associated with the given phone number for the specified instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: WhatsApp phone number whose profile picture URL needs to be fetched. - required: - - number - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/findContacts/{instanceName}: - post: - tags: - - Chat Controller - summary: Retrieve contact details using an ID. - description: This endpoint retrieves contact details associated with the given ID for the specified instance. - requestBody: - content: - application/json: - schema: - type: object - properties: - where: - type: object - properties: - id: - type: string - description: Unique ID of the contact to be fetched. - required: - - id - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/getBase64FromMediaMessage/{instanceName}: - post: - tags: - - Chat Controller - summary: Convert media message content to Base64. - description: This endpoint retrieves the Base64 representation of the content of a media message for the specified instance. - requestBody: - content: - application/json: - schema: - type: object - properties: - message: - type: object - properties: - key: - type: object - properties: - id: string - description: Unique ID of the message. - convertToMp4: - type: boolean - description: Indicates whether to convert the media to MP4 format. - example: true - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/findMessages/{instanceName}: - post: - tags: - - Chat Controller - summary: Search for messages based on specific criteria. - description: This endpoint retrieves messages that match the provided criteria for the specified instance. - requestBody: - content: - application/json: - schema: - type: object - properties: - where: - type: object - properties: - key: - type: object - properties: - remoteJid: - type: string - description: ID of the recipient of the message. - fromMe: - type: boolean - description: Indicates if the message was sent from the user. - id: - type: string - description: Unique ID of the message. - required: - - remoteJid - - fromMe - - id - message: - type: object - required: - - key - limit: - type: integer - description: Maximum number of messages to retrieve. - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/findStatusMessage/{instanceName}: - post: - tags: - - Chat Controller - summary: Search for status messages using an ID. - description: This endpoint retrieves status messages associated with the given ID for the specified instance. - requestBody: - content: - application/json: - schema: - type: object - properties: - where: - type: object - properties: - id: - type: string - description: Unique ID of the status message to be fetched. - required: - - id - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/findChats/{instanceName}: - get: - tags: - - Chat Controller - summary: List all chats associated with a specific instance. - description: This endpoint retrieves a list of all chats associated with the specified instance. - parameters: - - name: instanceName - in: path - required: true - schema: - type: string - description: The name of the instance to which the reaction should be sent. - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /group/create/{instanceName}: - post: - tags: - - Group Controller - summary: Create a new WhatsApp group. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - subject: - type: string - description: "- required - The name of the group." - description: - type: string - description: "- optional - A brief description or summary of the group." - participants: - type: array - items: - type: string - description: "- required - List of participant phone numbers." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/updateGroupPicture/{instanceName}: - put: - tags: - - Group Controller - summary: Update the group's display picture. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - image: - type: string - description: "- required - URL of the new group picture." - parameters: - - name: groupJid - in: query - schema: - type: string - description: "- required - The unique identifier of the group." - example: "120363046555718472@g.us" - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/updateGroupSubject/{instanceName}: - put: - tags: - - Group Controller - summary: Update the group's display picture. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - subject: - type: string - description: "- required - The new name of the group." - parameters: - - name: groupJid - in: query - schema: - type: string - description: "- required - The unique identifier of the group." - example: "120363046555718472@g.us" - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/updateGroupDescription/{instanceName}: - put: - tags: - - Group Controller - summary: Update the group's display picture. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - description: - type: string - description: "- required - The new description of the group." - parameters: - - name: groupJid - in: query - schema: - type: string - description: "- required - The unique identifier of the group." - example: "120363046555718472@g.us" - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/inviteCode/{instanceName}: - get: - tags: - - Group Controller - summary: Update the group's display picture. - parameters: - - name: groupJid - in: query - schema: - type: string - description: "- required - The unique identifier of the group." - example: "120363046555718472@g.us" - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/revokeInviteCode/{instanceName}: - put: - tags: - - Group Controller - summary: Update the group's display picture. - parameters: - - name: groupJid - in: query - schema: - type: string - description: "- required - The unique identifier of the group." - example: "120363046555718472@g.us" - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/sendInvite/{instanceName}: - post: - tags: - - Group Controller - summary: Update the group's display picture. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - groupJid: - type: string - description: "The unique identifier of the group." - description: - type: string - description: "The new description of the group." - numbers: - type: array - description: "List of participant phone numbers to be invited." - items: - type: string - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/inviteInfo/{instanceName}: - get: - tags: - - Group Controller - summary: Retrieve details about a specific group. - parameters: - - name: inviteCode - in: query - schema: - type: string - description: "- required - The invite code of the group." - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/findGroupInfos/{instanceName}: - get: - tags: - - Group Controller - summary: Retrieve details about a specific group. - parameters: - - name: groupJid - in: query - schema: - type: string - description: "- required - The unique identifier of the group." - example: "120363046555718472@g.us" - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/fetchAllGroups/{instanceName}: - get: - tags: - - Group Controller - summary: Retrieve details about a specific group. - parameters: - - name: getParticipants - in: query - schema: - type: boolean - description: "- required - Indicates whether to retrieve the participants of the group." - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/participants/{instanceName}: - get: - tags: - - Group Controller - summary: Retrieve a list of participants in a specific group. - parameters: - - name: groupJid - in: query - schema: - type: string - description: "- required - The unique identifier of the group." - example: "120363046555718472@g.us" - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/updateParticipant/{instanceName}: - put: - tags: - - Group Controller - summary: Update the status or role of a participant in the group. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - action: - type: string - enum: ["add", "remove", "promote", "demote"] - description: "- required - The action to be taken on the participant." - participants: - type: array - items: - type: string - description: "- required - List of participant phone numbers to be updated." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/updateSetting/{instanceName}: - put: - tags: - - Group Controller - summary: Update the status or role of a participant in the group. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - action: - type: string - enum: - ["announcement", "not_announcement", "locked", "unlocked"] - description: "- required - The action to be taken on the participant." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/toggleEphemeral/{instanceName}: - put: - tags: - - Group Controller - summary: Update the status or role of a participant in the group. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - expiration: - type: number - description: "- required - The action to be taken on the participant." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /group/leaveGroup/{instanceName}: - delete: - tags: - - Group Controller - summary: Exit from the specified WhatsApp group. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /instance/refreshToken/: - put: - tags: - - JWT - summary: Refresh an expired JWT token. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - oldToken: - type: string - description: "- required - The expired JWT token." - responses: - "200": - description: Successful response - content: - application/json: {} - - /webhook/set/{instanceName}: - post: - tags: - - Webhook - summary: Set up or modify the webhook for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - url: - type: string - format: uri - description: "The endpoint URL where the webhook data will be sent." - webhook_by_events: - type: boolean - description: "Indicates whether to send the webhook data by events." - webhook_base64: - type: boolean - description: "Indicates whether to send the webhook data in Base64 format." - events: - type: array - enum: - [ - "APPLICATION_STARTUP", - "QRCODE_UPDATED", - "MESSAGES_SET", - "MESSAGES_UPSERT", - "MESSAGES_UPDATE", - "MESSAGES_DELETE", - "SEND_MESSAGE", - "CONTACTS_SET", - "CONTACTS_UPSERT", - "CONTACTS_UPDATE", - "PRESENCE_UPDATE", - "CHATS_SET", - "CHATS_UPSERT", - "CHATS_UPDATE", - "CHATS_DELETE", - "GROUPS_UPSERT", - "GROUP_UPDATE", - "GROUP_PARTICIPANTS_UPDATE", - "CONNECTION_UPDATE", - "LABELS_EDIT", - "LABELS_ASSOCIATION", - "CALL", - "NEW_JWT_TOKEN", - ] - items: - type: string - description: "List of events to be sent to the webhook." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /webhook/find/{instanceName}: - get: - tags: - - Webhook - summary: Retrieve the webhook settings for a specific instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /websocket/set/{instanceName}: - post: - tags: - - Websocket - summary: Set up or modify the Websocket for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - description: "Indicates whether to enable the websocket." - events: - type: array - enum: - [ - "APPLICATION_STARTUP", - "QRCODE_UPDATED", - "MESSAGES_SET", - "MESSAGES_UPSERT", - "MESSAGES_UPDATE", - "MESSAGES_DELETE", - "SEND_MESSAGE", - "CONTACTS_SET", - "CONTACTS_UPSERT", - "CONTACTS_UPDATE", - "PRESENCE_UPDATE", - "CHATS_SET", - "CHATS_UPSERT", - "CHATS_UPDATE", - "CHATS_DELETE", - "GROUPS_UPSERT", - "GROUP_UPDATE", - "GROUP_PARTICIPANTS_UPDATE", - "CONNECTION_UPDATE", - "LABELS_EDIT", - "LABELS_ASSOCIATION", - "CALL", - "NEW_JWT_TOKEN", - ] - items: - type: string - description: "List of events to be sent to the websocket." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /websocket/find/{instanceName}: - get: - tags: - - Websocket - summary: Retrieve the websocket settings for a specific instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /rabbitmq/set/{instanceName}: - post: - tags: - - RabbitMQ - summary: Set up or modify the RabbitMQ for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - description: "Indicates whether to enable the RabbitMQ." - events: - type: array - enum: - [ - "APPLICATION_STARTUP", - "QRCODE_UPDATED", - "MESSAGES_SET", - "MESSAGES_UPSERT", - "MESSAGES_UPDATE", - "MESSAGES_DELETE", - "SEND_MESSAGE", - "CONTACTS_SET", - "CONTACTS_UPSERT", - "CONTACTS_UPDATE", - "PRESENCE_UPDATE", - "CHATS_SET", - "CHATS_UPSERT", - "CHATS_UPDATE", - "CHATS_DELETE", - "GROUPS_UPSERT", - "GROUP_UPDATE", - "GROUP_PARTICIPANTS_UPDATE", - "CONNECTION_UPDATE", - "LABELS_EDIT", - "LABELS_ASSOCIATION", - "CALL", - "NEW_JWT_TOKEN", - ] - items: - type: string - description: "List of events to be sent to the RabbitMQ." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /rabbitmq/find/{instanceName}: - get: - tags: - - RabbitMQ - summary: Retrieve the RabbitMQ settings for a specific instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /label/findLabels/{instanceName}: - get: - tags: - - Label Controller - summary: List all labels for an instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: array - items: - type: object - properties: - color: - type: integer - name: - type: string - id: - type: string - predefinedId: - type: string - required: - - color - - name - - id - /label/handleLabel/{instanceName}: - put: - tags: - - Label Controller - summary: Change the label (add or remove) for an specific chat. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - requestBody: - content: - application/json: - schema: - type: object - properties: - number: - type: string - labelId: - type: string - action: - type: string - enum: - - add - - remove - required: - - number - - labelId - - action - example: - number: '553499999999' - labelId: '1' - action: add - responses: - "200": - description: Successful response - content: - application/json: - schema: - type: object - properties: - numberJid: - type: string - labelId: - type: string - remove: - type: boolean - add: - type: boolean - required: - - numberJid - - labelId - - /settings/set/{instanceName}: - post: - tags: - - Settings - summary: Set up or modify the Settings for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - reject_call: - type: boolean - description: "Indicates whether to reject incoming calls." - msg_call: - type: string - description: "Message to be sent when rejecting a call." - groups_ignore: - type: boolean - description: "Indicates whether to ignore group messages." - always_online: - type: boolean - description: "Indicates whether to keep the instance always online." - read_messages: - type: boolean - description: "Indicates whether to mark messages as read." - read_status: - type: boolean - description: "Indicates whether to mark status messages as read." - sync_full_history: - type: boolean - description: "Indicates whether to request a full history messages sync on connect." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /settings/find/{instanceName}: - get: - tags: - - Settings - summary: Retrieve the Settings for a specific instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /chatwoot/set/{instanceName}: - post: - tags: - - Chatwoot - summary: Set up or modify the Chatwoot for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - description: "Indicates whether to enable the Chatwoot integration." - account_id: - type: string - description: "The Chatwoot account ID." - token: - type: string - description: "The Chatwoot token." - url: - type: string - description: "The Chatwoot URL." - sign_msg: - type: boolean - description: "Indicates whether to sign messages." - reopen_conversation: - type: boolean - description: "Indicates whether to reopen conversations." - conversation_pending: - type: boolean - description: "Indicates whether to mark conversations as pending." - merge_brazil_contacts: - type: boolean - description: "Indicates whether to merge Brazil numbers in case of numbers with and without ninth digit." - import_contacts: - type: boolean - description: "Indicates whether to import contacts from phone to Chatwoot when connecting." - import_messages: - type: boolean - description: "Indicates whether to import messages from phone to Chatwoot when connecting." - days_limit_import_messages: - type: number - description: "Indicates number of days to limit messages imported to Chatwoot." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chatwoot/find/{instanceName}: - get: - tags: - - Chatwoot - summary: Retrieve the Chatwoot for a specific instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /typebot/set/{instanceName}: - post: - tags: - - Typebot - summary: Set up or modify the Typebot for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - description: "Indicates whether to enable the Typebot integration." - url: - type: string - description: "The Chatwoot URL." - typebot: - type: string - description: "The Typebot Name." - expire: - type: number - description: "The Typebot Expire." - keyword_finish: - type: string - description: "The Typebot Keyword Finish." - delay_message: - type: number - description: "The Typebot Delay Message." - unknown_message: - type: string - description: "The Typebot Unknown Message." - listening_from_me: - type: boolean - description: "Indicates whether to listening from me." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /typebot/start/{instanceName}: - post: - tags: - - Typebot - summary: Start the Typebot for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - url: - type: string - description: "The Typebot URL." - typebot: - type: string - description: "The Typebot Name." - remoteJid: - type: string - description: "The Typebot RemoteJid." - startSession: - type: boolean - description: "Indicates whether to start session." - variables: - type: array - description: "List of variables." - items: - type: object - properties: - name: - type: string - description: "The variable name." - value: - type: string - description: "The variable value." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /typebot/find/{instanceName}: - get: - tags: - - Typebot - summary: Retrieve the Typebot for a specific instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /typebot/changeStatus/{instanceName}: - post: - tags: - - Typebot - summary: Change the status of the Typebot for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - remoteJid: - type: string - description: "The Typebot RemoteJid." - status: - type: string - description: "The Typebot Status." - enum: ["opened", "paused", "closed"] - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /proxy/set/{instanceName}: - post: - tags: - - Proxy - summary: Set up or modify the Proxy for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - description: "Indicates whether to enable the Proxy integration." - proxy: - type: string - description: "The Proxy URI." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /proxy/find/{instanceName}: - get: - tags: - - Proxy - summary: Retrieve the Proxy for a specific instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /chamaai/set/{instanceName}: - post: - tags: - - Chama AI - summary: Set up or modify the Chama AI for an instance. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - enabled: - type: boolean - description: "Indicates whether to enable the Chamai AI integration." - url: - type: string - description: "The Chamai AI URL." - token: - type: string - description: "The Chamai AI Token." - waNumber: - type: string - description: "The Chamai AI WhatsApp Number." - answerByAudio: - type: boolean - description: "Indicates whether to answer by audio." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chamaai/find/{instanceName}: - get: - tags: - - Chama AI - summary: Retrieve the Chama AI for a specific instance. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - - /chat/fetchBusinessProfile/{instanceName}: - post: - tags: - - Profile Settings - summary: Fetch the business profile of a specific contact. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: "- required - The phone number of the contact." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/fetchProfile/{instanceName}: - post: - tags: - - Profile Settings - summary: Fetch the profile of a specific contact. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - number: - type: string - description: "- required - The phone number of the contact." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/updateProfileName/{instanceName}: - post: - tags: - - Profile Settings - summary: Update the name of a specific contact. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - name: - type: string - description: "- required - The new name of the contact." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/updateProfileStatus/{instanceName}: - post: - tags: - - Profile Settings - summary: Update the status of a specific contact. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - status: - type: string - description: "- required - The new status of the contact." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/updateProfilePicture/{instanceName}: - put: - tags: - - Profile Settings - summary: Update the profile picture of a specific contact. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - picture: - type: string - description: "- required - The new profile picture of the contact." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/removeProfilePicture/{instanceName}: - delete: - tags: - - Profile Settings - summary: Remove the profile picture of a specific contact. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/fetchPrivacySettings/{instanceName}: - get: - tags: - - Profile Settings - summary: Fetch the privacy settings of a specific contact. - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} - /chat/updatePrivacySettings/{instanceName}: - put: - tags: - - Profile Settings - summary: Update the privacy settings of a specific contact. - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - privacySettings: - type: object - description: "- required - The new privacy settings of the contact." - properties: - readreceipts: - type: string - enum: ["all", "none"] - description: "- required - The new read receipts privacy setting of the contact." - profile: - type: string - enum: ["all", "contacts", "contact_blacklist", "none"] - description: "- required - The new profile privacy setting of the contact." - status: - type: string - enum: ["all", "contacts", "contact_blacklist", "none"] - description: "- required - The new status privacy setting of the contact." - online: - type: string - enum: ["all", "match_last_seen"] - description: "- required - The new online privacy setting of the contact." - last: - type: string - enum: ["all", "contacts", "contact_blacklist", "none"] - description: "- required - The new last seen privacy setting of the contact." - groupadd: - type: string - enum: ["all", "contacts", "contact_blacklist", "none"] - description: "- required - The new group add privacy setting of the contact." - parameters: - - name: instanceName - in: path - schema: - type: string - required: true - description: "- required" - example: "evolution" - responses: - "200": - description: Successful response - content: - application/json: {} diff --git a/src/libs/db.connect.ts b/src/libs/mongodb.connect.ts similarity index 80% rename from src/libs/db.connect.ts rename to src/libs/mongodb.connect.ts index b11610c7..539b4d96 100644 --- a/src/libs/db.connect.ts +++ b/src/libs/mongodb.connect.ts @@ -6,8 +6,8 @@ import { Logger } from '../config/logger.config'; const logger = new Logger('MongoDB'); const db = configService.get('DATABASE'); -export const dbserver = (() => { - if (db.ENABLED) { +export const mongodbServer = (() => { + if (db.ENABLED && db.PROVIDER === 'mongodb') { logger.verbose('connecting'); const dbs = mongoose.createConnection(db.CONNECTION.URI, { dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api', @@ -17,7 +17,7 @@ export const dbserver = (() => { process.on('beforeExit', () => { logger.verbose('instance destroyed'); - dbserver.destroy(true, (error) => logger.error(error)); + mongodbServer.destroy(true, (error) => logger.error(error)); }); return dbs; diff --git a/src/libs/prisma.connect.ts b/src/libs/prisma.connect.ts new file mode 100644 index 00000000..0ac126bd --- /dev/null +++ b/src/libs/prisma.connect.ts @@ -0,0 +1,23 @@ +import { PrismaClient } from '@prisma/client'; + +import { configService, Database } from '../config/env.config'; +import { Logger } from '../config/logger.config'; + +const logger = new Logger('MongoDB'); + +const db = configService.get('DATABASE'); +export const prismaServer = (() => { + if (db.ENABLED && db.PROVIDER !== 'mongodb') { + logger.verbose('connecting'); + const db = new PrismaClient(); + + logger.verbose('connected in ' + db.$connect); + + process.on('beforeExit', () => { + logger.verbose('instance destroyed'); + db.$disconnect(); + }); + + return db; + } +})(); diff --git a/src/main.ts b/src/main.ts index 2cc9e280..dd7322b0 100644 --- a/src/main.ts +++ b/src/main.ts @@ -16,7 +16,6 @@ import { Auth, configService, Cors, HttpServer, Rabbitmq, Sqs, Webhook } from '. import { onUnexpectedError } from './config/error.config'; import { Logger } from './config/logger.config'; import { ROOT_DIR } from './config/path.config'; -import { swaggerRouter } from './docs/swagger.conf'; import { ServerUP } from './utils/server-up'; function initWA() { @@ -59,8 +58,6 @@ async function bootstrap() { app.use('/', router); - if (!configService.get('SERVER').DISABLE_DOCS) app.use(swaggerRouter); - app.use( (err: Error, req: Request, res: Response, next: NextFunction) => { if (err) { diff --git a/src/utils/use-multi-file-auth-state-db.ts b/src/utils/use-multi-file-auth-state-mongodb.ts similarity index 92% rename from src/utils/use-multi-file-auth-state-db.ts rename to src/utils/use-multi-file-auth-state-mongodb.ts index 995ac92a..2bd2289b 100644 --- a/src/utils/use-multi-file-auth-state-db.ts +++ b/src/utils/use-multi-file-auth-state-mongodb.ts @@ -9,14 +9,14 @@ import { import { configService, Database } from '../config/env.config'; import { Logger } from '../config/logger.config'; -import { dbserver } from '../libs/db.connect'; +import { mongodbServer } from '../libs/mongodb.connect'; -export async function useMultiFileAuthStateDb( +export async function useMultiFileAuthStateMongoDb( coll: string, ): Promise<{ state: AuthenticationState; saveCreds: () => Promise }> { - const logger = new Logger(useMultiFileAuthStateDb.name); + const logger = new Logger(useMultiFileAuthStateMongoDb.name); - const client = dbserver.getClient(); + const client = mongodbServer.getClient(); const collection = client .db(configService.get('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances') diff --git a/src/utils/use-multi-file-auth-state-prisma.ts b/src/utils/use-multi-file-auth-state-prisma.ts new file mode 100644 index 00000000..39170696 --- /dev/null +++ b/src/utils/use-multi-file-auth-state-prisma.ts @@ -0,0 +1,158 @@ +import { PrismaClient } from '@prisma/client'; +import { BufferJSON, initAuthCreds, WAProto as proto } from '@whiskeysockets/baileys'; +import fs from 'fs/promises'; +import path from 'path'; + +const prisma = new PrismaClient(); + +const fixFileName = (file) => { + if (!file) { + return undefined; + } + const replacedSlash = file.replace(/\//g, '__'); + const replacedColon = replacedSlash.replace(/:/g, '-'); + return replacedColon; +}; + +export async function keyExists(sessionId) { + try { + const key = await prisma.session.findUnique({ where: { sessionId: sessionId } }); + return !!key; + } catch (error) { + console.log(`${error}`); + return false; + } +} + +export async function saveKey(sessionId, keyJson) { + const jaExiste = await keyExists(sessionId); + try { + if (!jaExiste) + return await prisma.session.create({ data: { sessionId: sessionId, creds: JSON.stringify(keyJson) } as any }); + await prisma.session.update({ where: { sessionId: sessionId }, data: { creds: JSON.stringify(keyJson) } }); + } catch (error) { + console.log(`${error}`); + return null; + } +} + +export async function getAuthKey(sessionId) { + try { + const registro = await keyExists(sessionId); + if (!registro) return null; + const auth = await prisma.session.findUnique({ where: { sessionId: sessionId } }); + return JSON.parse(auth?.creds); + } catch (error) { + console.log(`${error}`); + return null; + } +} + +async function deleteAuthKey(sessionId) { + try { + const registro = await keyExists(sessionId); + if (!registro) return; + await prisma.session.delete({ where: { sessionId: sessionId } }); + } catch (error) { + console.log('2', `${error}`); + } +} + +async function fileExists(file) { + try { + const stat = await fs.stat(file); + if (stat.isFile()) return true; + } catch (error) { + return; + } +} + +export default async function useMultiFileAuthStatePrisma(sessionId) { + const localFolder = path.join(process.cwd(), 'sessions', sessionId); + const localFile = (key) => path.join(localFolder, fixFileName(key) + '.json'); + await fs.mkdir(localFolder, { recursive: true }); + + async function writeData(data, key) { + const dataString = JSON.stringify(data, BufferJSON.replacer); + + if (key != 'creds') { + await fs.writeFile(localFile(key), dataString); + return; + } + await saveKey(sessionId, dataString); + return; + } + + async function readData(key) { + try { + let rawData; + + if (key != 'creds') { + if (!(await fileExists(localFile(key)))) return null; + rawData = await fs.readFile(localFile(key), { encoding: 'utf-8' }); + } else { + rawData = await getAuthKey(sessionId); + } + + const parsedData = JSON.parse(rawData, BufferJSON.reviver); + return parsedData; + } catch (error) { + return null; + } + } + + async function removeData(key) { + try { + if (key != 'creds') { + await fs.unlink(localFile(key)); + } else { + await deleteAuthKey(sessionId); + } + } catch (error) { + return; + } + } + + let creds = await readData('creds'); + if (!creds) { + creds = initAuthCreds(); + await writeData(creds, 'creds'); + } + + return { + state: { + creds, + keys: { + get: async (type, ids) => { + const data = {}; + await Promise.all( + ids.map(async (id) => { + let value = await readData(`${type}-${id}`); + if (type === 'app-state-sync-key' && value) { + value = proto.Message.AppStateSyncKeyData.fromObject(value); + } + + data[id] = value; + }), + ); + return data; + }, + set: async (data) => { + const tasks = []; + for (const category in data) { + for (const id in data[category]) { + const value = data[category][id]; + const key = `${category}-${id}`; + + tasks.push(value ? writeData(value, key) : removeData(key)); + } + } + await Promise.all(tasks); + }, + }, + }, + saveCreds: () => { + return writeData(creds, 'creds'); + }, + }; +} diff --git a/src/validate/validate.schema.ts b/src/validate/validate.schema.ts index 8f7cb1a0..709ba325 100644 --- a/src/validate/validate.schema.ts +++ b/src/validate/validate.schema.ts @@ -2,7 +2,6 @@ import { JSONSchema7, JSONSchema7Definition } from 'json-schema'; import { v4 } from 'uuid'; // Integrations Schema -export * from '../api/integrations/chamaai/validate/chamaai.schema'; export * from '../api/integrations/chatwoot/validate/chatwoot.schema'; export * from '../api/integrations/rabbitmq/validate/rabbitmq.schema'; export * from '../api/integrations/sqs/validate/sqs.schema'; @@ -63,10 +62,8 @@ export const instanceNameSchema: JSONSchema7 = { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ], }, }, @@ -979,10 +976,8 @@ export const webhookSchema: JSONSchema7 = { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ], }, }, @@ -1040,10 +1035,8 @@ export const websocketSchema: JSONSchema7 = { 'LABELS_EDIT', 'LABELS_ASSOCIATION', 'CALL', - 'NEW_JWT_TOKEN', 'TYPEBOT_START', 'TYPEBOT_CHANGE_STATUS', - 'CHAMA_AI_ACTION', ], }, },