init project evolution api

This commit is contained in:
Davidson Gomes
2023-06-09 07:48:59 -03:00
commit 2a1c426311
90 changed files with 9820 additions and 0 deletions

255
src/config/env.config.ts Normal file
View File

@@ -0,0 +1,255 @@
import { readFileSync } from 'fs';
import { load } from 'js-yaml';
import { join } from 'path';
import { SRC_DIR } from './path.config';
export type HttpServer = { TYPE: 'http' | 'https'; PORT: number };
export type HttpMethods = 'POST' | 'GET' | 'PUT' | 'DELETE';
export type Cors = {
ORIGIN: string[];
METHODS: HttpMethods[];
CREDENTIALS: boolean;
};
export type LogLevel = 'ERROR' | 'WARN' | 'DEBUG' | 'INFO' | 'LOG' | 'VERBOSE' | 'DARK';
export type Log = {
LEVEL: LogLevel[];
COLOR: boolean;
};
export type SaveData = {
INSTANCE: boolean;
OLD_MESSAGE: boolean;
NEW_MESSAGE: boolean;
MESSAGE_UPDATE: boolean;
CONTACTS: boolean;
CHATS: boolean;
};
export type StoreConf = {
CLEANING_INTERVAL: number;
MESSAGES: boolean;
CONTACTS: boolean;
CHATS: boolean;
};
export type DBConnection = {
URI: string;
DB_PREFIX_NAME: string;
};
export type Database = {
CONNECTION: DBConnection;
ENABLED: boolean;
SAVE_DATA: SaveData;
};
export type Redis = {
ENABLED: boolean;
URI: string;
PREFIX_KEY: string;
};
export type EventsWebhook = {
APPLICATION_STARTUP: boolean;
QRCODE_UPDATED: boolean;
MESSAGES_SET: boolean;
MESSAGES_UPSERT: boolean;
MESSAGES_UPDATE: boolean;
SEND_MESSAGE: boolean;
CONTACTS_SET: boolean;
CONTACTS_UPDATE: boolean;
CONTACTS_UPSERT: boolean;
PRESENCE_UPDATE: boolean;
CHATS_SET: boolean;
CHATS_UPDATE: boolean;
CHATS_DELETE: boolean;
CHATS_UPSERT: boolean;
CONNECTION_UPDATE: boolean;
GROUPS_UPSERT: boolean;
GROUP_UPDATE: boolean;
GROUP_PARTICIPANTS_UPDATE: boolean;
NEW_JWT_TOKEN: boolean;
};
export type ApiKey = { KEY: string };
export type Jwt = { EXPIRIN_IN: number; SECRET: string };
export type Instance = {
NAME: string;
WEBHOOK_URL: string;
MODE: string;
WEBHOOK_BY_EVENTS: boolean;
};
export type Auth = {
API_KEY: ApiKey;
JWT: Jwt;
TYPE: 'jwt' | 'apikey';
INSTANCE: Instance;
};
export type DelInstance = number | boolean;
export type GlobalWebhook = {
URL: string;
ENABLED: boolean;
WEBHOOK_BY_EVENTS: boolean;
};
export type SslConf = { PRIVKEY: string; FULLCHAIN: string };
export type Webhook = { GLOBAL?: GlobalWebhook; EVENTS: EventsWebhook };
export type ConfigSessionPhone = { CLIENT: string; NAME: string };
export type QrCode = { LIMIT: number };
export type Production = boolean;
export interface Env {
SERVER: HttpServer;
CORS: Cors;
SSL_CONF: SslConf;
STORE: StoreConf;
DATABASE: Database;
REDIS: Redis;
LOG: Log;
DEL_INSTANCE: DelInstance;
WEBHOOK: Webhook;
CONFIG_SESSION_PHONE: ConfigSessionPhone;
QRCODE: QrCode;
AUTHENTICATION: Auth;
PRODUCTION?: Production;
}
export type Key = keyof Env;
export class ConfigService {
constructor() {
this.loadEnv();
}
private env: Env;
public get<T = any>(key: Key) {
return this.env[key] as T;
}
private loadEnv() {
this.env = !(process.env?.DOCKER_ENV === 'true') ? this.envYaml() : this.envProcess();
this.env.PRODUCTION = process.env?.NODE_ENV === 'PROD';
if (process.env?.DOCKER_ENV === 'true') {
this.env.SERVER.TYPE = 'http';
this.env.SERVER.PORT = Number.parseInt(process.env?.SERVER_PORT ?? '8080');
}
}
private envYaml(): Env {
return load(readFileSync(join(SRC_DIR, 'env.yml'), { encoding: 'utf-8' })) as Env;
}
private envProcess(): Env {
return {
SERVER: {
TYPE: process.env.SERVER_TYPE as 'http' | 'https',
PORT: Number.parseInt(process.env.SERVER_PORT),
},
CORS: {
ORIGIN: process.env.CORS_ORIGIN.split(','),
METHODS: process.env.CORS_METHODS.split(',') as HttpMethods[],
CREDENTIALS: process.env?.CORS_CREDENTIALS === 'true',
},
SSL_CONF: {
PRIVKEY: process.env?.SSL_CONF_PRIVKEY,
FULLCHAIN: process.env?.SSL_CONF_FULLCHAIN,
},
STORE: {
CLEANING_INTERVAL: Number.isInteger(process.env?.STORE_CLEANING_TERMINAL)
? Number.parseInt(process.env.STORE_CLEANING_TERMINAL)
: undefined,
MESSAGES: process.env?.STORE_MESSAGE === 'true',
CONTACTS: process.env?.STORE_CONTACTS === 'true',
CHATS: process.env?.STORE_CHATS === 'true',
},
DATABASE: {
CONNECTION: {
URI: process.env.DATABASE_CONNECTION_URI,
DB_PREFIX_NAME: process.env.DATABASE_CONNECTION_DB_PREFIX_NAME,
},
ENABLED: process.env?.DATABASE_ENABLED === 'true',
SAVE_DATA: {
INSTANCE: process.env?.DATABASE_SAVE_DATA_INSTANCE === 'true',
OLD_MESSAGE: process.env?.DATABASE_SAVE_DATA_OLD_MESSAGE === 'true',
NEW_MESSAGE: process.env?.DATABASE_SAVE_DATA_NEW_MESSAGE === 'true',
MESSAGE_UPDATE: process.env?.DATABASE_SAVE_MESSAGE_UPDATE === 'true',
CONTACTS: process.env?.DATABASE_SAVE_DATA_CONTACTS === 'true',
CHATS: process.env?.DATABASE_SAVE_DATA_CHATS === 'true',
},
},
REDIS: {
ENABLED: process.env?.REDIS_ENABLED === 'true',
URI: process.env.REDIS_URI,
PREFIX_KEY: process.env.REDIS_PREFIX_KEY,
},
LOG: {
LEVEL: process.env?.LOG_LEVEL.split(',') as LogLevel[],
COLOR: process.env?.LOG_COLOR === 'true',
},
DEL_INSTANCE:
typeof process.env?.DEL_INSTANCE === 'boolean'
? process.env.DEL_INSTANCE === 'true'
: Number.parseInt(process.env.DEL_INSTANCE),
WEBHOOK: {
GLOBAL: {
URL: process.env?.WEBHOOK_GLOBAL_URL,
ENABLED: process.env?.WEBHOOK_GLOBAL_ENABLED === 'true',
WEBHOOK_BY_EVENTS: process.env?.WEBHOOK_GLOBAL_WEBHOOK_BY_EVENTS === 'true',
},
EVENTS: {
APPLICATION_STARTUP: process.env?.WEBHOOK_EVENTS_APPLICATION_STARTUP === 'true',
QRCODE_UPDATED: process.env?.WEBHOOK_EVENTS_QRCODE_UPDATED === 'true',
MESSAGES_SET: process.env?.WEBHOOK_EVENTS_MESSAGES_SET === 'true',
MESSAGES_UPSERT: process.env?.WEBHOOK_EVENTS_MESSAGES_UPSERT === '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_UPDATE: process.env?.WEBHOOK_EVENTS_CONTACTS_UPDATE === 'true',
CONTACTS_UPSERT: process.env?.WEBHOOK_EVENTS_CONTACTS_UPSERT === 'true',
PRESENCE_UPDATE: process.env?.WEBHOOK_EVENTS_PRESENCE_UPDATE === 'true',
CHATS_SET: process.env?.WEBHOOK_EVENTS_CHATS_SET === 'true',
CHATS_UPDATE: process.env?.WEBHOOK_EVENTS_CHATS_UPDATE === 'true',
CHATS_UPSERT: process.env?.WEBHOOK_EVENTS_CHATS_UPSERT === 'true',
CHATS_DELETE: process.env?.WEBHOOK_EVENTS_CHATS_DELETE === 'true',
CONNECTION_UPDATE: process.env?.WEBHOOK_EVENTS_CONNECTION_UPDATE === 'true',
GROUPS_UPSERT: process.env?.WEBHOOK_EVENTS_GROUPS_UPSERT === 'true',
GROUP_UPDATE: process.env?.WEBHOOK_EVENTS_GROUPS_UPDATE === 'true',
GROUP_PARTICIPANTS_UPDATE:
process.env?.WEBHOOK_EVENTS_GROUP_PARTICIPANTS_UPDATE === 'true',
NEW_JWT_TOKEN: process.env?.WEBHOOK_EVENTS_NEW_JWT_TOKEN === 'true',
},
},
CONFIG_SESSION_PHONE: {
CLIENT: process.env?.CONFIG_SESSION_PHONE_CLIENT,
NAME: process.env?.CONFIG_SESSION_PHONE_NAME,
},
QRCODE: {
LIMIT: Number.parseInt(process.env.QRCODE_LIMIT),
},
AUTHENTICATION: {
TYPE: process.env.AUTHENTICATION_TYPE as 'jwt',
API_KEY: {
KEY: process.env.AUTHENTICATION_API_KEY,
},
JWT: {
EXPIRIN_IN: Number.isInteger(process.env?.AUTHENTICATION_JWT_EXPIRIN_IN)
? Number.parseInt(process.env.AUTHENTICATION_JWT_EXPIRIN_IN)
: 3600,
SECRET: process.env.AUTHENTICATION_JWT_SECRET,
},
INSTANCE: {
NAME: process.env.AUTHENTICATION_INSTANCE_NAME,
WEBHOOK_URL: process.env.AUTHENTICATION_INSTANCE_WEBHOOK_URL,
MODE: process.env.AUTHENTICATION_INSTANCE_MODE,
WEBHOOK_BY_EVENTS:
process.env.AUTHENTICATION_INSTANCE_WEBHOOK_BY_EVENTS === 'true',
},
},
};
}
}
export const configService = new ConfigService();

