/* eslint-disable max-len */
/* eslint-disable global-require */
/* eslint-disable import/no-dynamic-require */
/* Requires - Importações essenciais para o funcionamento do módulo */
const fs = require('fs');
const path = require('path');
const { performance } = require('perf_hooks');
const { pathToFileURL } = require('url');
const { LRUCache } = require('lru-cache');
/**
* Log de performance padronizado
* @param {string} type - Tipo de carregamento
* @param {string} pathy - Caminho do módulo
* @param {number} start - Timestamp de início
*/
function logPerformance(type, pathy, start) {
const time = (performance.now() - start).toFixed(2);
const memory = (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2);
if (global?.config?.logIndexer?.value) console.debug(`[${type} LOAD] ${pathy} - ${time}ms | ${memory} MB`);
}
/**
* Carrega arquivos de configuração com tratamento de erros e retentativas
* @param {string} configPath - Caminho do arquivo de configuração
* @returns {object} Objeto JSON carregado
* @throws {Error} Se o arquivo não puder ser carregado após retentativas
*/
function loadConfigWithRetry(configPath, retries = 3, delay = 100) {
/* Tenta dar parse no JSON */
try {
return JSON.parse(fs.readFileSync(configPath));
/* Se der qualquer errinho */
} catch (err) {
/* E tentativas ainda for superior a zero */
if (retries > 0) {
/* Avisa da tentativa */
if (global?.config?.logIndexer?.value) console.warn(`[CONFIG] Retentativa para carregar ${configPath}...`);
/* Espera um certo tempo */
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, delay);
/* Tenta de novo */
return loadConfigWithRetry(configPath, retries - 1, delay * 2);
}
/* Dropa um erro se não conseguir carregar mesmo após tantas tentativas */
throw new Error(`Falha ao carregar configuração: ${configPath}`);
}
}
/**
* Cache de módulos com política LRU (Least Recently Used), mais eficiente que um Map simples.
*
* @constant
* @type {LRU}
* @property {number} max - Número máximo de itens no cache.
* @property {number} maxSize - Tamanho máximo do cache em bytes.
* @property {function} sizeCalculation - Função que calcula o tamanho de cada item em bytes.
* @property {number} ttl - Tempo de vida dos itens no cache (em milissegundos).
*
*/
const commandCache = new LRUCache({
max: 100,
maxSize: 50 * 1024 * 1024,
sizeCalculation: (value) => JSON.stringify(value).length,
ttl: 1000 * 60 * 15,
});
/* JSON's | Utilidades - Arquivos de configuração com carregamento seguro */
const commandPlaces = loadConfigWithRetry('./lib/Databases/Configurations/symlinks.json');
/* Configurações globais - Carregamento seguro com fallback */
global.config = global.config || loadConfigWithRetry('./lib/Databases/Configurations/config.json');
global.irisPath = global.irisPath || process.cwd();
global.region = global.region || 'pt';
global.logging = global.logging || require('./Initialize/logging');
/**
* Valida e sanitiza nomes de comandos para prevenir injeção
* @param {string} commandName - Nome do comando a ser validado
* @returns {string} Nome sanitizado
*/
function validateCommandName(commandName) {
if (typeof commandName !== 'string') return 'Default';
return commandName.replace(/[^a-z0-9_-]/gi, '').toLowerCase();
}
/**
* Junção segura de caminhos para prevenir directory traversal
* @param {string} base - Caminho base
* @param {...string} paths - Partes do caminho
* @returns {string} Caminho seguro
* @throws {Error} Se tentativa de directory traversal for detectada
*/
function safePathJoin(base, ...paths) {
/* Sanitiza os paths por meio de mapear seus locais */
const sanitizedPaths = paths.map((p) => {
/* Primeiro: Normalização */
const normalized = path.normalize(p);
/* Se tiver um exploit de ataque */
if (normalized.match(/(^|\/)\.\.($|\/)/)) {
/* Dropa um erro dizendo que é exploit */
throw new Error('[Directory Traversal] NOT ALLOWED OPERATION! DANGER!');
}
/* Retorna normalizado */
return normalized;
});
/* Define os paths corretos, sanitizados e resolvidos */
const fullPath = path.resolve(base, ...sanitizedPaths);
const resolvedBase = path.resolve(base);
/* Verificação mais rigorosa de caminho seguro */
if (!fullPath.startsWith(resolvedBase)) {
/* Vai que tenta acessar via classico .. no meio do local, pode causar vunerabilidades */
throw new Error(`Tentativa de acesso a caminho não permitido: ${fullPath}`);
}
/* Retorna o local completo e corrigido */
return fullPath;
}
/**
* Atualiza o arquivo symlinks.json com novos comandos de forma atômica
* @param {string} commandName - Nome do novo comando
* @throws {Error} Se a escrita falhar
*/
function updateSymlinks(commandName) {
/* Define os locais de JSON corretos */
const tempPath = './lib/Databases/Configurations/symlinks.temp.json';
const finalPath = './lib/Databases/Configurations/symlinks.json';
/* Define os dados que vai salvar */
commandPlaces[commandName] = {
alias: [commandName],
place: `./Functions/${commandName}`,
};
/* Escrita atômica para prevenir corrupção de arquivo */
fs.writeFileSync(tempPath, JSON.stringify(commandPlaces, null, 4));
/* Ai é só renomear para o nome correto */
fs.renameSync(tempPath, finalPath);
}
/**
* Lida com comandos não mapeados com cache local
* @param {string} commandName - Nome do comando
* @returns {Array<string>} Nome da pasta do comando
*/
function handleUnmappedCommand(commandName) {
/* Cache local para evitar I/O repetido e mais consumo */
if (handleUnmappedCommand.cache.has(commandName)) {
/* Retorna os dados via cache */
return handleUnmappedCommand.cache.get(commandName);
}
/* Define o let que armazenará o diretorio temporariamente */
let tempFolders = [];
/* Tenta via try catch para caso o local seja restrito ou dê erros */
try {
/* Faz a leitura dos nomes de pastas */
tempFolders = (fs.readdirSync('./lib/Functions')
.map((s) => s.toLowerCase())
);
/* Se der mesmo erro, aponta ele com detalhes */
} catch (err) {
/* E retorna o uso para a default */
if (global?.config?.logIndexer?.value) console.warn(`[WARN] Pasta Functions não encontrada: ${err.message}`);
return ['Default'];
}
/* Mas se der certo, atualiza os symlinks para as pastas corretas */
const result = (tempFolders.includes(commandName)
? (updateSymlinks(commandName), [commandName])
: ['Default']
);
/* Depois só coloca em cache e retorna */
handleUnmappedCommand.cache.set(commandName, result);
return result;
}
/* Cria um cachezinho de handleUnmappedCommand da função acima */
handleUnmappedCommand.cache = new Map();
/**
* Resolve o caminho completo para o comando com verificações
* @param {Array<string>} commandFolder - Nome da pasta do comando
* @returns {string} Caminho resolvido
* @throws {Error} Se o caminho for inválido
*/
function resolveCommandPath(commandFolder) {
/* Define a pasta para localizar */
const [folder] = commandFolder;
/* Se NÃO encontrar certinho um dado dropa um erro */
if (!commandPlaces[folder]) { throw new Error(`Configuração não encontrada para: ${folder}`); }
/* Resolve o local do comando e faz um join seguro */
const commandPath = path.resolve(__dirname, commandPlaces[folder].place);
return safePathJoin(__dirname, path.normalize(commandPath));
}
/**
* Carrega o módulo do comando com cache e circuit breaker
* @param {string} commandPath - Caminho do módulo
* @returns {Object} Módulo carregado
* @throws {Error} Se o carregamento falhar
*/
function loadCommandModule(commandPath) {
/* Circuit Breaker: Verifica se o módulo está em estado de falha */
if (loadCommandModule.failedModules.has(commandPath)) { throw new Error(`Módulo ${commandPath} está em estado de falha`); }
/* Tenta com try catch para caso haja falhas adicionais */
try {
/* Verificação de cache com LRU */
if (commandCache.has(commandPath)) {
/* Se tiver ele, apenas retorna uma mensagem e então diz que achou em cache */
if (global?.config?.logIndexer?.value) console.debug(`[CACHE HIT] ${commandPath}`);
return commandCache.get(commandPath);
}
/* Define a contagem de perfomance atual */
const start = performance.now();
/* Tenta carregar o módulo e definir seus dados */
const Sys = require(commandPath);
const module = Sys[Object.keys(Sys)[0]];
/* Insere em cache e printa o resultado da perfomance */
commandCache.set(commandPath, module);
logPerformance('SYNC', commandPath, start);
/* Retorna os dados */
return module;
/* Caso dê erro nesse módulo */
} catch (err) {
/* Circuit Breaker: Marca módulo como falho */
loadCommandModule.failedModules.set(commandPath, Date.now());
/* Dropa um erro */
throw err;
}
}
/* Cra um cache para os módulos que falharam */
loadCommandModule.failedModules = new Map();
/**
* Carrega módulo de forma assíncrona com tratamento moderno
* @param {string} commandPath - Caminho do módulo
* @returns {Promise<Object>} Promise com o módulo
* @throws {Error} Se o carregamento falhar
*/
async function loadCommandModuleAsync(commandPath) {
/* Circuit Breaker: Verifica se o módulo está em estado de falha */
if (loadCommandModule.failedModules.has(commandPath)) {
throw new Error(`Módulo ${commandPath} está em estado de falha`);
}
/* Tenta executar dentro de try-catch para lidar com falhas */
try {
/* Verificação de cache com LRU */
if (commandCache.has(commandPath)) {
/* Se o módulo estiver no cache, retorna o módulo */
if (global?.config?.logIndexer?.value) console.debug(`[ASYNC CACHE HIT] ${commandPath}`);
return commandCache.get(commandPath);
}
/* Define o início da contagem de performance */
const start = performance.now();
/* Tenta carregar o módulo de forma assíncrona e obter seus dados */
const { default: Sys } = await import(pathToFileURL(commandPath));
const module = Sys[Object.keys(Sys)[0]];
/* Insere o módulo carregado no cache */
commandCache.set(commandPath, module);
/* Registra a performance do carregamento assíncrono */
logPerformance('ASYNC', commandPath, start);
/* Retorna o módulo carregado */
return module;
/* Se ocorrer qualquer erro ao carregar o módulo */
} catch (err) {
/* Circuit Breaker: Marca o módulo como falho */
loadCommandModule.failedModules.set(commandPath, Date.now());
/* Propaga o erro */
throw err;
}
}
/**
* Tratamento de erros avançado com suporte a analytics
* @param {Error} error - Objeto de erro
* @param {string} [context] - Contexto adicional
*/
function handleSystemError(error, context = '') {
/* Define os dados de erro */
const errorData = {
timestamp: new Date().toISOString(),
message: error.message,
stack: error.stack,
context,
memoryUsage: process.memoryUsage(),
};
/* Printa ele de forma especial */
console.error('[SYSTEM ERROR]', errorData);
/* Se logging global estiver disponível */
if (global.logging && global.logging.echoError) {
/* Envia para analytics */
global.logging.echoError(error, false, __dirname);
}
}
/**
* Controlador principal do sistema
* @param {string} systemName - Nome do sistema/comando
* @returns {Object|boolean} Módulo carregado ou false
*/
function controlSystem(systemName = 'Default') {
/* Define o início da contagem de performance */
const startTime = performance.now();
/* Try catch para mais e mais erros absurdos */
try {
/* Valida o nome do comando para evitar exploits */
const toyBox = validateCommandName(systemName);
/* Busca o local do comando com base no alias */
let commandFolder = (Object.keys(commandPlaces)
.filter((objname) => commandPlaces[objname].alias.includes(toyBox))
);
/* Caso o local não seja encontrado, tenta mapear o comando */
if (commandFolder.length === 0) { commandFolder = handleUnmappedCommand(toyBox); }
/* Resolve o caminho do comando para carregamento */
const commandPath = resolveCommandPath(commandFolder);
/* Carrega o módulo usando o carregador principal */
const module = loadCommandModule(commandPath);
/* Registra a performance do carregamento */
logPerformance('PERF', systemName, startTime);
/* Retorna o módulo carregado */
return module;
/* Caso ocorra algum erro no processo */
} catch (error) {
/* Trata o erro e registra informações úteis */
handleSystemError(error, `controlSystem: ${systemName}`);
/* Retorna false para indicar falha */
return false;
}
}
/**
* Versão assíncrona com suporte a hot reload
* @param {string} systemName - Nome do sistema/comando
* @param {boolean} [bypassCache=false] - Ignora cache para hot reload
* @returns {Promise<Object|boolean>} Promise com o módulo ou false
*/
async function controlSystemAsync(systemName = 'Default', bypassCache = false) {
/* Define o início da contagem de performance */
const startTime = performance.now();
/* Try catch para mais e mais erros absurdos */
try {
/* Valida o nome do comando para evitar exploits */
const toyBox = validateCommandName(systemName);
/* Busca o local do comando com base no alias */
let commandFolder = (Object.keys(commandPlaces)
.filter((objname) => commandPlaces[objname].alias.includes(toyBox))
);
/* Caso o local não seja encontrado, tenta mapear o comando */
if (commandFolder.length === 0) { commandFolder = handleUnmappedCommand(toyBox); }
/* Resolve o caminho do comando para carregamento */
const commandPath = resolveCommandPath(commandFolder);
/* Caso o hot reload seja habilitado, limpa o cache */
if (bypassCache) { commandCache.delete(commandPath); }
/* Carrega o módulo de forma assíncrona */
const module = await loadCommandModuleAsync(commandPath);
/* Registra a performance do carregamento assíncrono */
logPerformance('ASYNC PERF', systemName, startTime);
/* Retorna o módulo carregado */
return module;
/* Caso ocorra algum erro no processo */
} catch (error) {
/* Trata o erro e registra informações úteis */
handleSystemError(error, `controlSystemAsync: ${systemName}`);
/* Retorna false para indicar falha */
return false;
}
}
/* Mensagem de inicialização com diagnóstico completo */
if (global?.config?.logIndexer?.value) {
console.debug('[SYSTEM BOOT]', {
status: 'ready',
memoryUsage: `${(process.memoryUsage().heapUsed / 1024 / 1024).toFixed(2)} MB`,
cachedCommands: commandCache.size,
nodeVersion: process.version,
});
}
/* Exporta a API com TypeScript-like types via JSDoc */
module.exports = Object.assign(
(systemName) => controlSystem(systemName),
{
controlSystem,
controlSystemAsync,
_internal: {
validateCommandName,
safePathJoin,
resolveCommandPath,
clearCache: () => commandCache.clear(),
getCacheStats: () => ({
size: commandCache.size,
hits: commandCache.hits,
misses: commandCache.misses,
}),
},
},
);