fix: Improved how Redis works for instances

This commit is contained in:
Davidson Gomes 2023-07-04 12:38:29 -03:00
parent 8cb431ad40
commit c4f39ab85c
16 changed files with 65 additions and 55 deletions

View File

@ -6,6 +6,7 @@
* Correction in decryption of poll votes * Correction in decryption of poll votes
* Change in the way the api sent and saved the sent messages, now it goes in the messages.upsert event * Change in the way the api sent and saved the sent messages, now it goes in the messages.upsert event
* Fixed cash when sending stickers via url * Fixed cash when sending stickers via url
* Improved how Redis works for instances
# 1.1.2 (2023-06-28 13:43) # 1.1.2 (2023-06-28 13:43)

View File

@ -54,7 +54,6 @@ ENV WEBHOOK_EVENTS_QRCODE_UPDATED=$WEBHOOK_EVENTS_QRCODE_UPDATED
ENV WEBHOOK_EVENTS_MESSAGES_SET=$WEBHOOK_EVENTS_MESSAGES_SET ENV WEBHOOK_EVENTS_MESSAGES_SET=$WEBHOOK_EVENTS_MESSAGES_SET
ENV WEBHOOK_EVENTS_MESSAGES_UPDATE=$WEBHOOK_EVENTS_MESSAGES_UPDATE ENV WEBHOOK_EVENTS_MESSAGES_UPDATE=$WEBHOOK_EVENTS_MESSAGES_UPDATE
ENV WEBHOOK_EVENTS_MESSAGES_UPSERT=$WEBHOOK_EVENTS_MESSAGES_UPSERT ENV WEBHOOK_EVENTS_MESSAGES_UPSERT=$WEBHOOK_EVENTS_MESSAGES_UPSERT
ENV WEBHOOK_EVENTS_SEND_MESSAGE=$WEBHOOK_EVENTS_SEND_MESSAGE
ENV WEBHOOK_EVENTS_CONTACTS_SET=$WEBHOOK_EVENTS_CONTACTS_SET ENV WEBHOOK_EVENTS_CONTACTS_SET=$WEBHOOK_EVENTS_CONTACTS_SET
ENV WEBHOOK_EVENTS_CONTACTS_UPSERT=$WEBHOOK_EVENTS_CONTACTS_UPSERT ENV WEBHOOK_EVENTS_CONTACTS_UPSERT=$WEBHOOK_EVENTS_CONTACTS_UPSERT
ENV WEBHOOK_EVENTS_CONTACTS_UPDATE=$WEBHOOK_EVENTS_CONTACTS_UPDATE ENV WEBHOOK_EVENTS_CONTACTS_UPDATE=$WEBHOOK_EVENTS_CONTACTS_UPDATE

View File

@ -55,7 +55,6 @@ services:
- WEBHOOK_EVENTS_MESSAGES_SET=true - WEBHOOK_EVENTS_MESSAGES_SET=true
- WEBHOOK_EVENTS_MESSAGES_UPSERT=true - WEBHOOK_EVENTS_MESSAGES_UPSERT=true
- WEBHOOK_EVENTS_MESSAGES_UPDATE=true - WEBHOOK_EVENTS_MESSAGES_UPDATE=true
- WEBHOOK_EVENTS_SEND_MESSAGE=true
- WEBHOOK_EVENTS_CONTACTS_SET=true - WEBHOOK_EVENTS_CONTACTS_SET=true
- WEBHOOK_EVENTS_CONTACTS_UPSERT=true - WEBHOOK_EVENTS_CONTACTS_UPSERT=true
- WEBHOOK_EVENTS_CONTACTS_UPDATE=true - WEBHOOK_EVENTS_CONTACTS_UPDATE=true

View File

