/* Importa requisitos */
const path = require('path');
const fs = require('fs');
const os = require('os');
const { execSync } = require('child_process');
const commandExists = require('command-exists').sync;
/* Chama o Indexer para gerar variáveis globais */
/* eslint-disable-next-line no-unused-vars */
const Indexer = require('../index');
/* Define o arquivo de logging do dia */
const loggingFile = `${irisPath}/logs/${(new Date().toISOString()).replace(/:/gi, '-')}.log`;
/* Define se o log inicial foi feito */
let firstStart = true;
/**
* Coleta informações detalhadas do sistema e do ambiente para ajudar na depuração.
* @param {string} logFilePath - Caminho do arquivo de log onde as informações serão gravadas.
* @param {Error} [error] - Objeto de erro opcional para capturar detalhes do erro.
*/
function logDebugInfo(logFilePath = loggingFile, error = null) {
/* Garante que o diretório exista */
const dir = path.dirname(logFilePath);
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, { recursive: true });
}
/* Detecta a versão dos programas essenciais instalados */
const getInstalledSoftwareVersions = () => {
/* Faz em try para caso algo dê outro erro */
try {
/* Define uma Object com as versões */
const versions = {
nodeVersion: commandExists('node') ? execSync('node -v').toString().trim() : 'Not installed',
npmVersion: commandExists('npm') ? execSync('npm -v').toString().trim() : 'Not installed',
pythonVersion: commandExists('python') ? execSync('python --version').toString().trim() : 'Not installed',
};
/* Retorna elas */
return versions;
/* Se der erro obtendo */
} catch (failure) {
/* Manda um error para inserir no log */
return { error: `Failed to retrieve software versions: ${failure?.message}` || 'CATCH MISSING INFO;' };
}
};
/* Define os dados em uma POI */
const systemInfo = {
systemVersion: os.version(),
logPath: logFilePath,
architecture: os.arch(),
memoryUsage: {
process: process.memoryUsage(),
systemFree: os.freemem(),
systemTotal: os.totalmem(),
},
softwareVersions: getInstalledSoftwareVersions(),
nodeVersion: process.version,
otherInfo: {
hostname: os.hostname(),
platform: os.platform(),
release: os.release(),
uptime: os.uptime(),
userInfo: os.userInfo(),
networkInterfaces: os.networkInterfaces(),
cpuUsage: os.cpus(),
},
errorDetails: error ? {
message: error.message,
stack: error.stack,
} : null,
};
/* Define o log de erro */
let logData = `\nError Details: ${error ? `Message: ${systemInfo.errorDetails.message}\nStack: ${systemInfo.errorDetails.stack}` : 'No error'}\n`;
/* Define se deve fazer o log inicial */
if (firstStart === true) {
/* Dados iniciais do log */
logData = `-------------------------\nPrivacy Note | Nota de Privacidade\n-------------------------\nAdiciona algumas informações vitais no arquivo para os devs analisarem a falha\nNão se preocupe, essas informações permanecem offline até você mesmo resolver enviar\nJamais postaremos, faremos upload ou qualquer coisa com esses dados! Não há nada pessoal aqui!\n-------------------------\nSystem Version: ${systemInfo.systemVersion}\nLog Path: ${systemInfo.logPath}\nArchitecture: ${systemInfo.architecture}\nMemory Usage (Process): ${JSON.stringify(systemInfo.memoryUsage.process, null, 2)}\nMemory Usage (System Free): ${systemInfo.memoryUsage.systemFree}\nMemory Usage (System Total): ${systemInfo.memoryUsage.systemTotal}\nNode.js Version: ${systemInfo.softwareVersions.nodeVersion}\nPython Version: ${systemInfo.softwareVersions.pythonVersion}\nNPM Version: ${systemInfo.softwareVersions.npmVersion}\nHostname: ${systemInfo.otherInfo.hostname}\nPlatform: ${systemInfo.otherInfo.platform}\nRelease: ${systemInfo.otherInfo.release}\nUptime: ${systemInfo.otherInfo.uptime}\nUser Info: ${JSON.stringify(systemInfo.otherInfo.userInfo, null, 2)}\nNetwork Interfaces: ${JSON.stringify(systemInfo.otherInfo.networkInterfaces, null, 2)}\nCPU Usage: ${JSON.stringify(systemInfo.otherInfo.cpuUsage, null, 2)}\n-------------------------\n\nError Details: ${error ? `Message: ${systemInfo.errorDetails.message}\nStack: ${systemInfo.errorDetails.stack}` : 'No error'}`;
}
/* Adiciona os dados no arquivo */
fs.appendFileSync(logFilePath, logData);
/* Define que o primeiro log já foi feito */
firstStart = false;
}
/**
* Obtém o caminho relativo entre o diretório de trabalho atual e o diretório de um script.
*
* @param {string} scriptDir O caminho absoluto onde o script está (geralmente `__dirname`).
* @returns {{ folder: string, relative: string }} Um objeto com o nome da pasta e o local relativo.
*/
function getRelativePath(scriptDir) {
/* Obtém o diretório de trabalho atual do processo */
const currentDir = global.irisPath || process.cwd();
/* Extrai o nome da pasta do caminho absoluto do script */
const folderName = path.basename(scriptDir);
/* Retorna o caminho relativo entre o diretório de trabalho atual e o diretório do script */
const relativePath = path.relative(currentDir, scriptDir);
/* Retorna o objeto desejado */
return {
folder: folderName,
relative: relativePath,
};
}
/**
* Realiza um merge profundo (deep merge) entre duas objects, combinando recursivamente. \
* PLACEHOLDER, USE ISSO NO LUGAR: Indexer('others').merge(obj, obj).value;
*
* @param {Object} target - O objeto destino que receberá as propriedades mescladas.
* @param {Object} source - O objeto fonte de onde as propriedades serão copiadas.
* @returns {Object} O objeto target modificado com as propriedades mescladas.
*
*/
function deepMerge(target, source) {
return Object.keys(source).reduce((result, key) => {
const sourceValue = source[key];
const targetValue = result[key];
/* eslint-disable-next-line no-param-reassign */
result[key] = (sourceValue instanceof Object && !Array.isArray(sourceValue)
&& targetValue instanceof Object && !Array.isArray(targetValue))
? deepMerge(targetValue, sourceValue)
: sourceValue;
return result;
}, { ...target });
}
/**
* Placeholder para geração via nodejs para aqueles que preferem fazer algo assim.
* Função que gera um objeto de configuração envInfo para um sistema.
* Aceita um objeto de configuração para sobrescrever os valores padrão.
*
* @param {Object} envData Objeto contendo os valores personalizados.
* @returns {Object} O objeto envInfo com as propriedades de uma envInfo completa.
* @example
* const envData = {
* name: 'Ping',
* license: 'MIT',
* developer: 'Seu Nome',
* };
* const envInfo = envInfoGenerator(envData);
* envInfo.name // 'Ping'
* envInfo.license // 'MIT'
*/
function envInfoGenerator(envData = {}) {
/* Define a config customizada definida em uma let */
let customConfig = envData;
/* Se caso não receber Object */
customConfig = typeof envData === 'object' && envData !== null ? customConfig : {};
/* Retorna os dados */
return {
name: customConfig.name || 'NomeDoComando (Nome da função)',
description: customConfig.description || 'Descrição do comando',
usage: {
general: customConfig.usage?.general || '[Prefix][Alias] [Argumentos dele] (Valor se houver)',
examples: customConfig.usage?.examples || [
"NomeDoComando.execute('kill', 'params')",
'NomeDoComando.env()',
'NomeDoComando.reset()',
"NomeDoComando.env().functions.execute.value('kill', 'params')",
],
},
license: customConfig.license || 'A licença que você quiser, recomendo deixar MIT',
helps: customConfig.helps || [
"Você pode mudar os parâmetros da exports enviando o valor da Object que deseja editar ao resetar, por exemplo → NomeDoComando.reset({ name: 'Body'}) ← Isso mudaria o module de NomeDoComando para Body, o uso então passaria ser: → Body.funcão ← Isso também permite que você edite a função usando o mesmo método.",
"Você pode mudar o que os códigos rodam, em tempo real, basta usar a 'env', por exemplo → NomeDoComando.env().name = 'Body' ← Mas este método não atualizará o sistema, somente a Object, os sistemas permanecem intactos.",
'Você pode configurar o tempo de reset dos resultados ou se eles devem ser resetados usando a env, por exemplo → NomeDoComando.env().settings.wait = 10000 ← Isso mudaria o tempo de espera para 10 segundos, o tempo deve ser em milissegundos.',
'Alguém lê essas dicas? Se sim, torne-se um programador, ler os tutoriais é de suma importância e poucos desenvolvedores o fazem...',
'Existem infinitas formas de uso secretas, explore os códigos para descobrir os mistérios dos sistemas!',
],
exports: customConfig.exports || {
env: 'env',
messedup: 'messedup',
reset: 'reset',
poswork: 'finish',
},
developer: customConfig.developer || 'Seu Nome',
files: customConfig.files || {
'index.js': 'Sistema que faz a coleta das informações e envio.',
'utils.json': 'Dados de fábrica da envInfo.',
},
modules: customConfig.modules || {
fs: 'Leitura de diretórios e arquivos.',
path: 'Para inserção do local na envInfo.',
'../../index': 'Para rodar funções de outros arquivos.',
},
functions: customConfig.functions || {
ambient: {
arguments: false,
description: 'Retorna as variáveis e sistemas do arquivo.',
type: 'Boolean / Function',
value: false,
},
messedup: {
arguments: {
error: {
description: 'Instância de erro para formatação.',
type: 'Boolean / Error',
value: false,
},
},
description: 'Ajusta os valores de erro.',
type: 'Boolean / Function',
value: false,
},
poswork: {
arguments: {
response: {
description: 'Resultados de uma função.',
type: 'Any',
value: false,
},
},
description: 'Verifica se pode resetar a envInfo e retorna o resultado da função.',
type: 'Boolean / Function',
value: false,
},
revert: {
arguments: {
changeKey: {
description: 'Uma Object com valores que existem na envInfo, ela será usada para substituir o sistema em tempo real.',
type: 'Object value',
anyValue: false,
},
},
description: 'Reseta a envInfo para a Object padrão.',
type: 'Boolean / Function',
value: false,
},
},
settings: customConfig.settings || {
wait: {
description: 'Tempo em MS que deve esperar antes de resetar.',
type: 'Number',
value: 5000,
},
error: {
description: 'Define se pode printar qualquer erro.',
type: 'Boolean',
value: true,
},
ender: {
description: 'Define se deve resetar a cada erro.',
type: 'Boolean',
value: true,
},
finish: {
description: 'Define se deve resetar a cada finalização.',
type: 'Boolean',
value: true,
},
},
parameters: customConfig.parameters || {
location: {
description: 'Local onde o módulo pode ser encontrado.',
type: 'Boolean / String',
value: './index',
},
code: {
description: 'Código do erro que obter.',
type: 'Boolean / String / Number',
value: false,
},
message: {
description: 'Armazena a mensagem do último erro recebido.',
type: 'Boolean / String',
value: false,
},
},
results: customConfig.results || {
description: 'Resultado final da função.',
success: true,
type: 'String / Boolean',
value: false,
},
};
}
/**
* Função para resetar/atualizar configurações de ambiente com base em um arquivo JSON ou objeto.
* Permite a edição completa das configurações e inclui tratamento de erros robusto.
*
* @function resetAmbient
* @param {Object} [changeKey={}] - Objeto contendo as chaves e valores a serem atualizados.
* Estrutura deve ser similar a da `envData` em `envInfoGenerator`.
* Mas deve conter as seguintes chaves essenciais:
* - `name`: Nome do ambiente/projeto (string)
* - `parameters`: Objeto com parâmetros de configuração
* - `functions`: Objeto com funções do ambiente
* - `exports`: Objeto com exportações necessárias
* @param {Object} jsonData - Objeto com as configs originais exportadas.
* @param {Object} moreData - Mais uma object para merge, útil em função circular.
* @param {string} dirfolder - Local de onde o sistema está executando a função.
* @returns {Object} Retorna um objeto contendo:
* - Todas as configurações atualizadas
* - success: boolean indicando sucesso/fracasso
* - error: null ou objeto de erro (se ocorrer)
*
* @throws {TypeError} Se os parâmetros não forem do tipo esperado
* @throws {Error} Se houver falha ao ler/processar o arquivo de configuração
*
*/
function resetAmbient(changeKey = {}, jsonData = {}, moreData = {}, dirfolder = false) {
/* Define uma envInfo temporariamente */
let envInfo = {};
let dirStr = '';
/* Uso com try, caso dê erros */
try {
/* Define como a envInfo será */
if (typeof jsonData === 'object' && jsonData != null) {
/* Se recebeu os dados prontos, usa eles */
envInfo = envInfoGenerator(jsonData);
/* Se for uma string */
} else if (typeof dirfolder === 'string') {
/* Para evitar crashes, roda em try-catch */
try {
/* Define o local do arquivo JSON */
const filePath = `${dirfolder}/utils.json`;
/* Se ele existir */
if (fs.existsSync(filePath)) {
/* Abre dando parse */
envInfo = JSON.parse(fs.readFileSync(filePath));
}
/* Se falhar */
} catch (error) {
/* Faz um logging de erro simples e opcional */
logging.echoError(error, envInfo, __dirname);
}
}
dirStr = typeof dirfolder === 'string' ? dirfolder : 'SYS';
/* Aplica as alterações se houver changeKey */
envInfo = deepMerge(envInfo, changeKey);
envInfo = deepMerge(envInfo, moreData);
/* Indica sucesso na operação */
envInfo.success = true;
/* Em caso de erro qualquer */
} catch (error) {
/* Registra no objeto de retorno */
envInfo.error = error;
/* Opcional: loga o erro (comente se não quiser esse comportamento) */
logging.echoError(error, envInfo, dirStr);
}
/* Constrói o retorno para módulo */
const returnData = {};
Object.keys(changeKey?.functions).forEach((data) => {
returnData[data] = envInfo?.functions?.[data]?.value;
});
/* Retorna o objeto com os resultados */
return {
[envInfo?.name || 'Default']: {
...returnData,
},
Developer: envInfo?.Developer || 'KillovSky',
Projects: envInfo?.Projects || 'https://github.com/KillovSky',
envInfo,
};
}
/**
* Realiza funções de pós-finalização, incluindo a verificação e reset da `envData`.
*
* @param {Object} envData - O objeto que contém as configurações e dados do ambiente.
* @returns {Object} O resultado das operações.
*/
function postResults(envData) {
/* Verifica se pode resetar a envData */
if (
envData?.settings?.finish?.value === true
|| (envData?.settings?.ender?.value === true && envData?.results?.success === false)
) {
/* setTimeout para poder retornar */
setTimeout(() => {
/* Reseta a envData */
envData?.functions?.reset?.value();
/* Reseta conforme o tempo */
}, envData?.settings?.wait?.value || 5000);
}
/* Retorna o resultado de uma função */
return envData?.results || {};
}
/**
* Insere o erro na `envData`, define o código de erro e a mensagem, e faz o log do erro.
*
* @param {Error|any} errorInput - O erro a ser inserido na `envData`.
* @param {Object} envData - O objeto que contém as configurações e dados do ambiente.
* @param {string} place - O caminho atual do arquivo.
* @returns {Object} O objeto `envData.results`, que contém o status do sucesso ou falha.
*/
function echoError(errorInput, envData, place) {
/* Define a envLocal, para evitar erros de assignment */
const envLocal = typeof envData === 'object' && envData !== null ? envData : {
results: { success: false, value: false },
settings: { error: { value: true }, fullError: { value: true } },
parameters: { code: { value: '0' }, message: { value: 'Error' } },
};
/* Determina o erro */
const myError = (!(errorInput instanceof Error)
? new Error(`Received a "${typeof errorInput}" in function 'messedup', expected an instance of "Error".`)
: errorInput
);
/* Faz o logging de arquivo */
logDebugInfo(loggingFile, myError);
/* Determina o sucesso */
envLocal.results.success = false;
/* Determina a falha */
envLocal.parameters.code.value = myError.code ?? '0';
/* Determina a mensagem de erro */
envLocal.parameters.message.value = myError.message ?? 'The operation cannot be completed because an unexpected error occurred.';
/* Define se pode printar erros */
if (envLocal.settings.error.value === true) {
/* Define se vai printar inteiro */
const showError = envLocal.settings.fullError?.value || true;
/* Se pode printar o erro inteiro */
if (showError) {
/* Só joga o erro na tela */
console.error(myError);
/* Se não, formata e printa */
} else console.log('\x1b[31m', `[${path.basename(place)} #${envLocal.parameters.code.value || 0}] →`, `\x1b[33m${envLocal.parameters.message.value}`);
}
/* Retorna o erro */
return envLocal.results;
}
/* Define globalmente */
global.logging = {
resetAmbient,
echoError,
postResults,
envInfoGenerator,
getRelativePath,
};
/* Exporta */
module.exports = {
resetAmbient,
echoError,
postResults,
envInfoGenerator,
getRelativePath,
};