Skip to content

Commit 1dc5bb8

Browse files
authored
Update monitor.service.ts
1 parent 8652d40 commit 1dc5bb8

1 file changed

Lines changed: 9 additions & 313 deletions

File tree

Lines changed: 9 additions & 313 deletions
Original file line numberDiff line numberDiff line change
@@ -1,95 +1,19 @@
1-
import { execSync } from 'child_process';
2-
import EventEmitter2 from 'eventemitter2';
3-
import { opendirSync, readdirSync, rmSync } from 'fs';
4-
import { Db } from 'mongodb';
5-
import { join } from 'path';
6-
7-
import { Auth, ConfigService, Database, DelInstance, HttpServer, Redis } from '../../config/env.config';
8-
import { Logger } from '../../config/logger.config';
9-
import { INSTANCE_DIR, STORE_DIR } from '../../config/path.config';
10-
import { dbserver } from '../../libs/db.connect';
11-
import { RedisCache } from '../../libs/redis.client';
12-
import {
13-
AuthModel,
14-
ChatwootModel,
15-
ContactModel,
16-
MessageModel,
17-
MessageUpModel,
18-
SettingsModel,
19-
WebhookModel,
20-
} from '../models';
21-
import { RepositoryBroker } from '../repository/repository.manager';
22-
import { WAStartupService } from './whatsapp.service';
23-
24-
export class WAMonitoringService {
25-
constructor(
26-
private readonly eventEmitter: EventEmitter2,
27-
private readonly configService: ConfigService,
28-
private readonly repository: RepositoryBroker,
29-
private readonly cache: RedisCache,
30-
) {
31-
this.logger.verbose('instance created');
32-
33-
this.removeInstance();
34-
this.noConnection();
35-
this.delInstanceFiles();
36-
37-
Object.assign(this.db, configService.get<Database>('DATABASE'));
38-
Object.assign(this.redis, configService.get<Redis>('REDIS'));
39-
40-
this.dbInstance = this.db.ENABLED
41-
? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
42-
: undefined;
43-
}
44-
45-
private readonly db: Partial<Database> = {};
46-
private readonly redis: Partial<Redis> = {};
47-
48-
private dbInstance: Db;
49-
50-
private dbStore = dbserver;
51-
52-
private readonly logger = new Logger(WAMonitoringService.name);
53-
public readonly waInstances: Record<string, WAStartupService> = {};
54-
55-
public delInstanceTime(instance: string) {
56-
const time = this.configService.get<DelInstance>('DEL_INSTANCE');
57-
if (typeof time === 'number' && time > 0) {
58-
this.logger.verbose(`Instance "${instance}" don't have connection, will be removed in ${time} minutes`);
59-
60-
setTimeout(async () => {
61-
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
62-
if (this.waInstances[instance]?.connectionStatus?.state === 'connecting') {
63-
await this.waInstances[instance]?.client?.logout('Log out instance: ' + instance);
64-
this.waInstances[instance]?.client?.ws?.close();
65-
this.waInstances[instance]?.client?.end(undefined);
66-
delete this.waInstances[instance];
67-
} else {
68-
delete this.waInstances[instance];
69-
this.eventEmitter.emit('remove.instance', instance, 'inner');
70-
}
71-
}
72-
}, 1000 * 60 * time);
73-
}
74-
}
75-
761
public async instanceInfo(instanceName?: string) {
772
this.logger.verbose('get instance info');
783

794
const urlServer = this.configService.get<HttpServer>('SERVER').URL;
805

816
const instances: any[] = await Promise.all(
827
Object.entries(this.waInstances).map(async ([key, value]) => {
83-
if (!value || !value.connectionStatus || !value.connectionStatus.state) {
84-
return {
85-
instance: {
86-
instanceName: key,
87-
status: value?.connectionStatus?.state || 'unknown',
88-
},
89-
};
8+
const status = value?.connectionStatus?.state || 'unknown';
9+
10+
if (status === 'unknown') {
11+
return null;
9012
}
9113

92-
this.logger.verbose('instance: ' + key + ' - connectionStatus: open');
14+
if (status === 'open') {
15+
this.logger.verbose('instance: ' + key + ' - connectionStatus: open');
16+
}
9317

9418
const instanceData: any = {
9519
instance: {
@@ -98,7 +22,7 @@ export class WAMonitoringService {
9822
profileName: (await value.getProfileName()) || 'not loaded',
9923
profilePictureUrl: value.profilePictureUrl,
10024
profileStatus: (await value.getProfileStatus()) || '',
101-
status: 'open',
25+
status: status,
10226
},
10327
};
10428

@@ -117,7 +41,7 @@ export class WAMonitoringService {
11741

11842
return instanceData;
11943
}),
120-
);
44+
).then((results) => results.filter((instance) => instance !== null));
12145

12246
this.logger.verbose('return instance info: ' + instances.length);
12347

@@ -128,231 +52,3 @@ export class WAMonitoringService {
12852

12953
return instances;
13054
}
131-
132-
private delInstanceFiles() {
133-
this.logger.verbose('cron to delete instance files started');
134-
setInterval(async () => {
135-
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
136-
const collections = await this.dbInstance.collections();
137-
collections.forEach(async (collection) => {
138-
const name = collection.namespace.replace(/^[\w-]+./, '');
139-
await this.dbInstance.collection(name).deleteMany({
140-
$or: [{ _id: { $regex: /^app.state.*/ } }, { _id: { $regex: /^session-.*/ } }],
141-
});
142-
this.logger.verbose('instance files deleted: ' + name);
143-
});
144-
} else if (!this.redis.ENABLED) {
145-
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
146-
for await (const dirent of dir) {
147-
if (dirent.isDirectory()) {
148-
const files = readdirSync(join(INSTANCE_DIR, dirent.name), {
149-
encoding: 'utf-8',
150-
});
151-
files.forEach(async (file) => {
152-
if (file.match(/^app.state.*/) || file.match(/^session-.*/)) {
153-
rmSync(join(INSTANCE_DIR, dirent.name, file), {
154-
recursive: true,
155-
force: true,
156-
});
157-
}
158-
});
159-
this.logger.verbose('instance files deleted: ' + dirent.name);
160-
}
161-
}
162-
}
163-
}, 3600 * 1000 * 2);
164-
}
165-
166-
public async cleaningUp(instanceName: string) {
167-
this.logger.verbose('cleaning up instance: ' + instanceName);
168-
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
169-
this.logger.verbose('cleaning up instance in database: ' + instanceName);
170-
await this.repository.dbServer.connect();
171-
const collections: any[] = await this.dbInstance.collections();
172-
if (collections.length > 0) {
173-
await this.dbInstance.dropCollection(instanceName);
174-
}
175-
return;
176-
}
177-
178-
if (this.redis.ENABLED) {
179-
this.logger.verbose('cleaning up instance in redis: ' + instanceName);
180-
this.cache.reference = instanceName;
181-
await this.cache.delAll();
182-
return;
183-
}
184-
185-
this.logger.verbose('cleaning up instance in files: ' + instanceName);
186-
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
187-
}
188-
189-
public async cleaningStoreFiles(instanceName: string) {
190-
if (!this.db.ENABLED) {
191-
this.logger.verbose('cleaning store files instance: ' + instanceName);
192-
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
193-
194-
execSync(`rm -rf ${join(STORE_DIR, 'chats', instanceName)}`);
195-
execSync(`rm -rf ${join(STORE_DIR, 'contacts', instanceName)}`);
196-
execSync(`rm -rf ${join(STORE_DIR, 'message-up', instanceName)}`);
197-
execSync(`rm -rf ${join(STORE_DIR, 'messages', instanceName)}`);
198-
199-
execSync(`rm -rf ${join(STORE_DIR, 'auth', 'apikey', instanceName + '.json')}`);
200-
execSync(`rm -rf ${join(STORE_DIR, 'webhook', instanceName + '.json')}`);
201-
execSync(`rm -rf ${join(STORE_DIR, 'chatwoot', instanceName + '*')}`);
202-
execSync(`rm -rf ${join(STORE_DIR, 'chamaai', instanceName + '*')}`);
203-
execSync(`rm -rf ${join(STORE_DIR, 'proxy', instanceName + '*')}`);
204-
execSync(`rm -rf ${join(STORE_DIR, 'rabbitmq', instanceName + '*')}`);
205-
execSync(`rm -rf ${join(STORE_DIR, 'typebot', instanceName + '*')}`);
206-
execSync(`rm -rf ${join(STORE_DIR, 'websocket', instanceName + '*')}`);
207-
execSync(`rm -rf ${join(STORE_DIR, 'settings', instanceName + '*')}`);
208-
209-
return;
210-
}
211-
212-
this.logger.verbose('cleaning store database instance: ' + instanceName);
213-
214-
await AuthModel.deleteMany({ owner: instanceName });
215-
await ContactModel.deleteMany({ owner: instanceName });
216-
await MessageModel.deleteMany({ owner: instanceName });
217-
await MessageUpModel.deleteMany({ owner: instanceName });
218-
await AuthModel.deleteMany({ _id: instanceName });
219-
await WebhookModel.deleteMany({ _id: instanceName });
220-
await ChatwootModel.deleteMany({ _id: instanceName });
221-
await SettingsModel.deleteMany({ _id: instanceName });
222-
223-
return;
224-
}
225-
226-
public async loadInstance() {
227-
this.logger.verbose('Loading instances');
228-
229-
try {
230-
if (this.redis.ENABLED) {
231-
await this.loadInstancesFromRedis();
232-
} else if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
233-
await this.loadInstancesFromDatabase();
234-
} else {
235-
await this.loadInstancesFromFiles();
236-
}
237-
} catch (error) {
238-
this.logger.error(error);
239-
}
240-
}
241-
242-
private async setInstance(name: string) {
243-
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
244-
instance.instanceName = name;
245-
this.logger.verbose('Instance loaded: ' + name);
246-
247-
await instance.connectToWhatsapp();
248-
this.logger.verbose('connectToWhatsapp: ' + name);
249-
250-
this.waInstances[name] = instance;
251-
}
252-
253-
private async loadInstancesFromRedis() {
254-
this.logger.verbose('Redis enabled');
255-
await this.cache.connect(this.redis as Redis);
256-
const keys = await this.cache.instanceKeys();
257-
258-
if (keys?.length > 0) {
259-
this.logger.verbose('Reading instance keys and setting instances');
260-
await Promise.all(keys.map((k) => this.setInstance(k.split(':')[1])));
261-
} else {
262-
this.logger.verbose('No instance keys found');
263-
}
264-
}
265-
266-
private async loadInstancesFromDatabase() {
267-
this.logger.verbose('Database enabled');
268-
await this.repository.dbServer.connect();
269-
const collections: any[] = await this.dbInstance.collections();
270-
271-
if (collections.length > 0) {
272-
this.logger.verbose('Reading collections and setting instances');
273-
await Promise.all(collections.map((coll) => this.setInstance(coll.namespace.replace(/^[\w-]+\./, ''))));
274-
} else {
275-
this.logger.verbose('No collections found');
276-
}
277-
}
278-
279-
private async loadInstancesFromFiles() {
280-
this.logger.verbose('Store in files enabled');
281-
const dir = opendirSync(INSTANCE_DIR, { encoding: 'utf-8' });
282-
const instanceDirs = [];
283-
284-
for await (const dirent of dir) {
285-
if (dirent.isDirectory()) {
286-
instanceDirs.push(dirent.name);
287-
} else {
288-
this.logger.verbose('No instance files found');
289-
}
290-
}
291-
292-
await Promise.all(
293-
instanceDirs.map(async (instanceName) => {
294-
this.logger.verbose('Reading instance files and setting instances: ' + instanceName);
295-
const files = readdirSync(join(INSTANCE_DIR, instanceName), { encoding: 'utf-8' });
296-
297-
if (files.length === 0) {
298-
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
299-
} else {
300-
await this.setInstance(instanceName);
301-
}
302-
}),
303-
);
304-
}
305-
306-
private removeInstance() {
307-
this.eventEmitter.on('remove.instance', async (instanceName: string) => {
308-
this.logger.verbose('remove instance: ' + instanceName);
309-
try {
310-
this.logger.verbose('instance: ' + instanceName + ' - removing from memory');
311-
this.waInstances[instanceName] = undefined;
312-
} catch (error) {
313-
this.logger.error(error);
314-
}
315-
316-
try {
317-
this.logger.verbose('request cleaning up instance: ' + instanceName);
318-
this.cleaningUp(instanceName);
319-
this.cleaningStoreFiles(instanceName);
320-
} finally {
321-
this.logger.warn(`Instance "${instanceName}" - REMOVED`);
322-
}
323-
});
324-
this.eventEmitter.on('logout.instance', async (instanceName: string) => {
325-
this.logger.verbose('logout instance: ' + instanceName);
326-
try {
327-
this.logger.verbose('request cleaning up instance: ' + instanceName);
328-
this.cleaningUp(instanceName);
329-
} finally {
330-
this.logger.warn(`Instance "${instanceName}" - LOGOUT`);
331-
}
332-
});
333-
}
334-
335-
private noConnection() {
336-
this.logger.verbose('checking instances without connection');
337-
this.eventEmitter.on('no.connection', async (instanceName) => {
338-
try {
339-
this.logger.verbose('logging out instance: ' + instanceName);
340-
await this.waInstances[instanceName]?.client?.logout('Log out instance: ' + instanceName);
341-
342-
this.logger.verbose('close connection instance: ' + instanceName);
343-
this.waInstances[instanceName]?.client?.ws?.close();
344-
345-
this.waInstances[instanceName].instance.qrcode = { count: 0 };
346-
this.waInstances[instanceName].stateConnection.state = 'close';
347-
} catch (error) {
348-
this.logger.error({
349-
localError: 'noConnection',
350-
warn: 'Error deleting instance from memory.',
351-
error,
352-
});
353-
} finally {
354-
this.logger.warn(`Instance "${instanceName}" - NOT CONNECTION`);
355-
}
356-
});
357-
}
358-
}

0 commit comments

Comments
 (0)