View File

@@ -0,0 +1,21 @@
import { Logger } from './logger.config';
export function onUnexpectedError() {
process.on('uncaughtException', (error, origin) => {
const logger = new Logger('uncaughtException');
logger.error({
origin,
stderr: process.stderr.fd,
error,
});
});
process.on('unhandledRejection', (error, origin) => {
const logger = new Logger('unhandledRejection');
logger.error({
origin,
stderr: process.stderr.fd,
error,
});
});
}

View File

@@ -0,0 +1,7 @@
import EventEmitter2 from 'eventemitter2';
export const eventEmitter = new EventEmitter2({
delimiter: '.',
newListener: false,
ignoreErrors: false,
});

137
src/config/logger.config.ts Normal file
View File

@@ -0,0 +1,137 @@
import { configService, Log } from './env.config';
import dayjs from 'dayjs';
const formatDateLog = (timestamp: number) =>
dayjs(timestamp)
.toDate()
.toString()
.replace(/\sGMT.+/, '');
enum Color {
LOG = '\x1b[32m',
INFO = '\x1b[34m',
WARN = '\x1b[33m',
ERROR = '\x1b[31m',
DEBUG = '\x1b[36m',
VERBOSE = '\x1b[37m',
DARK = '\x1b[30m',
}
enum Command {
RESET = '\x1b[0m',
BRIGHT = '\x1b[1m',
UNDERSCORE = '\x1b[4m',
}
enum Level {
LOG = Color.LOG + '%s' + Command.RESET,
DARK = Color.DARK + '%s' + Command.RESET,
INFO = Color.INFO + '%s' + Command.RESET,
WARN = Color.WARN + '%s' + Command.RESET,
ERROR = Color.ERROR + '%s' + Command.RESET,
DEBUG = Color.DEBUG + '%s' + Command.RESET,
VERBOSE = Color.VERBOSE + '%s' + Command.RESET,
}
enum Type {
LOG = 'LOG',
WARN = 'WARN',
INFO = 'INFO',
DARK = 'DARK',
ERROR = 'ERROR',
DEBUG = 'DEBUG',
VERBOSE = 'VERBOSE',
}
enum Background {
LOG = '\x1b[42m',
INFO = '\x1b[44m',
WARN = '\x1b[43m',
DARK = '\x1b[40m',
ERROR = '\x1b[41m',
DEBUG = '\x1b[46m',
VERBOSE = '\x1b[47m',
}
export class Logger {
private readonly configService = configService;
constructor(private context = 'Logger') {}
public setContext(value: string) {
this.context = value;
}
private console(value: any, type: Type) {
const types: Type[] = [];
this.configService.get<Log>('LOG').LEVEL.forEach((level) => types.push(Type[level]));
const typeValue = typeof value;
if (types.includes(type)) {
if (configService.get<Log>('LOG').COLOR) {
console.log(
/*Command.UNDERSCORE +*/ Command.BRIGHT + Level[type],
'[Evolution API]',
Command.BRIGHT + Color[type],
process.pid.toString(),
Command.RESET,
Command.BRIGHT + Color[type],
'-',
Command.BRIGHT + Color.VERBOSE,
`${formatDateLog(Date.now())} `,
Command.RESET,
Color[type] + Background[type] + Command.BRIGHT,
`${type} ` + Command.RESET,
Color.WARN + Command.BRIGHT,
`[${this.context}]` + Command.RESET,
Color[type] + Command.BRIGHT,
`[${typeValue}]` + Command.RESET,
Color[type],
typeValue !== 'object' ? value : '',
Command.RESET,
);
typeValue === 'object' ? console.log(/*Level.DARK,*/ value, '\n') : '';
} else {
console.log(
'[Evolution API]',
process.pid.toString(),
'-',
`${formatDateLog(Date.now())} `,
`${type} `,
`[${this.context}]`,
`[${typeValue}]`,
value,
);
}
}
}
public log(value: any) {
this.console(value, Type.LOG);
}
public info(value: any) {
this.console(value, Type.INFO);
}
public warn(value: any) {
this.console(value, Type.WARN);
}
public error(value: any) {
this.console(value, Type.ERROR);
}
public verbose(value: any) {
this.console(value, Type.VERBOSE);
}
public debug(value: any) {
this.console(value, Type.DEBUG);
}
public dark(value: any) {
this.console(value, Type.DARK);
}
}

View File

@@ -0,0 +1,6 @@
import { join } from 'path';
export const ROOT_DIR = process.cwd();
export const INSTANCE_DIR = join(ROOT_DIR, 'instances');
export const SRC_DIR = join(ROOT_DIR, 'src');
export const AUTH_DIR = join(ROOT_DIR, 'store', 'auth');