api-stats-logger
Version:
SDK completo de logging e monitoramento de APIs com nova estrutura de logs organizada, auto-instrumentação, dashboard em tempo real e CLI para configuração automática. Suporta logs estruturados por contexto (HTTP, business, security, system) com campos op
607 lines (506 loc) • 18.5 kB
JavaScript
const { Command } = require('commander');
const UIComponents = require('./ui-components');
const axios = require('axios');
const open = require('open');
const path = require('path');
const fs = require('fs');
class CommandManager {
constructor() {
this.ui = new UIComponents();
this.program = new Command();
this.baseUrl = process.env.API_STATS_URL || 'https://apistats.eway-api.com.br';
// Inicializar armazenamento seguro
const SecureStorage = require('./secure-storage');
this.secureStorage = new SecureStorage();
// Migrar credenciais antigas
this.secureStorage.migrateOldCredentials();
this.authData = {};
this.setupCommands();
}
setupCommands() {
this.program
.name('api-stats-logger')
.description('CLI para gerenciamento de projetos API Stats')
.version('2.1.3');
// Comando principal de inicialização
this.program
.command('init')
.description('Inicializar configuração do projeto')
.action(() => this.initProject());
// Comandos após autenticação
if (this.authData.token) {
this.program
.command('projects')
.alias('p')
.description('Listar todos os projetos')
.action(() => this.listProjects());
this.program
.command('select')
.alias('s')
.description('Selecionar projeto ativo')
.action(() => this.selectProject());
this.program
.command('status')
.description('Mostrar status do usuário e projeto atual')
.action(() => this.showStatus());
this.program
.command('create')
.alias('c')
.description('Criar novo projeto')
.action(() => this.createProject());
this.program
.command('delete')
.alias('d')
.description('Deletar projeto')
.argument('[project-id]', 'ID do projeto para deletar')
.action((projectId) => this.deleteProject(projectId));
this.program
.command('apikey')
.alias('k')
.description('Gerar nova API key')
.argument('[project-id]', 'ID do projeto')
.action((projectId) => this.generateApiKey(projectId));
this.program
.command('open')
.alias('o')
.description('Abrir projeto no navegador')
.argument('[project-id]', 'ID do projeto')
.action((projectId) => this.openProject(projectId));
this.program
.command('settings')
.description('Abrir configurações do projeto')
.argument('[project-id]', 'ID do projeto')
.action((projectId) => this.openSettings(projectId));
this.program
.command('logout')
.description('Fazer logout da CLI')
.action(() => this.logout());
this.program
.command('user')
.alias('u')
.description('Mostrar informações do usuário')
.action(() => this.showUser());
this.program
.command('logs')
.alias('l')
.description('Visualizar logs do projeto')
.argument('[project-id]', 'ID do projeto')
.option('-f, --follow', 'Seguir logs em tempo real')
.option('-n, --lines <number>', 'Número de linhas', '50')
.action((projectId, options) => this.showLogs(projectId, options));
this.program
.command('stats')
.description('Mostrar estatísticas do projeto')
.argument('[project-id]', 'ID do projeto')
.option('-d, --days <number>', 'Número de dias', '7')
.action((projectId, options) => this.showStats(projectId, options));
} else {
// Comandos para usuário não autenticado
this.program
.command('login')
.description('Fazer login na CLI')
.action(() => this.login());
this.program
.command('register')
.description('Criar nova conta')
.action(() => this.register());
}
}
async run() {
if (process.argv.length === 2) {
// Sem argumentos, mostrar interface principal
if (this.authData.token) {
await this.showMainMenu();
} else {
await this.initProject();
}
} else {
// Com argumentos, executar comando específico
await this.program.parseAsync(process.argv);
}
}
async showMainMenu() {
this.ui.header();
const user = await this.getCurrentUser();
if (user) {
this.ui.userStatus(user);
}
const choices = [
{ name: `${this.ui.icons.project} Gerenciar Projetos`, value: 'projects' },
{ name: `${this.ui.icons.key} Gerar API Key`, value: 'apikey' },
{ name: `${this.ui.icons.settings} Configurações`, value: 'settings' },
{ name: `${this.ui.icons.link} Abrir Dashboard`, value: 'open' },
{ name: `${this.ui.icons.user} Perfil do Usuário`, value: 'user' },
{ name: `${this.ui.icons.help} Ajuda`, value: 'help' },
{ name: `${this.ui.icons.logout} Logout`, value: 'logout' }
];
const { selected } = await this.ui.select('O que você deseja fazer?', choices);
switch (selected) {
case 'projects':
await this.manageProjects();
break;
case 'apikey':
await this.generateApiKey();
break;
case 'settings':
await this.openSettings();
break;
case 'open':
await this.openProject();
break;
case 'user':
await this.showUser();
break;
case 'help':
await this.showHelp();
break;
case 'logout':
await this.logout();
break;
}
}
async manageProjects() {
this.ui.section('Gerenciamento de Projetos');
const choices = [
{ name: `${this.ui.icons.project} Listar Projetos`, value: 'list' },
{ name: `${this.ui.icons.arrow} Selecionar Projeto`, value: 'select' },
{ name: `${this.ui.icons.project} Criar Projeto`, value: 'create' },
{ name: `${this.ui.icons.delete} Deletar Projeto`, value: 'delete' },
{ name: `${this.ui.icons.arrow} Voltar`, value: 'back' }
];
const { selected } = await this.ui.select('Escolha uma opção:', choices);
switch (selected) {
case 'list':
await this.listProjects();
break;
case 'select':
await this.selectProject();
break;
case 'create':
await this.createProject();
break;
case 'delete':
await this.deleteProject();
break;
case 'back':
await this.showMainMenu();
break;
}
}
async listProjects() {
const projects = await this.ui.withProgress('Carregando projetos', async () => {
const response = await axios.get(`${this.baseUrl}/projects`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return response.data;
});
if (projects.length === 0) {
this.ui.info('Você não possui projetos. Use "create" para criar um novo.');
return;
}
this.ui.section('Seus Projetos');
const rows = projects.map(project => [
project.name,
project.description || 'Sem descrição',
project.environment || 'production',
project.createdAt ? new Date(project.createdAt).toLocaleDateString() : 'N/A'
]);
this.ui.table(['Nome', 'Descrição', 'Ambiente', 'Criado em'], rows);
}
async selectProject() {
const projects = await this.ui.withProgress('Carregando projetos', async () => {
const response = await axios.get(`${this.baseUrl}/projects`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return response.data;
});
if (projects.length === 0) {
this.ui.error('Você não possui projetos. Use "create" para criar um novo.');
return;
}
const choices = projects.map(project => ({
name: `${this.ui.icons.project} ${project.name} (${project.environment})`,
value: project,
short: project.name
}));
const { selected } = await this.ui.select('Selecione um projeto:', choices);
// Salvar projeto selecionado
this.saveSelectedProject(selected);
this.ui.success(`Projeto "${selected.name}" selecionado com sucesso!`);
}
async createProject() {
this.ui.section('Criar Novo Projeto');
const { value: name } = await this.ui.input('Nome do projeto:', {
validate: (input) => input.length > 0 || 'Nome é obrigatório'
});
const { value: description } = await this.ui.input('Descrição (opcional):');
const environments = [
{ name: 'Production', value: 'production' },
{ name: 'Development', value: 'development' },
{ name: 'Testing', value: 'testing' },
{ name: 'Staging', value: 'staging' }
];
const { selected: environment } = await this.ui.select('Ambiente:', environments);
const project = await this.ui.withProgress('Criando projeto', async () => {
const response = await axios.post(`${this.baseUrl}/projects`, {
name,
description,
environment
}, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return response.data;
});
this.ui.success(`Projeto "${project.name}" criado com sucesso!`);
this.ui.info(`ID do projeto: ${project._id}`);
}
async deleteProject(projectId) {
let project;
if (!projectId) {
const projects = await this.ui.withProgress('Carregando projetos', async () => {
const response = await axios.get(`${this.baseUrl}/projects`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return response.data;
});
if (projects.length === 0) {
this.ui.error('Você não possui projetos para deletar.');
return;
}
const choices = projects.map(p => ({
name: `${this.ui.icons.project} ${p.name} (${p.environment})`,
value: p,
short: p.name
}));
const { selected } = await this.ui.select('Selecione o projeto para deletar:', choices);
project = selected;
} else {
project = { _id: projectId };
}
const { confirmed } = await this.ui.confirm(`Tem certeza que deseja deletar o projeto "${project.name || project._id}"?`);
if (!confirmed) {
this.ui.info('Operação cancelada.');
return;
}
await this.ui.withProgress('Deletando projeto', async () => {
await axios.delete(`${this.baseUrl}/projects/${project._id}`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
});
this.ui.success('Projeto deletado com sucesso!');
}
async generateApiKey(projectId) {
let project;
if (!projectId) {
const projects = await this.ui.withProgress('Carregando projetos', async () => {
const response = await axios.get(`${this.baseUrl}/projects`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return response.data;
});
if (projects.length === 0) {
this.ui.error('Você não possui projetos. Crie um projeto primeiro.');
return;
}
const choices = projects.map(p => ({
name: `${this.ui.icons.project} ${p.name} (${p.environment})`,
value: p,
short: p.name
}));
const { selected } = await this.ui.select('Selecione o projeto:', choices);
project = selected;
} else {
project = { _id: projectId };
}
const url = `https://apistats.eway.app.br/applications/${project._id}/configuracoes`;
this.ui.info(`Abrindo página de configurações para gerar nova API key...`);
this.ui.info(`URL: ${url}`);
await open(url);
}
async openProject(projectId) {
let project;
if (!projectId) {
const projects = await this.ui.withProgress('Carregando projetos', async () => {
const response = await axios.get(`${this.baseUrl}/projects`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return response.data;
});
if (projects.length === 0) {
this.ui.error('Você não possui projetos.');
return;
}
const choices = projects.map(p => ({
name: `${this.ui.icons.project} ${p.name} (${p.environment})`,
value: p,
short: p.name
}));
const { selected } = await this.ui.select('Selecione o projeto:', choices);
project = selected;
} else {
project = { _id: projectId };
}
const url = `https://apistats.eway.app.br/applications/${project._id}`;
this.ui.info(`Abrindo projeto no navegador...`);
await open(url);
}
async openSettings(projectId) {
let project;
if (!projectId) {
const projects = await this.ui.withProgress('Carregando projetos', async () => {
const response = await axios.get(`${this.baseUrl}/projects`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return response.data;
});
if (projects.length === 0) {
this.ui.error('Você não possui projetos.');
return;
}
const choices = projects.map(p => ({
name: `${this.ui.icons.project} ${p.name} (${p.environment})`,
value: p,
short: p.name
}));
const { selected } = await this.ui.select('Selecione o projeto:', choices);
project = selected;
} else {
project = { _id: projectId };
}
const url = `https://apistats.eway.app.br/applications/${project._id}/configuracoes`;
this.ui.info(`Abrindo configurações do projeto...`);
await open(url);
}
async logout() {
const { confirmed } = await this.ui.confirm('Tem certeza que deseja fazer logout?');
if (!confirmed) {
this.ui.info('Operação cancelada.');
return;
}
await this.clearAuthData();
this.ui.success('Logout realizado com sucesso!');
this.ui.info('Use "login" para fazer login novamente.');
}
async showUser() {
const user = await this.ui.withProgress('Carregando informações do usuário', async () => {
const response = await axios.get(`${this.baseUrl}/auth/profile`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return response.data;
});
this.ui.section('Informações do Usuário');
this.ui.userStatus({
name: user.firstName ? `${user.firstName} ${user.lastName}` : user.username,
email: user.email,
projects: user.projects || 0
});
}
async showHelp() {
const commands = [
{ command: 'projects, p', description: 'Listar todos os projetos' },
{ command: 'select, s', description: 'Selecionar projeto ativo' },
{ command: 'create, c', description: 'Criar novo projeto' },
{ command: 'delete, d [id]', description: 'Deletar projeto' },
{ command: 'apikey, k [id]', description: 'Gerar nova API key' },
{ command: 'open, o [id]', description: 'Abrir projeto no navegador' },
{ command: 'settings [id]', description: 'Abrir configurações' },
{ command: 'user, u', description: 'Informações do usuário' },
{ command: 'logout', description: 'Fazer logout da CLI' },
{ command: 'help', description: 'Mostrar esta ajuda' }
];
this.ui.commandList(commands);
}
async getCurrentUser() {
try {
const response = await axios.get(`${this.baseUrl}/auth/profile`, {
headers: { Authorization: `Bearer ${this.authData.token}` }
});
return {
name: response.data.firstName ? `${response.data.firstName} ${response.data.lastName}` : response.data.username,
email: response.data.email,
projects: response.data.projects || 0
};
} catch (error) {
return null;
}
}
async loadAuthData() {
try {
const credentials = await this.secureStorage.getCredentials();
if (credentials) {
this.authData = {
token: credentials.token,
deviceId: credentials.deviceId,
timestamp: Date.now()
};
return this.authData;
}
} catch (error) {
console.warn('⚠️ Erro ao carregar credenciais:', error.message);
}
return {};
}
async saveAuthData(token, deviceId) {
try {
const data = { token, deviceId, timestamp: Date.now() };
this.authData = data;
const success = await this.secureStorage.setCredentials(token, deviceId);
if (!success) {
console.warn('⚠️ Não foi possível salvar credenciais de forma segura');
}
} catch (error) {
console.warn('⚠️ Erro ao salvar credenciais:', error.message);
}
}
async clearAuthData() {
try {
await this.secureStorage.clearCredentials();
this.authData = {};
} catch (error) {
console.warn('⚠️ Erro ao limpar credenciais:', error.message);
}
}
saveSelectedProject(project) {
const projectFile = path.join(process.cwd(), '.api-stats-project');
fs.writeFileSync(projectFile, JSON.stringify(project, null, 2));
}
// Métodos para implementar outros comandos
async initProject() {
this.ui.info('Funcionalidade de inicialização será implementada diretamente na CLI moderna.');
this.ui.info('Use os comandos disponíveis: projects, create, select, etc.');
}
async login() {
this.ui.info('Use o comando principal da CLI para fazer login.');
this.ui.info('Execute: npx api-stats-logger');
}
async register() {
this.ui.info('Use o comando principal da CLI para registrar uma conta.');
this.ui.info('Execute: npx api-stats-logger');
}
async showStatus() {
const user = await this.getCurrentUser();
if (user) {
this.ui.userStatus(user);
}
const projectFile = path.join(process.cwd(), '.api-stats-project');
if (fs.existsSync(projectFile)) {
try {
const project = JSON.parse(fs.readFileSync(projectFile, 'utf8'));
this.ui.section('Projeto Atual');
this.ui.info(`Nome: ${project.name}`);
this.ui.info(`Ambiente: ${project.environment}`);
this.ui.info(`ID: ${project._id}`);
} catch (error) {
this.ui.warning('Erro ao carregar informações do projeto atual.');
}
}
}
async showLogs(projectId, options) {
this.ui.info('Funcionalidade de logs em desenvolvimento...');
}
async showStats(projectId, options) {
this.ui.info('Funcionalidade de estatísticas em desenvolvimento...');
}
}
module.exports = CommandManager;