mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-12-20 04:12:23 -06:00
refactor: event folder
This commit is contained in:
@@ -1,53 +1,271 @@
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { EventController } from '@api/controllers/event.controller';
|
||||
import { RabbitmqDto } from '@api/integrations/event/rabbitmq/dto/rabbitmq.dto';
|
||||
import { RabbitmqService } from '@api/integrations/event/rabbitmq/services/rabbitmq.service';
|
||||
import { configService, Rabbitmq } from '@config/env.config';
|
||||
import { BadRequestException } from '@exceptions';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
import { configService, Log, Rabbitmq } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { NotFoundException } from '@exceptions';
|
||||
import * as amqp from 'amqplib/callback_api';
|
||||
|
||||
export class RabbitmqController {
|
||||
constructor(private readonly rabbitmqService: RabbitmqService) {}
|
||||
export class RabbitmqController extends EventController {
|
||||
public amqpChannel: amqp.Channel | null = null;
|
||||
private readonly logger = new Logger(RabbitmqController.name);
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
public async createRabbitmq(instance: InstanceDto, data: RabbitmqDto) {
|
||||
if (!configService.get<Rabbitmq>('RABBITMQ').ENABLED) throw new BadRequestException('Rabbitmq is disabled');
|
||||
public init(): void {
|
||||
if (!configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
|
||||
amqp.connect(uri, (error, connection) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
connection.createChannel((channelError, channel) => {
|
||||
if (channelError) {
|
||||
reject(channelError);
|
||||
return;
|
||||
}
|
||||
|
||||
const exchangeName = 'evolution_exchange';
|
||||
|
||||
channel.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
this.amqpChannel = channel;
|
||||
|
||||
this.logger.info('AMQP initialized');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
}).then(() => {
|
||||
if (configService.get<Rabbitmq>('RABBITMQ')?.GLOBAL_ENABLED) this.initGlobalQueues();
|
||||
});
|
||||
}
|
||||
|
||||
private set channel(channel: amqp.Channel) {
|
||||
this.amqpChannel = channel;
|
||||
}
|
||||
|
||||
public get channel(): amqp.Channel {
|
||||
return this.amqpChannel;
|
||||
}
|
||||
|
||||
public async set(instanceName: string, data: RabbitmqDto): Promise<wa.LocalRabbitmq> {
|
||||
if (!data.enabled) {
|
||||
data.events = [];
|
||||
} else {
|
||||
if (0 === data.events.length) {
|
||||
data.events = this.events;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.events.length === 0) {
|
||||
data.events = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_EDITED',
|
||||
'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',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
];
|
||||
}
|
||||
try {
|
||||
await this.get(instanceName);
|
||||
|
||||
return this.rabbitmqService.create(instance, data);
|
||||
return this.prisma.rabbitmq.update({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
} catch (err) {
|
||||
return this.prisma.rabbitmq.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
events: data.events,
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async findRabbitmq(instance: InstanceDto) {
|
||||
return this.rabbitmqService.find(instance);
|
||||
public async get(instanceName: string): Promise<wa.LocalWebsocket> {
|
||||
if (undefined === this.monitor.waInstances[instanceName]) {
|
||||
throw new NotFoundException('Instance not found');
|
||||
}
|
||||
|
||||
const data = await this.prisma.rabbitmq.findUnique({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
throw new NotFoundException('Rabbitmq not found');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async emit({
|
||||
instanceName,
|
||||
origin,
|
||||
event,
|
||||
data,
|
||||
serverUrl,
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
}: {
|
||||
instanceName: string;
|
||||
origin: string;
|
||||
event: string;
|
||||
data: Object;
|
||||
serverUrl: string;
|
||||
dateTime: string;
|
||||
sender: string;
|
||||
apiKey?: string;
|
||||
}): Promise<void> {
|
||||
if (!configService.get<Rabbitmq>('RABBITMQ')?.ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceRabbitmq = await this.get(instanceName);
|
||||
const rabbitmqLocal = instanceRabbitmq.events;
|
||||
const rabbitmqGlobal = configService.get<Rabbitmq>('RABBITMQ').GLOBAL_ENABLED;
|
||||
const rabbitmqEvents = configService.get<Rabbitmq>('RABBITMQ').EVENTS;
|
||||
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||
const logEnabled = configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS');
|
||||
|
||||
const message = {
|
||||
event,
|
||||
instance: instanceName,
|
||||
data,
|
||||
server_url: serverUrl,
|
||||
date_time: dateTime,
|
||||
sender,
|
||||
apikey: apiKey,
|
||||
};
|
||||
|
||||
if (instanceRabbitmq.enabled && this.amqpChannel) {
|
||||
if (Array.isArray(rabbitmqLocal) && rabbitmqLocal.includes(we)) {
|
||||
const exchangeName = instanceName ?? 'evolution_exchange';
|
||||
|
||||
let retry = 0;
|
||||
|
||||
while (retry < 3) {
|
||||
try {
|
||||
await this.amqpChannel.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
const eventName = event.replace(/_/g, '.').toLowerCase();
|
||||
|
||||
const queueName = `${instanceName}.${eventName}`;
|
||||
|
||||
await this.amqpChannel.assertQueue(queueName, {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
arguments: {
|
||||
'x-queue-type': 'quorum',
|
||||
},
|
||||
});
|
||||
|
||||
await this.amqpChannel.bindQueue(queueName, exchangeName, eventName);
|
||||
|
||||
await this.amqpChannel.publish(exchangeName, event, Buffer.from(JSON.stringify(message)));
|
||||
|
||||
if (logEnabled) {
|
||||
const logData = {
|
||||
local: `${origin}.sendData-RabbitMQ`,
|
||||
...message,
|
||||
};
|
||||
|
||||
this.logger.log(logData);
|
||||
}
|
||||
break;
|
||||
} catch (error) {
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (rabbitmqGlobal && rabbitmqEvents[we] && this.amqpChannel) {
|
||||
const exchangeName = 'evolution_exchange';
|
||||
|
||||
let retry = 0;
|
||||
|
||||
while (retry < 3) {
|
||||
try {
|
||||
await this.amqpChannel.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
const queueName = event;
|
||||
|
||||
await this.amqpChannel.assertQueue(queueName, {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
arguments: {
|
||||
'x-queue-type': 'quorum',
|
||||
},
|
||||
});
|
||||
|
||||
await this.amqpChannel.bindQueue(queueName, exchangeName, event);
|
||||
|
||||
await this.amqpChannel.publish(exchangeName, event, Buffer.from(JSON.stringify(message)));
|
||||
|
||||
if (logEnabled) {
|
||||
const logData = {
|
||||
local: `${origin}.sendData-RabbitMQ-Global`,
|
||||
...message,
|
||||
};
|
||||
|
||||
this.logger.log(logData);
|
||||
}
|
||||
|
||||
break;
|
||||
} catch (error) {
|
||||
retry++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async initGlobalQueues(): Promise<void> {
|
||||
this.logger.info('Initializing global queues');
|
||||
const events = configService.get<Rabbitmq>('RABBITMQ').EVENTS;
|
||||
|
||||
if (!events) {
|
||||
this.logger.warn('No events to initialize on AMQP');
|
||||
return;
|
||||
}
|
||||
|
||||
const eventKeys = Object.keys(events);
|
||||
|
||||
eventKeys.forEach((event) => {
|
||||
if (events[event] === false) return;
|
||||
|
||||
const queueName = `${event.replace(/_/g, '.').toLowerCase()}`;
|
||||
const exchangeName = 'evolution_exchange';
|
||||
|
||||
this.amqpChannel.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
this.amqpChannel.assertQueue(queueName, {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
arguments: {
|
||||
'x-queue-type': 'quorum',
|
||||
},
|
||||
});
|
||||
|
||||
this.amqpChannel.bindQueue(queueName, exchangeName, event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Constructor } from '@api/dto/integration.dto';
|
||||
|
||||
export class RabbitmqDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
export function RabbitMQInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
rabbitmqEnabled?: boolean;
|
||||
rabbitmqEvents?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,137 +0,0 @@
|
||||
import { configService, Rabbitmq } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { JsonValue } from '@prisma/client/runtime/library';
|
||||
import * as amqp from 'amqplib/callback_api';
|
||||
|
||||
const logger = new Logger('AMQP');
|
||||
|
||||
let amqpChannel: amqp.Channel | null = null;
|
||||
|
||||
export const initAMQP = () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
|
||||
amqp.connect(uri, (error, connection) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
connection.createChannel((channelError, channel) => {
|
||||
if (channelError) {
|
||||
reject(channelError);
|
||||
return;
|
||||
}
|
||||
|
||||
const exchangeName = configService.get<Rabbitmq>('RABBITMQ').EXCHANGE_NAME || 'evolution_exchange';
|
||||
|
||||
channel.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
amqpChannel = channel;
|
||||
|
||||
logger.info('AMQP initialized');
|
||||
resolve();
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const getAMQP = (): amqp.Channel | null => {
|
||||
return amqpChannel;
|
||||
};
|
||||
|
||||
export const initGlobalQueues = () => {
|
||||
logger.info('Initializing global queues');
|
||||
const events = configService.get<Rabbitmq>('RABBITMQ').EVENTS;
|
||||
|
||||
if (!events) {
|
||||
logger.warn('No events to initialize on AMQP');
|
||||
return;
|
||||
}
|
||||
|
||||
const eventKeys = Object.keys(events);
|
||||
|
||||
eventKeys.forEach((event) => {
|
||||
if (events[event] === false) return;
|
||||
|
||||
const queueName = `${event.replace(/_/g, '.').toLowerCase()}`;
|
||||
const amqp = getAMQP();
|
||||
const exchangeName = configService.get<Rabbitmq>('RABBITMQ').EXCHANGE_NAME || 'evolution_exchange';
|
||||
|
||||
amqp.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
amqp.assertQueue(queueName, {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
arguments: {
|
||||
'x-queue-type': 'quorum',
|
||||
},
|
||||
});
|
||||
|
||||
amqp.bindQueue(queueName, exchangeName, event);
|
||||
});
|
||||
};
|
||||
|
||||
export const initQueues = (instanceName: string, events: string[]) => {
|
||||
if (!events || !events.length) return;
|
||||
|
||||
const queues = events.map((event) => {
|
||||
return `${event.replace(/_/g, '.').toLowerCase()}`;
|
||||
});
|
||||
|
||||
queues.forEach((event) => {
|
||||
const amqp = getAMQP();
|
||||
const exchangeName = instanceName ?? 'evolution_exchange';
|
||||
|
||||
amqp.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
const queueName = `${instanceName}.${event}`;
|
||||
|
||||
amqp.assertQueue(queueName, {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
arguments: {
|
||||
'x-queue-type': 'quorum',
|
||||
},
|
||||
});
|
||||
|
||||
amqp.bindQueue(queueName, exchangeName, event);
|
||||
});
|
||||
};
|
||||
|
||||
export const removeQueues = (instanceName: string, events: JsonValue) => {
|
||||
const eventsArray = Array.isArray(events) ? events.map((event) => String(event)) : [];
|
||||
|
||||
if (!events || !eventsArray.length) return;
|
||||
|
||||
const channel = getAMQP();
|
||||
|
||||
const queues = eventsArray.map((event) => {
|
||||
return `${event.replace(/_/g, '.').toLowerCase()}`;
|
||||
});
|
||||
|
||||
const exchangeName = instanceName ?? 'evolution_exchange';
|
||||
|
||||
queues.forEach((event) => {
|
||||
const amqp = getAMQP();
|
||||
|
||||
amqp.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
});
|
||||
|
||||
const queueName = `${instanceName}.${event}`;
|
||||
|
||||
amqp.deleteQueue(queueName);
|
||||
});
|
||||
|
||||
channel.deleteExchange(exchangeName);
|
||||
};
|
||||
@@ -15,7 +15,7 @@ export class RabbitmqRouter extends RouterBroker {
|
||||
request: req,
|
||||
schema: rabbitmqSchema,
|
||||
ClassRef: RabbitmqDto,
|
||||
execute: (instance, data) => rabbitmqController.createRabbitmq(instance, data),
|
||||
execute: (instance, data) => rabbitmqController.set(instance.instanceName, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
@@ -25,7 +25,7 @@ export class RabbitmqRouter extends RouterBroker {
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => rabbitmqController.findRabbitmq(instance),
|
||||
execute: (instance) => rabbitmqController.get(instance.instanceName),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { RabbitmqDto } from '@api/integrations/event/rabbitmq/dto/rabbitmq.dto';
|
||||
import { initQueues } from '@api/integrations/event/rabbitmq/libs/amqp.server';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { Rabbitmq } from '@prisma/client';
|
||||
|
||||
export class RabbitmqService {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
private readonly logger = new Logger('RabbitmqService');
|
||||
|
||||
public create(instance: InstanceDto, data: RabbitmqDto) {
|
||||
this.waMonitor.waInstances[instance.instanceName].setRabbitmq(data);
|
||||
|
||||
initQueues(instance.instanceName, data.events);
|
||||
return { rabbitmq: { ...instance, rabbitmq: data } };
|
||||
}
|
||||
|
||||
public async find(instance: InstanceDto): Promise<Rabbitmq> {
|
||||
try {
|
||||
const result = await this.waMonitor.waInstances[instance.instanceName].findRabbitmq();
|
||||
|
||||
if (Object.keys(result).length === 0) {
|
||||
throw new Error('Rabbitmq not found');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +1,245 @@
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { EventController } from '@api/controllers/event.controller';
|
||||
import { SqsDto } from '@api/integrations/event/sqs/dto/sqs.dto';
|
||||
import { SqsService } from '@api/integrations/event/sqs/services/sqs.service';
|
||||
import { configService, Sqs } from '@config/env.config';
|
||||
import { BadRequestException } from '@exceptions';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
import { SQS } from '@aws-sdk/client-sqs';
|
||||
import { configService, Log, Sqs } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { NotFoundException } from '@exceptions';
|
||||
|
||||
export class SqsController {
|
||||
constructor(private readonly sqsService: SqsService) {}
|
||||
export class SqsController extends EventController {
|
||||
private sqs: SQS;
|
||||
private readonly logger = new Logger(SqsController.name);
|
||||
|
||||
public async createSqs(instance: InstanceDto, data: SqsDto) {
|
||||
if (!configService.get<Sqs>('SQS').ENABLED) throw new BadRequestException('Sqs is disabled');
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
public init(): void {
|
||||
if (!configService.get<Sqs>('SQS')?.ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
new Promise<void>((resolve, reject) => {
|
||||
const awsConfig = configService.get<Sqs>('SQS');
|
||||
this.sqs = new SQS({
|
||||
credentials: {
|
||||
accessKeyId: awsConfig.ACCESS_KEY_ID,
|
||||
secretAccessKey: awsConfig.SECRET_ACCESS_KEY,
|
||||
},
|
||||
|
||||
region: awsConfig.REGION,
|
||||
});
|
||||
|
||||
this.logger.info('SQS initialized');
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
private set channel(sqs: SQS) {
|
||||
this.sqs = sqs;
|
||||
}
|
||||
|
||||
public get channel(): SQS {
|
||||
return this.sqs;
|
||||
}
|
||||
|
||||
public async set(instanceName: string, data: SqsDto): Promise<wa.LocalSqs> {
|
||||
if (!data.enabled) {
|
||||
data.events = [];
|
||||
} else {
|
||||
if (0 === data.events.length) {
|
||||
data.events = this.events;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.events.length === 0) {
|
||||
data.events = [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_EDITED',
|
||||
'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',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
];
|
||||
}
|
||||
try {
|
||||
await this.get(instanceName);
|
||||
|
||||
return this.sqsService.create(instance, data);
|
||||
return this.prisma.sqs.update({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
} catch (err) {
|
||||
return this.prisma.sqs.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
events: data.events,
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async findSqs(instance: InstanceDto) {
|
||||
return this.sqsService.find(instance);
|
||||
public async get(instanceName: string): Promise<wa.LocalSqs> {
|
||||
if (undefined === this.monitor.waInstances[instanceName]) {
|
||||
throw new NotFoundException('Instance not found');
|
||||
}
|
||||
|
||||
const data = await this.prisma.sqs.findUnique({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
throw new NotFoundException('Sqs not found');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async emit({
|
||||
instanceName,
|
||||
origin,
|
||||
event,
|
||||
data,
|
||||
serverUrl,
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
}: {
|
||||
instanceName: string;
|
||||
origin: string;
|
||||
event: string;
|
||||
data: Object;
|
||||
serverUrl: string;
|
||||
dateTime: string;
|
||||
sender: string;
|
||||
apiKey?: string;
|
||||
}): Promise<void> {
|
||||
if (!configService.get<Sqs>('SQS')?.ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceSqs = await this.get(instanceName);
|
||||
const sqsLocal = instanceSqs.events;
|
||||
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||
|
||||
if (instanceSqs.enabled) {
|
||||
if (this.sqs) {
|
||||
if (Array.isArray(sqsLocal) && sqsLocal.includes(we)) {
|
||||
const eventFormatted = `${event.replace('.', '_').toLowerCase()}`;
|
||||
|
||||
const queueName = `${instanceName}_${eventFormatted}.fifo`;
|
||||
|
||||
const sqsConfig = configService.get<Sqs>('SQS');
|
||||
|
||||
const sqsUrl = `https://sqs.${sqsConfig.REGION}.amazonaws.com/${sqsConfig.ACCOUNT_ID}/${queueName}`;
|
||||
|
||||
const message = {
|
||||
event,
|
||||
instance: instanceName,
|
||||
data,
|
||||
server_url: serverUrl,
|
||||
date_time: dateTime,
|
||||
sender,
|
||||
apikey: apiKey,
|
||||
};
|
||||
|
||||
const params = {
|
||||
MessageBody: JSON.stringify(message),
|
||||
MessageGroupId: 'evolution',
|
||||
MessageDeduplicationId: `${instanceName}_${eventFormatted}_${Date.now()}`,
|
||||
QueueUrl: sqsUrl,
|
||||
};
|
||||
|
||||
this.sqs.sendMessage(params, (err) => {
|
||||
if (err) {
|
||||
this.logger.error({
|
||||
local: `${origin}.sendData-SQS`,
|
||||
message: err?.message,
|
||||
hostName: err?.hostname,
|
||||
code: err?.code,
|
||||
stack: err?.stack,
|
||||
name: err?.name,
|
||||
url: queueName,
|
||||
server_url: serverUrl,
|
||||
});
|
||||
} else {
|
||||
if (configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS')) {
|
||||
const logData = {
|
||||
local: `${origin}.sendData-SQS`,
|
||||
...message,
|
||||
};
|
||||
|
||||
this.logger.log(logData);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async initQueues(instanceName: string, events: string[]) {
|
||||
if (!events || !events.length) return;
|
||||
|
||||
const queues = events.map((event) => {
|
||||
return `${event.replace(/_/g, '_').toLowerCase()}`;
|
||||
});
|
||||
|
||||
queues.forEach((event) => {
|
||||
const queueName = `${instanceName}_${event}.fifo`;
|
||||
|
||||
this.sqs.createQueue(
|
||||
{
|
||||
QueueName: queueName,
|
||||
Attributes: {
|
||||
FifoQueue: 'true',
|
||||
},
|
||||
},
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
this.logger.error(`Error creating queue ${queueName}: ${err.message}`);
|
||||
} else {
|
||||
this.logger.info(`Queue ${queueName} created: ${data.QueueUrl}`);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
public async removeQueues(instanceName: string, events: any) {
|
||||
const eventsArray = Array.isArray(events) ? events.map((event) => String(event)) : [];
|
||||
if (!events || !eventsArray.length) return;
|
||||
|
||||
const queues = eventsArray.map((event) => {
|
||||
return `${event.replace(/_/g, '_').toLowerCase()}`;
|
||||
});
|
||||
|
||||
queues.forEach((event) => {
|
||||
const queueName = `${instanceName}_${event}.fifo`;
|
||||
|
||||
this.sqs.getQueueUrl(
|
||||
{
|
||||
QueueName: queueName,
|
||||
},
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
this.logger.error(`Error getting queue URL for ${queueName}: ${err.message}`);
|
||||
} else {
|
||||
const queueUrl = data.QueueUrl;
|
||||
|
||||
this.sqs.deleteQueue(
|
||||
{
|
||||
QueueUrl: queueUrl,
|
||||
},
|
||||
(deleteErr) => {
|
||||
if (deleteErr) {
|
||||
this.logger.error(`Error deleting queue ${queueName}: ${deleteErr.message}`);
|
||||
} else {
|
||||
this.logger.info(`Queue ${queueName} deleted`);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Constructor } from '@api/dto/integration.dto';
|
||||
|
||||
export class SqsDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
export function SQSInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
sqsEnabled?: boolean;
|
||||
sqsEvents?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,101 +0,0 @@
|
||||
import { SQS } from '@aws-sdk/client-sqs';
|
||||
import { configService, Sqs } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { JsonValue } from '@prisma/client/runtime/library';
|
||||
|
||||
const logger = new Logger('SQS');
|
||||
|
||||
let sqs: SQS;
|
||||
|
||||
export const initSQS = () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const awsConfig = configService.get<Sqs>('SQS');
|
||||
sqs = new SQS({
|
||||
credentials: {
|
||||
accessKeyId: awsConfig.ACCESS_KEY_ID,
|
||||
secretAccessKey: awsConfig.SECRET_ACCESS_KEY,
|
||||
},
|
||||
|
||||
region: awsConfig.REGION,
|
||||
});
|
||||
|
||||
logger.info('SQS initialized');
|
||||
resolve();
|
||||
});
|
||||
};
|
||||
|
||||
export const getSQS = (): SQS => {
|
||||
return sqs;
|
||||
};
|
||||
|
||||
export const initQueues = (instanceName: string, events: string[]) => {
|
||||
if (!events || !events.length) return;
|
||||
|
||||
const queues = events.map((event) => {
|
||||
return `${event.replace(/_/g, '_').toLowerCase()}`;
|
||||
});
|
||||
|
||||
const sqs = getSQS();
|
||||
|
||||
queues.forEach((event) => {
|
||||
const queueName = `${instanceName}_${event}.fifo`;
|
||||
|
||||
sqs.createQueue(
|
||||
{
|
||||
QueueName: queueName,
|
||||
Attributes: {
|
||||
FifoQueue: 'true',
|
||||
},
|
||||
},
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
logger.error(`Error creating queue ${queueName}: ${err.message}`);
|
||||
} else {
|
||||
logger.info(`Queue ${queueName} created: ${data.QueueUrl}`);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
export const removeQueues = (instanceName: string, events: JsonValue) => {
|
||||
const eventsArray = Array.isArray(events) ? events.map((event) => String(event)) : [];
|
||||
if (!events || !eventsArray.length) return;
|
||||
|
||||
const sqs = getSQS();
|
||||
|
||||
const queues = eventsArray.map((event) => {
|
||||
return `${event.replace(/_/g, '_').toLowerCase()}`;
|
||||
});
|
||||
|
||||
queues.forEach((event) => {
|
||||
const queueName = `${instanceName}_${event}.fifo`;
|
||||
|
||||
sqs.getQueueUrl(
|
||||
{
|
||||
QueueName: queueName,
|
||||
},
|
||||
(err, data) => {
|
||||
if (err) {
|
||||
logger.error(`Error getting queue URL for ${queueName}: ${err.message}`);
|
||||
} else {
|
||||
const queueUrl = data.QueueUrl;
|
||||
|
||||
sqs.deleteQueue(
|
||||
{
|
||||
QueueUrl: queueUrl,
|
||||
},
|
||||
(deleteErr) => {
|
||||
if (deleteErr) {
|
||||
logger.error(`Error deleting queue ${queueName}: ${deleteErr.message}`);
|
||||
} else {
|
||||
logger.info(`Queue ${queueName} deleted`);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
});
|
||||
};
|
||||
@@ -15,7 +15,7 @@ export class SqsRouter extends RouterBroker {
|
||||
request: req,
|
||||
schema: sqsSchema,
|
||||
ClassRef: SqsDto,
|
||||
execute: (instance, data) => sqsController.createSqs(instance, data),
|
||||
execute: (instance, data) => sqsController.set(instance.instanceName, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
@@ -25,7 +25,7 @@ export class SqsRouter extends RouterBroker {
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => sqsController.findSqs(instance),
|
||||
execute: (instance) => sqsController.get(instance.instanceName),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { SqsDto } from '@api/integrations/event/sqs/dto/sqs.dto';
|
||||
import { initQueues } from '@api/integrations/event/sqs/libs/sqs.server';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { Sqs } from '@prisma/client';
|
||||
|
||||
export class SqsService {
|
||||
constructor(private readonly waMonitor: WAMonitoringService) {}
|
||||
|
||||
private readonly logger = new Logger('SqsService');
|
||||
|
||||
public create(instance: InstanceDto, data: SqsDto) {
|
||||
this.waMonitor.waInstances[instance.instanceName].setSqs(data);
|
||||
|
||||
initQueues(instance.instanceName, data.events);
|
||||
return { sqs: { ...instance, sqs: data } };
|
||||
}
|
||||
|
||||
public async find(instance: InstanceDto): Promise<Sqs> {
|
||||
try {
|
||||
const result = await this.waMonitor.waInstances[instance.instanceName].findSqs();
|
||||
|
||||
if (Object.keys(result).length === 0) {
|
||||
throw new Error('Sqs not found');
|
||||
}
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
import { EventController } from '@api/controllers/event.controller';
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
import { configService, Log, Webhook, Websocket } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { BadRequestException, NotFoundException } from '@exceptions';
|
||||
import axios from 'axios';
|
||||
import { isURL } from 'class-validator';
|
||||
|
||||
import { WebhookDto } from '../dto/webhook.dto';
|
||||
|
||||
export class WebhookController extends EventController {
|
||||
private readonly logger = new Logger(WebhookController.name);
|
||||
|
||||
constructor(prismaRepository: PrismaRepository, waMonitor: WAMonitoringService) {
|
||||
super(prismaRepository, waMonitor);
|
||||
}
|
||||
|
||||
public async set(instanceName: string, data: WebhookDto): Promise<wa.LocalWebHook> {
|
||||
if (!isURL(data.url, { require_tld: false })) {
|
||||
throw new BadRequestException('Invalid "url" property');
|
||||
}
|
||||
|
||||
if (!data.enabled) {
|
||||
data.events = [];
|
||||
} else {
|
||||
if (0 === data.events.length) {
|
||||
data.events = this.events;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
await this.get(instanceName);
|
||||
|
||||
return this.prisma.webhook.update({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
data,
|
||||
});
|
||||
} catch (err) {
|
||||
return this.prisma.webhook.create({
|
||||
data: {
|
||||
enabled: data.enabled,
|
||||
events: data.events,
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
url: data.url,
|
||||
webhookBase64: data.webhookBase64,
|
||||
webhookByEvents: data.webhookByEvents,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public async get(instanceName: string): Promise<wa.LocalWebHook> {
|
||||
if (undefined === this.monitor.waInstances[instanceName]) {
|
||||
throw new NotFoundException('Instance not found');
|
||||
}
|
||||
|
||||
const data = await this.prisma.webhook.findUnique({
|
||||
where: {
|
||||
instanceId: this.monitor.waInstances[instanceName].instanceId,
|
||||
},
|
||||
});
|
||||
|
||||
if (!data) {
|
||||
throw new NotFoundException('Webhook not found');
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async emit({
|
||||
instanceName,
|
||||
origin,
|
||||
event,
|
||||
data,
|
||||
serverUrl,
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
local,
|
||||
}: {
|
||||
instanceName: string;
|
||||
origin: string;
|
||||
event: string;
|
||||
data: Object;
|
||||
serverUrl: string;
|
||||
dateTime: string;
|
||||
sender: string;
|
||||
apiKey?: string;
|
||||
local?: boolean;
|
||||
}): Promise<void> {
|
||||
if (!configService.get<Websocket>('WEBSOCKET')?.ENABLED) {
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceWebhook = await this.get(instanceName);
|
||||
const webhookGlobal = configService.get<Webhook>('WEBHOOK');
|
||||
const webhookLocal = instanceWebhook.events;
|
||||
const we = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||
const transformedWe = we.replace(/_/gm, '-').toLowerCase();
|
||||
const enabledLog = configService.get<Log>('LOG').LEVEL.includes('WEBHOOKS');
|
||||
|
||||
const webhookData = {
|
||||
event,
|
||||
instance: instanceName,
|
||||
data,
|
||||
destination: instanceWebhook.url,
|
||||
date_time: dateTime,
|
||||
sender,
|
||||
server_url: serverUrl,
|
||||
apikey: apiKey,
|
||||
};
|
||||
if (local) {
|
||||
if (Array.isArray(webhookLocal) && webhookLocal.includes(we)) {
|
||||
let baseURL: string;
|
||||
|
||||
if (instanceWebhook.webhookByEvents) {
|
||||
baseURL = `${instanceWebhook.url}/${transformedWe}`;
|
||||
} else {
|
||||
baseURL = instanceWebhook.url;
|
||||
}
|
||||
|
||||
if (enabledLog) {
|
||||
const logData = {
|
||||
local: `${origin}.sendData-Webhook`,
|
||||
url: baseURL,
|
||||
...webhookData,
|
||||
};
|
||||
|
||||
this.logger.log(logData);
|
||||
}
|
||||
|
||||
try {
|
||||
if (instanceWebhook.enabled && isURL(instanceWebhook.url, { require_tld: false })) {
|
||||
const httpService = axios.create({ baseURL });
|
||||
|
||||
await httpService.post('', webhookData);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
local: `${origin}.sendData-Webhook`,
|
||||
message: error?.message,
|
||||
hostName: error?.hostname,
|
||||
syscall: error?.syscall,
|
||||
code: error?.code,
|
||||
error: error?.errno,
|
||||
stack: error?.stack,
|
||||
name: error?.name,
|
||||
url: baseURL,
|
||||
server_url: serverUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (webhookGlobal.GLOBAL?.ENABLED) {
|
||||
if (webhookGlobal.EVENTS[we]) {
|
||||
const globalWebhook = configService.get<Webhook>('WEBHOOK').GLOBAL;
|
||||
|
||||
let globalURL;
|
||||
|
||||
if (webhookGlobal.GLOBAL.WEBHOOK_BY_EVENTS) {
|
||||
globalURL = `${globalWebhook.URL}/${transformedWe}`;
|
||||
} else {
|
||||
globalURL = globalWebhook.URL;
|
||||
}
|
||||
|
||||
if (enabledLog) {
|
||||
const logData = {
|
||||
local: `${origin}.sendData-Webhook-Global`,
|
||||
url: globalURL,
|
||||
...webhookData,
|
||||
};
|
||||
|
||||
this.logger.log(logData);
|
||||
}
|
||||
|
||||
try {
|
||||
if (globalWebhook && globalWebhook?.ENABLED && isURL(globalURL)) {
|
||||
const httpService = axios.create({ baseURL: globalURL });
|
||||
|
||||
await httpService.post('', webhookData);
|
||||
}
|
||||
} catch (error) {
|
||||
this.logger.error({
|
||||
local: `${origin}.sendData-Webhook-Global`,
|
||||
message: error?.message,
|
||||
hostName: error?.hostname,
|
||||
syscall: error?.syscall,
|
||||
code: error?.code,
|
||||
error: error?.errno,
|
||||
stack: error?.stack,
|
||||
name: error?.name,
|
||||
url: globalURL,
|
||||
server_url: serverUrl,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public async receiveWebhook(data: any) {
|
||||
if (data.object === 'whatsapp_business_account') {
|
||||
if (data.entry[0]?.changes[0]?.field === 'message_template_status_update') {
|
||||
const template = await this.prismaRepository.template.findFirst({
|
||||
where: { templateId: `${data.entry[0].changes[0].value.message_template_id}` },
|
||||
});
|
||||
|
||||
if (!template) {
|
||||
console.log('template not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const { webhookUrl } = template;
|
||||
|
||||
await axios.post(webhookUrl, data.entry[0].changes[0].value, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
data.entry?.forEach(async (entry: any) => {
|
||||
const numberId = entry.changes[0].value.metadata.phone_number_id;
|
||||
|
||||
if (!numberId) {
|
||||
this.logger.error('WebhookService -> receiveWebhook -> numberId not found');
|
||||
return;
|
||||
}
|
||||
|
||||
const instance = await this.prismaRepository.instance.findFirst({
|
||||
where: { number: numberId },
|
||||
});
|
||||
|
||||
if (!instance) {
|
||||
this.logger.error('WebhookService -> receiveWebhook -> instance not found');
|
||||
return;
|
||||
}
|
||||
|
||||
await this.waMonitor.waInstances[instance.name].connectToWhatsapp(data);
|
||||
|
||||
return;
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
18
src/api/integrations/event/webhook/dto/webhook.dto.ts
Normal file
18
src/api/integrations/event/webhook/dto/webhook.dto.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { Constructor } from '@api/dto/integration.dto';
|
||||
|
||||
export class WebhookDto {
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
events?: string[];
|
||||
webhookByEvents?: boolean;
|
||||
webhookBase64?: boolean;
|
||||
}
|
||||
|
||||
export function WebhookInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
webhookUrl?: string;
|
||||
webhookByEvents?: boolean;
|
||||
webhookBase64?: boolean;
|
||||
webhookEvents?: string[];
|
||||
};
|
||||
}
|
||||
38
src/api/integrations/event/webhook/routes/webhook.router.ts
Normal file
38
src/api/integrations/event/webhook/routes/webhook.router.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { RouterBroker } from '@api/abstract/abstract.router';
|
||||
import { InstanceDto } from '@api/dto/instance.dto';
|
||||
import { HttpStatus } from '@api/routes/index.router';
|
||||
import { webhookController } from '@api/server.module';
|
||||
import { ConfigService } from '@config/env.config';
|
||||
import { instanceSchema, webhookSchema } from '@validate/validate.schema';
|
||||
import { RequestHandler, Router } from 'express';
|
||||
|
||||
import { WebhookDto } from '../dto/webhook.dto';
|
||||
|
||||
export class WebhookRouter extends RouterBroker {
|
||||
constructor(readonly configService: ConfigService, ...guards: RequestHandler[]) {
|
||||
super();
|
||||
this.router
|
||||
.post(this.routerPath('set'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<WebhookDto>({
|
||||
request: req,
|
||||
schema: webhookSchema,
|
||||
ClassRef: WebhookDto,
|
||||
execute: (instance, data) => webhookController.set(instance.instanceName, data),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.CREATED).json(response);
|
||||
})
|
||||
.get(this.routerPath('find'), ...guards, async (req, res) => {
|
||||
const response = await this.dataValidate<InstanceDto>({
|
||||
request: req,
|
||||
schema: instanceSchema,
|
||||
ClassRef: InstanceDto,
|
||||
execute: (instance) => webhookController.get(instance.instanceName),
|
||||
});
|
||||
|
||||
res.status(HttpStatus.OK).json(response);
|
||||
});
|
||||
}
|
||||
|
||||
public readonly router: Router = Router();
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
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 websocketSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
properties: {
|
||||
enabled: { type: 'boolean', enum: [true, false] },
|
||||
events: {
|
||||
type: 'array',
|
||||
minItems: 0,
|
||||
items: {
|
||||
type: 'string',
|
||||
enum: [
|
||||
'APPLICATION_STARTUP',
|
||||
'QRCODE_UPDATED',
|
||||
'MESSAGES_SET',
|
||||
'MESSAGES_UPSERT',
|
||||
'MESSAGES_EDITED',
|
||||
'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',
|
||||
'TYPEBOT_START',
|
||||
'TYPEBOT_CHANGE_STATUS',
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
required: ['enabled'],
|
||||
...isNotEmpty('enabled'),
|
||||
};
|
||||
@@ -3,7 +3,7 @@ import { WebsocketDto } from '@api/integrations/event/websocket/dto/websocket.dt
|
||||
import { PrismaRepository } from '@api/repository/repository.service';
|
||||
import { WAMonitoringService } from '@api/services/monitor.service';
|
||||
import { wa } from '@api/types/wa.types';
|
||||
import { configService, Cors, HttpServer, Log, Websocket } from '@config/env.config';
|
||||
import { configService, Cors, Log, Websocket } from '@config/env.config';
|
||||
import { Logger } from '@config/logger.config';
|
||||
import { NotFoundException } from '@exceptions';
|
||||
import { Server } from 'http';
|
||||
@@ -109,11 +109,19 @@ export class WebsocketController extends EventController {
|
||||
origin,
|
||||
event,
|
||||
data,
|
||||
serverUrl,
|
||||
dateTime,
|
||||
sender,
|
||||
apiKey,
|
||||
}: {
|
||||
instanceName: string;
|
||||
origin: string;
|
||||
event: string;
|
||||
data: Object;
|
||||
serverUrl: string;
|
||||
dateTime: string;
|
||||
sender: string;
|
||||
apiKey?: string;
|
||||
}): Promise<void> {
|
||||
if (!configService.get<Websocket>('WEBSOCKET')?.ENABLED) {
|
||||
return;
|
||||
@@ -121,14 +129,14 @@ export class WebsocketController extends EventController {
|
||||
|
||||
const configEv = event.replace(/[.-]/gm, '_').toUpperCase();
|
||||
const logEnabled = configService.get<Log>('LOG').LEVEL.includes('WEBSOCKET');
|
||||
const serverUrl = configService.get<HttpServer>('SERVER').URL;
|
||||
const date = new Date(Date.now() - new Date().getTimezoneOffset() * 60000).toISOString();
|
||||
const message = {
|
||||
event,
|
||||
instanceName,
|
||||
instance: instanceName,
|
||||
data,
|
||||
serverUrl,
|
||||
date,
|
||||
server_url: serverUrl,
|
||||
date_time: dateTime,
|
||||
sender,
|
||||
apikey: apiKey,
|
||||
};
|
||||
|
||||
if (configService.get<Websocket>('WEBSOCKET')?.GLOBAL_EVENTS) {
|
||||
|
||||
@@ -1,4 +1,13 @@
|
||||
import { Constructor } from '@api/dto/integration.dto';
|
||||
|
||||
export class WebsocketDto {
|
||||
enabled: boolean;
|
||||
events?: string[];
|
||||
}
|
||||
|
||||
export function WebsocketInstanceMixin<TBase extends Constructor>(Base: TBase) {
|
||||
return class extends Base {
|
||||
websocketEnabled?: boolean;
|
||||
websocketEvents?: string[];
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user