mirror of
https://github.com/EvolutionAPI/evolution-api.git
synced 2025-07-26 10:28:38 -06:00
docs:Update code comments, add JSDoc annotations
This commit is contained in:
parent
bddd6408ac
commit
aef6deb89b
@ -3,19 +3,28 @@ import * as amqp from 'amqplib/callback_api';
|
||||
import { configService, Rabbitmq } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
// Create a logger instance specifically for AMQP-related logs.
|
||||
const logger = new Logger('AMQP');
|
||||
|
||||
// Declare an AMQP channel, initially set to null.
|
||||
let amqpChannel: amqp.Channel | null = null;
|
||||
|
||||
/**
|
||||
* Initializes the AMQP (Advanced Message Queuing Protocol) connection.
|
||||
* @returns {Promise<void>} A promise that resolves when the AMQP connection is established.
|
||||
*/
|
||||
export const initAMQP = () => {
|
||||
return new Promise<void>((resolve, reject) => {
|
||||
const uri = configService.get<Rabbitmq>('RABBITMQ').URI;
|
||||
|
||||
// Connect to the RabbitMQ server.
|
||||
amqp.connect(uri, (error, connection) => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
return;
|
||||
}
|
||||
|
||||
// Create an AMQP channel for communication.
|
||||
connection.createChannel((channelError, channel) => {
|
||||
if (channelError) {
|
||||
reject(channelError);
|
||||
@ -24,6 +33,7 @@ export const initAMQP = () => {
|
||||
|
||||
const exchangeName = 'evolution_exchange';
|
||||
|
||||
// Declare an exchange with topic routing.
|
||||
channel.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
@ -38,13 +48,23 @@ export const initAMQP = () => {
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the initialized AMQP channel.
|
||||
* @returns {amqp.Channel | null} The initialized AMQP channel or null if not initialized.
|
||||
*/
|
||||
export const getAMQP = (): amqp.Channel | null => {
|
||||
return amqpChannel;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initializes queues for specified events.
|
||||
* @param {string} instanceName - The name of the instance.
|
||||
* @param {string[]} events - An array of event names.
|
||||
*/
|
||||
export const initQueues = (instanceName: string, events: string[]) => {
|
||||
if (!events || !events.length) return;
|
||||
|
||||
// Transform event names into queue names.
|
||||
const queues = events.map((event) => {
|
||||
return `${event.replace(/_/g, '.').toLowerCase()}`;
|
||||
});
|
||||
@ -53,6 +73,7 @@ export const initQueues = (instanceName: string, events: string[]) => {
|
||||
const amqp = getAMQP();
|
||||
const exchangeName = instanceName ?? 'evolution_exchange';
|
||||
|
||||
// Assert the exchange with topic routing.
|
||||
amqp.assertExchange(exchangeName, 'topic', {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
@ -60,6 +81,7 @@ export const initQueues = (instanceName: string, events: string[]) => {
|
||||
|
||||
const queueName = `${instanceName}.${event}`;
|
||||
|
||||
// Assert a queue with quorum support.
|
||||
amqp.assertQueue(queueName, {
|
||||
durable: true,
|
||||
autoDelete: false,
|
||||
@ -68,6 +90,7 @@ export const initQueues = (instanceName: string, events: string[]) => {
|
||||
},
|
||||
});
|
||||
|
||||
// Bind the queue to the exchange with the corresponding event.
|
||||
amqp.bindQueue(queueName, exchangeName, event);
|
||||
});
|
||||
};
|
||||
|
@ -3,23 +3,73 @@ import mongoose from 'mongoose';
|
||||
import { configService, Database } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
/**
|
||||
* Object for logging MongoDB-related messages.
|
||||
* @type {Logger}
|
||||
*/
|
||||
const logger = new Logger('MongoDB');
|
||||
|
||||
/**
|
||||
* Database settings retrieved from the configuration file.
|
||||
* @type {Database}
|
||||
*/
|
||||
const db = configService.get<Database>('DATABASE');
|
||||
|
||||
/**
|
||||
* Function that creates and returns a connection to MongoDB using Mongoose.
|
||||
* @returns {mongoose.Connection | undefined} MongoDB connection or `undefined` if the connection is not enabled.
|
||||
*/
|
||||
export const dbserver = (() => {
|
||||
if (db.ENABLED) {
|
||||
/**
|
||||
* Log message indicating an attempt to connect to MongoDB.
|
||||
*/
|
||||
logger.verbose('connecting');
|
||||
const dbs = mongoose.createConnection(db.CONNECTION.URI, {
|
||||
|
||||
/**
|
||||
* Options for the MongoDB connection.
|
||||
* @type {mongoose.ConnectionOptions}
|
||||
*/
|
||||
const connectionOptions = {
|
||||
dbName: db.CONNECTION.DB_PREFIX_NAME + '-whatsapp-api',
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Creation of the MongoDB connection.
|
||||
* @type {mongoose.Connection}
|
||||
*/
|
||||
const dbs = mongoose.createConnection(db.CONNECTION.URI, connectionOptions);
|
||||
|
||||
/**
|
||||
* Log message indicating the successful connection to MongoDB.
|
||||
*/
|
||||
logger.verbose('connected in ' + db.CONNECTION.URI);
|
||||
|
||||
/**
|
||||
* Informative log message about the connected database name.
|
||||
*/
|
||||
logger.info('ON - dbName: ' + dbs['$dbName']);
|
||||
|
||||
/**
|
||||
* Registers an event handler for the beforeExit process event.
|
||||
*/
|
||||
process.on('beforeExit', () => {
|
||||
/**
|
||||
* Log message indicating the destruction of the MongoDB connection instance.
|
||||
*/
|
||||
logger.verbose('instance destroyed');
|
||||
|
||||
/**
|
||||
* Destroys the MongoDB connection.
|
||||
* @param {boolean} [force=false] - Indicates whether the destruction should be forced.
|
||||
* @param {function(Error)} [callback] - Callback function to handle errors during destruction.
|
||||
*/
|
||||
dbserver.destroy(true, (error) => logger.error(error));
|
||||
});
|
||||
|
||||
/**
|
||||
* Returns the MongoDB connection.
|
||||
*/
|
||||
return dbs;
|
||||
}
|
||||
})();
|
||||
|
@ -4,13 +4,24 @@ import { BufferJSON } from '@whiskeysockets/baileys';
|
||||
import { Redis } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
/**
|
||||
* Class representing a Redis cache.
|
||||
*/
|
||||
export class RedisCache {
|
||||
/**
|
||||
* Disconnects from the Redis server.
|
||||
*/
|
||||
async disconnect() {
|
||||
await this.client.disconnect();
|
||||
this.statusConnection = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new instance of RedisCache.
|
||||
*/
|
||||
constructor() {
|
||||
this.logger.verbose('instance created');
|
||||
|
||||
process.on('beforeExit', async () => {
|
||||
this.logger.verbose('instance destroyed');
|
||||
if (this.statusConnection) {
|
||||
@ -24,11 +35,19 @@ export class RedisCache {
|
||||
private instanceName: string;
|
||||
private redisEnv: Redis;
|
||||
|
||||
/**
|
||||
* Sets the reference for the Redis instance.
|
||||
* @param {string} reference - The reference to set.
|
||||
*/
|
||||
public set reference(reference: string) {
|
||||
this.logger.verbose('set reference: ' + reference);
|
||||
this.instanceName = reference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects to the Redis server.
|
||||
* @param {Redis} redisEnv - The Redis configuration.
|
||||
*/
|
||||
public async connect(redisEnv: Redis) {
|
||||
this.logger.verbose('connecting');
|
||||
this.client = createClient({ url: redisEnv.URI });
|
||||
@ -41,6 +60,10 @@ export class RedisCache {
|
||||
private readonly logger = new Logger(RedisCache.name);
|
||||
private client: RedisClientType;
|
||||
|
||||
/**
|
||||
* Retrieves keys for the Redis instance.
|
||||
* @returns {Promise<string[]>} An array of keys.
|
||||
*/
|
||||
public async instanceKeys(): Promise<string[]> {
|
||||
try {
|
||||
this.logger.verbose('instance keys: ' + this.redisEnv.PREFIX_KEY + ':*');
|
||||
@ -50,6 +73,11 @@ export class RedisCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if a specific key exists.
|
||||
* @param {string} key - The key to check.
|
||||
* @returns {Promise<boolean>} `true` if the key exists, otherwise `false`.
|
||||
*/
|
||||
public async keyExists(key?: string) {
|
||||
if (key) {
|
||||
this.logger.verbose('keyExists: ' + key);
|
||||
@ -59,6 +87,12 @@ export class RedisCache {
|
||||
return !!(await this.instanceKeys()).find((i) => i === this.instanceName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes data to Redis cache.
|
||||
* @param {string} field - The field to write data to.
|
||||
* @param {any} data - The data to write.
|
||||
* @returns {Promise<boolean>} `true` if the write is successful, otherwise `false`.
|
||||
*/
|
||||
public async writeData(field: string, data: any) {
|
||||
try {
|
||||
this.logger.verbose('writeData: ' + field);
|
||||
@ -70,6 +104,11 @@ export class RedisCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads data from Redis cache.
|
||||
* @param {string} field - The field to read data from.
|
||||
* @returns {Promise<any | null>} The data if found, otherwise `null`.
|
||||
*/
|
||||
public async readData(field: string) {
|
||||
try {
|
||||
this.logger.verbose('readData: ' + field);
|
||||
@ -87,6 +126,11 @@ export class RedisCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes data from Redis cache.
|
||||
* @param {string} field - The field to remove data from.
|
||||
* @returns {Promise<boolean>} `true` if the removal is successful, otherwise `false`.
|
||||
*/
|
||||
public async removeData(field: string) {
|
||||
try {
|
||||
this.logger.verbose('removeData: ' + field);
|
||||
@ -96,6 +140,11 @@ export class RedisCache {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes all data associated with the Redis instance.
|
||||
* @param {string} hash - The hash to delete, defaults to the instance name.
|
||||
* @returns {Promise<boolean>} `true` if the deletion is successful, otherwise `false`.
|
||||
*/
|
||||
public async delAll(hash?: string) {
|
||||
try {
|
||||
this.logger.verbose('instance delAll: ' + hash);
|
||||
|
@ -4,23 +4,35 @@ import { Server as SocketIO } from 'socket.io';
|
||||
import { configService, Cors, Websocket } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
|
||||
// Create a logger instance specifically for socket-related logs.
|
||||
const logger = new Logger('Socket');
|
||||
|
||||
// Declare the Socket.IO instance.
|
||||
let io: SocketIO;
|
||||
|
||||
const cors = configService.get<Cors>('CORS').ORIGIN;
|
||||
// Get the allowed origins from the configuration.
|
||||
const corsOrigins = configService.get<Cors>('CORS').ORIGIN;
|
||||
|
||||
export const initIO = (httpServer: Server) => {
|
||||
/**
|
||||
* Initialize Socket.IO with the provided HTTP server.
|
||||
* @param {Server} httpServer - The HTTP server to attach Socket.IO to.
|
||||
* @returns {SocketIO | null} The Socket.IO instance if enabled, or null if disabled.
|
||||
*/
|
||||
export const initIO = (httpServer: Server): SocketIO | null => {
|
||||
// Check if WebSocket is enabled in the configuration.
|
||||
if (configService.get<Websocket>('WEBSOCKET')?.ENABLED) {
|
||||
// Create a new Socket.IO instance with CORS configuration.
|
||||
io = new SocketIO(httpServer, {
|
||||
cors: {
|
||||
origin: cors,
|
||||
origin: corsOrigins,
|
||||
},
|
||||
});
|
||||
|
||||
// Handle the 'connection' event when a user connects.
|
||||
io.on('connection', (socket) => {
|
||||
logger.info('User connected');
|
||||
|
||||
// Handle the 'disconnect' event when a user disconnects.
|
||||
socket.on('disconnect', () => {
|
||||
logger.info('User disconnected');
|
||||
});
|
||||
@ -29,12 +41,20 @@ export const initIO = (httpServer: Server) => {
|
||||
logger.info('Socket.io initialized');
|
||||
return io;
|
||||
}
|
||||
|
||||
// WebSocket is disabled, return null.
|
||||
return null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the Socket.IO instance.
|
||||
* @throws {Error} Throws an error if Socket.IO is not initialized.
|
||||
* @returns {SocketIO} The initialized Socket.IO instance.
|
||||
*/
|
||||
export const getIO = (): SocketIO => {
|
||||
logger.verbose('Getting Socket.io');
|
||||
|
||||
// If Socket.IO is not initialized, throw an error.
|
||||
if (!io) {
|
||||
logger.error('Socket.io not initialized');
|
||||
throw new Error('Socket.io not initialized');
|
||||
|
24
src/main.ts
24
src/main.ts
@ -16,16 +16,27 @@ import { ServerUP } from './utils/server-up';
|
||||
import { HttpStatus, router } from './whatsapp/routers/index.router';
|
||||
import { waMonitor } from './whatsapp/whatsapp.module';
|
||||
|
||||
/**
|
||||
* Initializes WhatsApp monitoring.
|
||||
*/
|
||||
function initWA() {
|
||||
waMonitor.loadInstance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Bootstrap the application.
|
||||
*/
|
||||
function bootstrap() {
|
||||
const logger = new Logger('SERVER');
|
||||
const app = express();
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
/**
|
||||
* Determine if the request origin is allowed by CORS.
|
||||
* @param requestOrigin The origin of the incoming request.
|
||||
* @param callback The callback function to invoke.
|
||||
*/
|
||||
origin(requestOrigin, callback) {
|
||||
const { ORIGIN } = configService.get<Cors>('CORS');
|
||||
if (ORIGIN.includes('*')) {
|
||||
@ -53,6 +64,13 @@ function bootstrap() {
|
||||
app.use('/', router);
|
||||
|
||||
app.use(
|
||||
/**
|
||||
* Error handling middleware.
|
||||
* @param err The error object.
|
||||
* @param req The Express request object.
|
||||
* @param res The Express response object.
|
||||
* @param next The next function to invoke.
|
||||
*/
|
||||
(err: Error, req: Request, res: Response, next: NextFunction) => {
|
||||
if (err) {
|
||||
const webhook = configService.get<Webhook>('WEBHOOK');
|
||||
@ -98,6 +116,12 @@ function bootstrap() {
|
||||
|
||||
next();
|
||||
},
|
||||
/**
|
||||
* 404 Not Found middleware.
|
||||
* @param req The Express request object.
|
||||
* @param res The Express response object.
|
||||
* @param next The next function to invoke.
|
||||
*/
|
||||
(req: Request, res: Response, next: NextFunction) => {
|
||||
const { method, url } = req;
|
||||
|
||||
|
@ -8,22 +8,38 @@ import { configService, SslConf } from '../config/env.config';
|
||||
export class ServerUP {
|
||||
static #app: Express;
|
||||
|
||||
/**
|
||||
* Set the Express application instance.
|
||||
* @param {Express} e - The Express application instance.
|
||||
*/
|
||||
static set app(e: Express) {
|
||||
this.#app = e;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an HTTPS server instance with SSL configuration.
|
||||
* @returns {https.Server} An HTTPS server instance.
|
||||
*/
|
||||
static get https() {
|
||||
// Retrieve SSL certificate and private key paths from configuration.
|
||||
const { FULLCHAIN, PRIVKEY } = configService.get<SslConf>('SSL_CONF');
|
||||
|
||||
// Create an HTTPS server using the SSL certificate and private key.
|
||||
return https.createServer(
|
||||
{
|
||||
cert: readFileSync(FULLCHAIN),
|
||||
key: readFileSync(PRIVKEY),
|
||||
cert: readFileSync(FULLCHAIN), // Read SSL certificate file.
|
||||
key: readFileSync(PRIVKEY), // Read private key file.
|
||||
},
|
||||
ServerUP.#app,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an HTTP server instance.
|
||||
* @returns {http.Server} An HTTP server instance.
|
||||
*/
|
||||
static get http() {
|
||||
// Create an HTTP server using the Express application instance.
|
||||
return http.createServer(ServerUP.#app);
|
||||
}
|
||||
}
|
||||
|
@ -11,17 +11,25 @@ import { configService, Database } from '../config/env.config';
|
||||
import { Logger } from '../config/logger.config';
|
||||
import { dbserver } from '../libs/db.connect';
|
||||
|
||||
/**
|
||||
* Provides a function to handle AuthenticationState and credentials using a MongoDB collection.
|
||||
* @param {string} coll - The name of the MongoDB collection.
|
||||
* @returns {Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }>} An object with AuthenticationState and saveCreds function.
|
||||
*/
|
||||
export async function useMultiFileAuthStateDb(
|
||||
coll: string,
|
||||
): Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }> {
|
||||
const logger = new Logger(useMultiFileAuthStateDb.name);
|
||||
|
||||
// Get the MongoDB client from the database server connection.
|
||||
const client = dbserver.getClient();
|
||||
|
||||
// Construct the collection name based on the database prefix.
|
||||
const collection = client
|
||||
.db(configService.get<Database>('DATABASE').CONNECTION.DB_PREFIX_NAME + '-instances')
|
||||
.collection(coll);
|
||||
|
||||
// Helper function to write data to the MongoDB collection.
|
||||
const writeData = async (data: any, key: string): Promise<any> => {
|
||||
try {
|
||||
await client.connect();
|
||||
@ -33,6 +41,7 @@ export async function useMultiFileAuthStateDb(
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to read data from the MongoDB collection.
|
||||
const readData = async (key: string): Promise<any> => {
|
||||
try {
|
||||
await client.connect();
|
||||
@ -44,6 +53,7 @@ export async function useMultiFileAuthStateDb(
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to remove data from the MongoDB collection.
|
||||
const removeData = async (key: string) => {
|
||||
try {
|
||||
await client.connect();
|
||||
@ -53,6 +63,7 @@ export async function useMultiFileAuthStateDb(
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize AuthenticationCreds using stored or default values.
|
||||
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
|
||||
|
||||
return {
|
||||
@ -60,8 +71,6 @@ export async function useMultiFileAuthStateDb(
|
||||
creds,
|
||||
keys: {
|
||||
get: async (type, ids: string[]) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const data: { [_: string]: SignalDataTypeMap[type] } = {};
|
||||
await Promise.all(
|
||||
ids.map(async (id) => {
|
||||
@ -90,6 +99,7 @@ export async function useMultiFileAuthStateDb(
|
||||
},
|
||||
},
|
||||
},
|
||||
// Save the credentials to the MongoDB collection.
|
||||
saveCreds: async () => {
|
||||
return writeData(creds, 'creds');
|
||||
},
|
||||
|
@ -9,6 +9,11 @@ import {
|
||||
import { Logger } from '../config/logger.config';
|
||||
import { RedisCache } from '../libs/redis.client';
|
||||
|
||||
/**
|
||||
* Provides a function to handle AuthenticationState and credentials using a Redis cache.
|
||||
* @param {RedisCache} cache - The RedisCache instance to store and retrieve data.
|
||||
* @returns {Promise<{ state: AuthenticationState; saveCreds: () => Promise<void> }>} An object with AuthenticationState and saveCreds function.
|
||||
*/
|
||||
export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
|
||||
state: AuthenticationState;
|
||||
saveCreds: () => Promise<void>;
|
||||
@ -27,11 +32,12 @@ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
|
||||
try {
|
||||
return await cache.readData(key);
|
||||
} catch (error) {
|
||||
logger.error({ readData: 'writeData', error });
|
||||
logger.error({ readData: 'readData', error });
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// Helper function to remove data from the Redis cache.
|
||||
const removeData = async (key: string) => {
|
||||
try {
|
||||
return await cache.removeData(key);
|
||||
@ -40,6 +46,7 @@ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize AuthenticationCreds using stored or default values.
|
||||
const creds: AuthenticationCreds = (await readData('creds')) || initAuthCreds();
|
||||
|
||||
return {
|
||||
@ -77,6 +84,7 @@ export async function useMultiFileAuthStateRedisDb(cache: RedisCache): Promise<{
|
||||
},
|
||||
},
|
||||
},
|
||||
// Save the credentials to the Redis cache.
|
||||
saveCreds: async () => {
|
||||
return await writeData(creds, 'creds');
|
||||
},
|
||||
|
@ -1,6 +1,12 @@
|
||||
import { JSONSchema7, JSONSchema7Definition } from 'json-schema';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
/**
|
||||
* Represents a utility function to generate JSON schema for non-empty properties.
|
||||
*
|
||||
* @param {string[]} propertyNames - The names of properties to check for non-emptiness.
|
||||
* @returns {JSONSchema7} A JSON schema with non-empty property checks.
|
||||
*/
|
||||
const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
|
||||
const properties = {};
|
||||
propertyNames.forEach(
|
||||
@ -21,6 +27,11 @@ const isNotEmpty = (...propertyNames: string[]): JSONSchema7 => {
|
||||
};
|
||||
|
||||
// Instance Schema
|
||||
/**
|
||||
* JSON schema for an instance with properties like instanceName, webhook, and more.
|
||||
*
|
||||
* @type {JSONSchema7}
|
||||
*/
|
||||
export const instanceNameSchema: JSONSchema7 = {
|
||||
$id: v4(),
|
||||
type: 'object',
|
||||
|
@ -11,6 +11,9 @@ import { InstanceDto } from '../dto/instance.dto';
|
||||
import { RepositoryBroker } from '../repository/repository.manager';
|
||||
import { WAMonitoringService } from './monitor.service';
|
||||
|
||||
/**
|
||||
* Represents the payload of a JWT token.
|
||||
*/
|
||||
export type JwtPayload = {
|
||||
instanceName: string;
|
||||
apiName: string;
|
||||
@ -19,11 +22,22 @@ export type JwtPayload = {
|
||||
tokenId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the structure of an old JWT token.
|
||||
*/
|
||||
export class OldToken {
|
||||
oldToken: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Service responsible for authentication-related operations.
|
||||
*/
|
||||
export class AuthService {
|
||||
/**
|
||||
* Creates an instance of AuthService.
|
||||
* @param configService The configuration service.
|
||||
* @param waMonitor The monitoring service for WhatsApp instances.
|
||||
* @param repository The repository manager for database operations.
|
||||
*/
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly waMonitor: WAMonitoringService,
|
||||
@ -32,6 +46,12 @@ export class AuthService {
|
||||
|
||||
private readonly logger = new Logger(AuthService.name);
|
||||
|
||||
/**
|
||||
* Generates a JWT token for a given instance.
|
||||
* @param instance The instance DTO for which to generate the token.
|
||||
* @returns An object containing the generated JWT token.
|
||||
* @throws BadRequestException if an error occurs during token generation.
|
||||
*/
|
||||
private async jwt(instance: InstanceDto) {
|
||||
const jwtOpts = this.configService.get<Auth>('AUTHENTICATION').JWT;
|
||||
const token = sign(
|
||||
@ -61,6 +81,13 @@ export class AuthService {
|
||||
return { jwt: token };
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an API key for a given instance.
|
||||
* @param instance The instance DTO for which to generate the API key.
|
||||
* @param token (Optional) An existing API key to use.
|
||||
* @returns An object containing the generated or defined API key.
|
||||
* @throws BadRequestException if an error occurs during API key generation.
|
||||
*/
|
||||
private async apikey(instance: InstanceDto, token?: string) {
|
||||
const apikey = token ? token : v4().toUpperCase();
|
||||
|
||||
@ -81,6 +108,11 @@ export class AuthService {
|
||||
return { apikey };
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for the existence of a duplicate token among instances.
|
||||
* @param token The token to check for duplication.
|
||||
* @returns `true` if the token is not duplicated among instances, otherwise throws a BadRequestException.
|
||||
*/
|
||||
public async checkDuplicateToken(token: string) {
|
||||
const instances = await this.waMonitor.instanceInfo();
|
||||
|
||||
@ -97,6 +129,12 @@ export class AuthService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates an authentication hash (JWT token or API key) based on the authentication type.
|
||||
* @param instance The instance DTO for which to generate the hash.
|
||||
* @param token (Optional) An existing token to use (for API key generation).
|
||||
* @returns An object containing the generated authentication hash (JWT token or API key).
|
||||
*/
|
||||
public async generateHash(instance: InstanceDto, token?: string) {
|
||||
const options = this.configService.get<Auth>('AUTHENTICATION');
|
||||
|
||||
@ -105,6 +143,12 @@ export class AuthService {
|
||||
return (await this[options.TYPE](instance, token)) as { jwt: string } | { apikey: string };
|
||||
}
|
||||
|
||||
/**
|
||||
* Refreshes a JWT token based on an old JWT token.
|
||||
* @param oldToken An old JWT token to refresh.
|
||||
* @returns An object containing the refreshed JWT token and instanceName.
|
||||
* @throws BadRequestException if the oldToken is invalid or an error occurs during token refresh.
|
||||
*/
|
||||
public async refreshToken({ oldToken }: OldToken) {
|
||||
this.logger.verbose('refreshing token');
|
||||
|
||||
|
@ -24,41 +24,95 @@ import {
|
||||
import { RepositoryBroker } from '../repository/repository.manager';
|
||||
import { WAStartupService } from './whatsapp.service';
|
||||
|
||||
/**
|
||||
* Represents a service for monitoring WhatsApp instances.
|
||||
*/
|
||||
export class WAMonitoringService {
|
||||
/**
|
||||
* Creates an instance of WAMonitoringService.
|
||||
* @param {EventEmitter2} eventEmitter - Event emitter for handling events.
|
||||
* @param {ConfigService} configService - Configuration service.
|
||||
* @param {RepositoryBroker} repository - Repository broker for database operations.
|
||||
* @param {RedisCache} cache - Redis cache for storing data.
|
||||
*/
|
||||
constructor(
|
||||
private readonly eventEmitter: EventEmitter2,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly repository: RepositoryBroker,
|
||||
private readonly cache: RedisCache,
|
||||
) {
|
||||
/**
|
||||
* Logger instance for logging service activities.
|
||||
* @private
|
||||
* @type {Logger}
|
||||
*/
|
||||
this.logger.verbose('instance created');
|
||||
|
||||
// Initialize service functionalities
|
||||
this.removeInstance();
|
||||
this.noConnection();
|
||||
this.delInstanceFiles();
|
||||
|
||||
// Load configuration settings
|
||||
Object.assign(this.db, configService.get<Database>('DATABASE'));
|
||||
Object.assign(this.redis, configService.get<Redis>('REDIS'));
|
||||
|
||||
// Initialize the MongoDB database instance
|
||||
this.dbInstance = this.db.ENABLED
|
||||
? this.repository.dbServer?.db(this.db.CONNECTION.DB_PREFIX_NAME + '-instances')
|
||||
: undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configuration settings for the database.
|
||||
* @private
|
||||
* @type {Partial<Database>}
|
||||
*/
|
||||
private readonly db: Partial<Database> = {};
|
||||
|
||||
/**
|
||||
* Configuration settings for Redis.
|
||||
* @private
|
||||
* @type {Partial<Redis>}
|
||||
*/
|
||||
private readonly redis: Partial<Redis> = {};
|
||||
|
||||
/**
|
||||
* Database instance for instance management.
|
||||
* @private
|
||||
* @type {Db}
|
||||
*/
|
||||
private dbInstance: Db;
|
||||
|
||||
/**
|
||||
* Database store connection.
|
||||
* @private
|
||||
* @type {Db}
|
||||
*/
|
||||
private dbStore = dbserver;
|
||||
|
||||
/**
|
||||
* Logger instance for logging service activities.
|
||||
* @private
|
||||
* @type {Logger}
|
||||
*/
|
||||
private readonly logger = new Logger(WAMonitoringService.name);
|
||||
|
||||
/**
|
||||
* A dictionary of WhatsApp instances being monitored.
|
||||
* @public
|
||||
* @type {Record<string, WAStartupService>}
|
||||
*/
|
||||
public readonly waInstances: Record<string, WAStartupService> = {};
|
||||
|
||||
/**
|
||||
* Initiates a timer to remove an instance after a specific time of inactivity.
|
||||
* @param {string} instance - The name of the instance to monitor.
|
||||
*/
|
||||
public delInstanceTime(instance: string) {
|
||||
const time = this.configService.get<DelInstance>('DEL_INSTANCE');
|
||||
if (typeof time === 'number' && time > 0) {
|
||||
this.logger.verbose(`Instance "${instance}" don't have connection, will be removed in ${time} minutes`);
|
||||
this.logger.verbose(`Instance "${instance}" doesn't have a connection, will be removed in ${time} minutes`);
|
||||
|
||||
setTimeout(async () => {
|
||||
if (this.waInstances[instance]?.connectionStatus?.state !== 'open') {
|
||||
@ -135,6 +189,13 @@ ocultado por francis fim */
|
||||
|
||||
// inserido por francis inicio
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves information about WhatsApp instances, including details about the connection, owner, and status.
|
||||
* @param instanceName The name of the WhatsApp instance to retrieve information for. Optional.
|
||||
* @returns An array of objects containing information about WhatsApp instances.
|
||||
* @throws NotFoundException If `instanceName` is specified, and the instance is not found.
|
||||
*/
|
||||
public async instanceInfo(instanceName?: string) {
|
||||
this.logger.verbose('get instance info');
|
||||
if (instanceName && !this.waInstances[instanceName]) {
|
||||
@ -220,7 +281,9 @@ public async instanceInfo(instanceName?: string) {
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Initializes a cron job to delete instance files at regular intervals.
|
||||
*/
|
||||
private delInstanceFiles() {
|
||||
this.logger.verbose('cron to delete instance files started');
|
||||
setInterval(async () => {
|
||||
@ -255,6 +318,10 @@ public async instanceInfo(instanceName?: string) {
|
||||
}, 3600 * 1000 * 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans data for a specific WhatsApp instance, including the database, cache, or files, depending on the configuration.
|
||||
* @param instanceName The name of the WhatsApp instance to clean up.
|
||||
*/
|
||||
public async cleaningUp(instanceName: string) {
|
||||
this.logger.verbose('cleaning up instance: ' + instanceName);
|
||||
if (this.db.ENABLED && this.db.SAVE_DATA.INSTANCE) {
|
||||
@ -278,6 +345,11 @@ public async instanceInfo(instanceName?: string) {
|
||||
rmSync(join(INSTANCE_DIR, instanceName), { recursive: true, force: true });
|
||||
}
|
||||
|
||||
/**
|
||||
* Cleans storage files for a specific WhatsApp instance, including messages, contacts, etc.
|
||||
*
|
||||
* @param instanceName The name of the WhatsApp instance to clean storage files for.
|
||||
*/
|
||||
public async cleaningStoreFiles(instanceName: string) {
|
||||
if (!this.db.ENABLED) {
|
||||
this.logger.verbose('cleaning store files instance: ' + instanceName);
|
||||
@ -314,7 +386,9 @@ public async instanceInfo(instanceName?: string) {
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads WhatsApp instances based on storage settings, such as Redis, a database, or local files.
|
||||
*/
|
||||
public async loadInstance() {
|
||||
this.logger.verbose('Loading instances');
|
||||
|
||||
@ -330,7 +404,11 @@ public async instanceInfo(instanceName?: string) {
|
||||
this.logger.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Configures a new WhatsApp instance and connects it to the WhatsApp service.
|
||||
*
|
||||
* @param name The name of the WhatsApp instance to configure.
|
||||
*/
|
||||
private async setInstance(name: string) {
|
||||
const instance = new WAStartupService(this.configService, this.eventEmitter, this.repository, this.cache);
|
||||
instance.instanceName = name;
|
||||
@ -394,7 +472,11 @@ public async instanceInfo(instanceName?: string) {
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a WhatsApp instance from memory and performs associated data cleanup.
|
||||
*
|
||||
* @param instanceName The name of the WhatsApp instance to remove.
|
||||
*/
|
||||
private removeInstance() {
|
||||
this.eventEmitter.on('remove.instance', async (instanceName: string) => {
|
||||
this.logger.verbose('remove instance: ' + instanceName);
|
||||
@ -423,7 +505,9 @@ public async instanceInfo(instanceName?: string) {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks for WhatsApp instances without a connection and takes appropriate actions, such as logout and connection closure.
|
||||
*/
|
||||
private noConnection() {
|
||||
this.logger.verbose('checking instances without connection');
|
||||
this.eventEmitter.on('no.connection', async (instanceName) => {
|
||||
|
@ -1,6 +1,8 @@
|
||||
/* eslint-disable @typescript-eslint/no-namespace */
|
||||
import { AuthenticationState, WAConnectionState } from '@whiskeysockets/baileys';
|
||||
|
||||
/**
|
||||
* Enumeration of various application events.
|
||||
*/
|
||||
export enum Events {
|
||||
APPLICATION_STARTUP = 'application.startup',
|
||||
QRCODE_UPDATED = 'qrcode.updated',
|
||||
@ -28,13 +30,23 @@ export enum Events {
|
||||
CHAMA_AI_ACTION = 'chama-ai.action',
|
||||
}
|
||||
|
||||
/**
|
||||
* Namespace containing various WhatsApp-related types.
|
||||
*/
|
||||
export declare namespace wa {
|
||||
/**
|
||||
* Represents a QR code for pairing with WhatsApp.
|
||||
*/
|
||||
export type QrCode = {
|
||||
count?: number;
|
||||
pairingCode?: string;
|
||||
base64?: string;
|
||||
code?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents information about a WhatsApp instance.
|
||||
*/
|
||||
export type Instance = {
|
||||
qrcode?: QrCode;
|
||||
pairingCode?: string;
|
||||
@ -45,6 +57,9 @@ export declare namespace wa {
|
||||
profilePictureUrl?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents local webhook settings.
|
||||
*/
|
||||
export type LocalWebHook = {
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
@ -52,6 +67,9 @@ export declare namespace wa {
|
||||
webhook_by_events?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents local Chatwoot settings.
|
||||
*/
|
||||
export type LocalChatwoot = {
|
||||
enabled?: boolean;
|
||||
account_id?: string;
|
||||
@ -64,6 +82,9 @@ export declare namespace wa {
|
||||
conversation_pending?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents local settings.
|
||||
*/
|
||||
export type LocalSettings = {
|
||||
reject_call?: boolean;
|
||||
msg_call?: string;
|
||||
@ -73,22 +94,34 @@ export declare namespace wa {
|
||||
read_status?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents local WebSocket settings.
|
||||
*/
|
||||
export type LocalWebsocket = {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents local RabbitMQ settings.
|
||||
*/
|
||||
export type LocalRabbitmq = {
|
||||
enabled?: boolean;
|
||||
events?: string[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a session within a Typebot instance.
|
||||
*/
|
||||
type Session = {
|
||||
remoteJid?: string;
|
||||
sessionId?: string;
|
||||
createdAt?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents local Typebot settings.
|
||||
*/
|
||||
export type LocalTypebot = {
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
@ -101,11 +134,17 @@ export declare namespace wa {
|
||||
sessions?: Session[];
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents local proxy settings.
|
||||
*/
|
||||
export type LocalProxy = {
|
||||
enabled?: boolean;
|
||||
proxy?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents local Chamaai settings.
|
||||
*/
|
||||
export type LocalChamaai = {
|
||||
enabled?: boolean;
|
||||
url?: string;
|
||||
@ -114,17 +153,29 @@ export declare namespace wa {
|
||||
answerByAudio?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents the state of a connection with a WhatsApp instance.
|
||||
*/
|
||||
export type StateConnection = {
|
||||
instance?: string;
|
||||
state?: WAConnectionState | 'refused';
|
||||
statusReason?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
* Represents a status message type.
|
||||
*/
|
||||
export type StatusMessage = 'ERROR' | 'PENDING' | 'SERVER_ACK' | 'DELIVERY_ACK' | 'READ' | 'DELETED' | 'PLAYED';
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of media message types.
|
||||
*/
|
||||
export const TypeMediaMessage = ['imageMessage', 'documentMessage', 'audioMessage', 'videoMessage', 'stickerMessage'];
|
||||
|
||||
/**
|
||||
* Array of message subtype types.
|
||||
*/
|
||||
export const MessageSubtype = [
|
||||
'ephemeralMessage',
|
||||
'documentWithCaptionMessage',
|
||||
|
Loading…
Reference in New Issue
Block a user