@ -65,7 +65,6 @@ export type EventsWebhook = {
MESSAGES_SET: boolean; MESSAGES_SET: boolean;
MESSAGES_UPSERT: boolean; MESSAGES_UPSERT: boolean;
MESSAGES_UPDATE: boolean; MESSAGES_UPDATE: boolean;
SEND_MESSAGE: boolean;
CONTACTS_SET: boolean; CONTACTS_SET: boolean;
CONTACTS_UPDATE: boolean; CONTACTS_UPDATE: boolean;
CONTACTS_UPSERT: boolean; CONTACTS_UPSERT: boolean;
@ -221,7 +220,6 @@ export class ConfigService {
MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true', MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true',
MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true', MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === 'true',
MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true', MESSAGES_UPDATE: process.env?.WEBHOOK_EVENTS_MESSAGES_UPDATE === 'true',
SEND_MESSAGE: process.env?.WEBHOOK_EVENTS_SEND_MESSAGE === 'true',
CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true', CONTACTS_SET: process.env?.WEBHOOK_EVENTS_CONTACTS_SET === 'true',
CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true', CONTACTS_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true',
CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true', CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true',

View File

@ -5,10 +5,16 @@ import { Logger } from '../config/logger.config';
const logger = new Logger('Db Connection'); const logger = new Logger('Db Connection');
const db = configService.get<Database>('DATABASE'); const db = configService.get<Database>('DATABASE');
export const dbserver = db.ENABLED export const dbserver = (() => {
? mongoose.createConnection(db.CONNECTION.URI, { if (db.ENABLED) {
const dbs = mongoose.createConnection(db.CONNECTION.URI, {
dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api', dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api',
}) });
: null; logger.info('ON - dbName: ' + dbs['$dbName']);
process.on('beforeExit', () => {
dbserver.destroy(true, (error) => logger.error(error));
});
db.ENABLED ? logger.info('ON - dbName: ' + dbserver['$dbName']) : null; return dbs;
}
})();

View File

@ -4,16 +4,29 @@ import { BufferJSON } from '@whiskeysockets/baileys';
import { Redis } from '../config/env.config'; import { Redis } from '../config/env.config';
export class RedisCache { export class RedisCache {
constructor(private readonly redisEnv: Partial<Redis>, private instanceName?: string) { constructor() {
this.client = createClient({ url: this.redisEnv.URI }); process.on('beforeExit', async () => {
if (this.statusConnection) {
this.client.connect(); await this.client.disconnect();
}
});
} }
private statusConnection = false;
private instanceName: string;
private redisEnv: Redis;
public set reference(reference: string) { public set reference(reference: string) {
this.instanceName = reference; this.instanceName = reference;
} }
public async connect(redisEnv: Redis) {
this.client = createClient({ url: redisEnv.URI });
await this.client.connect();
this.statusConnection = true;
this.redisEnv = redisEnv;
}
private readonly logger = new Logger(RedisCache.name); private readonly logger = new Logger(RedisCache.name);
private client: RedisClientType; private client: RedisClientType;
@ -35,6 +48,7 @@ export class RedisCache {
public async writeData(field: string, data: any) { public async writeData(field: string, data: any) {
try { try {
const json = JSON.stringify(data, BufferJSON.replacer); const json = JSON.stringify(data, BufferJSON.replacer);
return await this.client.hSet( return await this.client.hSet(
this.redisEnv.PREFIX_KEY + ':' + this.instanceName, this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
field, field,
@ -51,6 +65,7 @@ export class RedisCache {
this.redisEnv.PREFIX_KEY + ':' + this.instanceName, this.redisEnv.PREFIX_KEY + ':' + this.instanceName,
field, field,
); );
if (data) { if (data) {
return JSON.parse(data, BufferJSON.reviver); return JSON.parse(data, BufferJSON.reviver);
} }
@ -79,20 +94,4 @@ export class RedisCache {
this.logger.error(error); this.logger.error(error);
} }
} }
public async closeConnection() {
try {
await this.client.quit();
} catch (error) {
this.logger.error(error);
}
}
public async destructor() {
await this.closeConnection();
}
public async destroy() {
await this.destructor();
}
} }

View File

@ -93,7 +93,6 @@ WEBHOOK:
MESSAGES_SET: true MESSAGES_SET: true
MESSAGES_UPSERT: true MESSAGES_UPSERT: true
MESSAGES_UPDATE: true MESSAGES_UPDATE: true
SEND_MESSAGE: true
CONTACTS_SET: true CONTACTS_SET: true
CONTACTS_UPSERT: true CONTACTS_UPSERT: true
CONTACTS_UPDATE: true CONTACTS_UPDATE: true

View File

@ -16,8 +16,6 @@ function initWA() {
} }
function bootstrap() { function bootstrap() {
initWA();
const logger = new Logger('SERVER'); const logger = new Logger('SERVER');
const app = express(); const app = express();
@ -34,8 +32,8 @@ function bootstrap() {
methods: [...configService.get<Cors>('CORS').METHODS], methods: [...configService.get<Cors>('CORS').METHODS],
credentials: configService.get<Cors>('CORS').CREDENTIALS, credentials: configService.get<Cors>('CORS').CREDENTIALS,
}), }),
urlencoded({ extended: true, limit: '50mb' }), urlencoded({ extended: true, limit: '136mb' }),
json({ limit: '50mb' }), json({ limit: '136mb' }),
compression(), compression(),
); );
@ -73,6 +71,8 @@ function bootstrap() {
logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT), logger.log(httpServer.TYPE.toUpperCase() + ' - ON: ' + httpServer.PORT),
); );
initWA();
onUnexpectedError(); onUnexpectedError();
} }

