refactor: event folder

This commit is contained in:
Davidson Gomes
2024-08-20 15:27:32 -03:00
parent 64ed0faa83
commit 7b79591e42
35 changed files with 1100 additions and 1539 deletions

View File

@@ -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);
});
}
}

View File

@@ -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[];
};
}

View File

@@ -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);
};

View File

@@ -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);

View File

@@ -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;
}
}
}