View File

@ -9,17 +9,12 @@ import { RedisCache } from '../db/redis.client';
import { Logger } from '../config/logger.config'; import { Logger } from '../config/logger.config';
import { Redis } from '../config/env.config'; import { Redis } from '../config/env.config';
export async function useMultiFileAuthStateRedisDb( export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
redisEnv: Partial<Redis>,
instanceName: string,
): Promise<{
state: AuthenticationState; state: AuthenticationState;
saveCreds: () => Promise<void>; saveCreds: () => Promise<void>;
}> { }> {
const logger = new Logger(useMultiFileAuthStateRedisDb.name); const logger = new Logger(useMultiFileAuthStateRedisDb.name);
const cache = new RedisCache(redisEnv, instanceName);
const writeData = async (data: any, key: string): Promise<any> => { const writeData = async (data: any, key: string): Promise<any> => {
try { try {
return await cache.writeData(key, data); return await cache.writeData(key, data);

View File

@ -39,7 +39,6 @@ export const instanceNameSchema: JSONSchema7 = {
'MESSAGES_SET', 'MESSAGES_SET',
'MESSAGES_UPSERT', 'MESSAGES_UPSERT',
'MESSAGES_UPDATE', 'MESSAGES_UPDATE',
'SEND_MESSAGE',
'CONTACTS_SET', 'CONTACTS_SET',
'CONTACTS_UPSERT', 'CONTACTS_UPSERT',
'CONTACTS_UPDATE', 'CONTACTS_UPDATE',
@ -802,7 +801,6 @@ export const webhookSchema: JSONSchema7 = {
'MESSAGES_SET', 'MESSAGES_SET',
'MESSAGES_UPSERT', 'MESSAGES_UPSERT',
'MESSAGES_UPDATE', 'MESSAGES_UPDATE',
'SEND_MESSAGE',
'CONTACTS_SET', 'CONTACTS_SET',
'CONTACTS_UPSERT', 'CONTACTS_UPSERT',
'CONTACTS_UPDATE', 'CONTACTS_UPDATE',

View File

@ -10,6 +10,7 @@ import { WAStartupService } from '../services/whatsapp.service';
import { WebhookService } from '../services/webhook.service'; import { WebhookService } from '../services/webhook.service';
import { Logger } from '../../config/logger.config'; import { Logger } from '../../config/logger.config';
import { wa } from '../types/wa.types'; import { wa } from '../types/wa.types';
import { RedisCache } from '../../db/redis.client';
export class InstanceController { export class InstanceController {
constructor( constructor(
@ -19,6 +20,7 @@ export class InstanceController {
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
private readonly authService: AuthService, private readonly authService: AuthService,
private readonly webhookService: WebhookService, private readonly webhookService: WebhookService,
private readonly cache: RedisCache,
) {} ) {}
private readonly logger = new Logger(InstanceController.name); private readonly logger = new Logger(InstanceController.name);
@ -47,6 +49,7 @@ export class InstanceController {
this.configService, this.configService,
this.eventEmitter, this.eventEmitter,
this.repository, this.repository,
this.cache,
); );
instance.instanceName = instanceName; instance.instanceName = instanceName;
this.waMonitor.waInstances[instance.instanceName] = instance; this.waMonitor.waInstances[instance.instanceName] = instance;
@ -92,6 +95,7 @@ export class InstanceController {
this.configService, this.configService,
this.eventEmitter, this.eventEmitter,
this.repository, this.repository,
this.cache,
); );
instance.instanceName = instanceName; instance.instanceName = instanceName;
this.waMonitor.waInstances[instance.instanceName] = instance; this.waMonitor.waInstances[instance.instanceName] = instance;
@ -171,6 +175,7 @@ export class InstanceController {
this.configService, this.configService,
this.eventEmitter, this.eventEmitter,
this.repository, this.repository,
this.cache,
); );
instance.instanceName = instanceName; instance.instanceName = instanceName;

View File

@ -9,7 +9,7 @@ import {
NotFoundException, NotFoundException,
} from '../../exceptions'; } from '../../exceptions';
import { InstanceDto } from '../dto/instance.dto'; import { InstanceDto } from '../dto/instance.dto';
import { waMonitor } from '../whatsapp.module'; import { cache, waMonitor } from '../whatsapp.module';
import { Database, Redis, configService } from '../../config/env.config'; import { Database, Redis, configService } from '../../config/env.config';
import { RedisCache } from '../../db/redis.client'; import { RedisCache } from '../../db/redis.client';
@ -20,7 +20,6 @@ async function getInstance(instanceName: string) {
const exists = !!waMonitor.waInstances[instanceName]; const exists = !!waMonitor.waInstances[instanceName];
if (redisConf.ENABLED) { if (redisConf.ENABLED) {
const cache = new RedisCache(redisConf, instanceName);
const keyExists = await cache.keyExists(); const keyExists = await cache.keyExists();
return exists || keyExists; return exists || keyExists;
} }

View File

@ -14,14 +14,15 @@ import {
import { RepositoryBroker } from '../repository/repository.manager'; import { RepositoryBroker } from '../repository/repository.manager';
import { NotFoundException } from '../../exceptions'; import { NotFoundException } from '../../exceptions';
import { Db } from 'mongodb'; import { Db } from 'mongodb';
import { RedisCache } from '../../db/redis.client';
import { initInstance } from '../whatsapp.module'; import { initInstance } from '../whatsapp.module';
import { RedisCache } from '../../db/redis.client';
export class WAMonitoringService { export class WAMonitoringService {
constructor( constructor(
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly repository: RepositoryBroker, private readonly repository: RepositoryBroker,
private readonly cache: RedisCache,
) { ) {
this.removeInstance(); this.removeInstance();
this.noConnection(); this.noConnection();
@ -33,15 +34,12 @@ export class WAMonitoringService {
this.dbInstance = this.db.ENABLED this.dbInstance = this.db.ENABLED
? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances') ? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
: undefined; : undefined;
this.redisCache = this.redis.ENABLED ? new RedisCache(this.redis) : undefined;
} }
private readonly db: Partial<Database> = {}; private readonly db: Partial<Database> = {};
private readonly redis: Partial<Redis> = {}; private readonly redis: Partial<Redis> = {};
private dbInstance: Db; private dbInstance: Db;
private redisCache: RedisCache;
private readonly logger = new Logger(WAMonitoringService.name); private readonly logger = new Logger(WAMonitoringService.name);
public readonly waInstances: Record<string, WAStartupService> = {}; public readonly waInstances: Record<string, WAStartupService> = {};
@ -144,6 +142,7 @@ export class WAMonitoringService {
], ],
}); });
}); });
} else if (this.redis.ENABLED) {
} else { } else {
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' }); const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
for await (const dirent of dir) { for await (const dirent of dir) {
@ -176,8 +175,8 @@ export class WAMonitoringService {
} }
if (this.redis.ENABLED) { if (this.redis.ENABLED) {
this.redisCache.reference = instanceName; this.cache.reference = instanceName;
await this.redisCache.delAll(); await this.cache.delAll();
return; return;
} }
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true }); rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
@ -189,6 +188,7 @@ export class WAMonitoringService {
this.configService, this.configService,
this.eventEmitter, this.eventEmitter,
this.repository, this.repository,
this.cache,
); );
instance.instanceName = name; instance.instanceName = name;
await instance.connectToWhatsapp(); await instance.connectToWhatsapp();
@ -197,7 +197,8 @@ export class WAMonitoringService {
try { try {
if (this.redis.ENABLED) { if (this.redis.ENABLED) {
const keys = await this.redisCache.instanceKeys(); await this.cache.connect(this.redis as Redis);
const keys = await this.cache.instanceKeys();
if (keys?.length > 0) { if (keys?.length > 0) {
keys.forEach(async (k) => await set(k.split(':')[1])); keys.forEach(async (k) => await set(k.split(':')[1]));
} else { } else {

View File

@ -115,12 +115,14 @@ import { dbserver } from '../../db/db.connect';
import NodeCache from 'node-cache'; import NodeCache from 'node-cache';
import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db'; import { useMultiFileAuthStateRedisDb } from '../../utils/use-multi-file-auth-state-redis-db';
import sharp from 'sharp'; import sharp from 'sharp';
import { RedisCache } from '../../db/redis.client';
export class WAStartupService { export class WAStartupService {
constructor( constructor(
private readonly configService: ConfigService, private readonly configService: ConfigService,
private readonly eventEmitter: EventEmitter2, private readonly eventEmitter: EventEmitter2,
private readonly repository: RepositoryBroker, private readonly repository: RepositoryBroker,
private readonly cache: RedisCache,
) { ) {
this.cleanStore(); this.cleanStore();
this.instance.qrcode = { count: 0 }; this.instance.qrcode = { count: 0 };
@ -478,7 +480,8 @@ export class WAStartupService {
const redis = this.configService.get<Redis>('REDIS'); const redis = this.configService.get<Redis>('REDIS');
if (redis?.ENABLED) { if (redis?.ENABLED) {
return await useMultiFileAuthStateRedisDb(redis, this.instance.name); this.cache.reference = this.instance.name;
return await useMultiFileAuthStateRedisDb(this.cache);
} }
if (db.SAVE_DATA.INSTANCE && db.ENABLED) { if (db.SAVE_DATA.INSTANCE && db.ENABLED) {

View File

@ -9,7 +9,6 @@ export enum Events {
MESSAGES_SET = 'messages.set', MESSAGES_SET = 'messages.set',
MESSAGES_UPSERT = 'messages.upsert', MESSAGES_UPSERT = 'messages.upsert',
MESSAGES_UPDATE = 'messages.update', MESSAGES_UPDATE = 'messages.update',
SEND_MESSAGE = 'send.message',
CONTACTS_SET = 'contacts.set', CONTACTS_SET = 'contacts.set',
CONTACTS_UPSERT = 'contacts.upsert', CONTACTS_UPSERT = 'contacts.upsert',
CONTACTS_UPDATE = 'contacts.update', CONTACTS_UPDATE = 'contacts.update',

View File

@ -29,6 +29,7 @@ import { AuthRepository } from './repository/auth.repository';
import { WAStartupService } from './services/whatsapp.service'; import { WAStartupService } from './services/whatsapp.service';
import { delay } from '@whiskeysockets/baileys'; import { delay } from '@whiskeysockets/baileys';
import { Events } from './types/wa.types'; import { Events } from './types/wa.types';
import { RedisCache } from '../db/redis.client';
const logger = new Logger('WA MODULE'); const logger = new Logger('WA MODULE');
@ -49,7 +50,14 @@ export const repository = new RepositoryBroker(
dbserver?.getClient(), dbserver?.getClient(),
); );
export const waMonitor = new WAMonitoringService(eventEmitter, configService, repository); export const cache = new RedisCache();
export const waMonitor = new WAMonitoringService(
eventEmitter,
configService,
repository,
cache,
);
const authService = new AuthService(configService, waMonitor, repository); const authService = new AuthService(configService, waMonitor, repository);
@ -64,6 +72,7 @@ export const instanceController = new InstanceController(
eventEmitter, eventEmitter,
authService, authService,
webhookService, webhookService,
cache,
); );
export const viewsController = new ViewsController(waMonitor, configService); export const viewsController = new ViewsController(waMonitor, configService);
export const sendMessageController = new SendMessageController(waMonitor); export const sendMessageController = new SendMessageController(waMonitor);
@ -71,7 +80,7 @@ export const chatController = new ChatController(waMonitor);
export const groupController = new GroupController(waMonitor); export const groupController = new GroupController(waMonitor);
export async function initInstance() { export async function initInstance() {
const instance = new WAStartupService(configService, eventEmitter, repository); const instance = new WAStartupService(configService, eventEmitter, repository, cache);
const mode = configService.get<Auth>('AUTHENTICATION').INSTANCE.MODE; const mode = configService.get<Auth>('AUTHENTICATION').INSTANCE.MODE;