@erickzao/api-stats-cli
Version:
CLI oficial para configuração automática do API Stats Logger
1,079 lines (896 loc) • 30.8 kB
JavaScript
#!/usr/bin/env node
const fs = require('fs');
const path = require('path');
const readline = require('readline');
const axios = require('axios');
class ApiStatsCLI {
constructor() {
this.rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
this.baseUrl = process.env.API_STATS_URL || 'http://localhost:3000';
this.authToken = null; // Armazenar token JWT
this.deviceId = null; // Armazenar deviceId para requisições autenticadas
}
async run() {
console.log('🚀 API Stats Logger - Configuração Inicial\n');
try {
// 1. Verificar se usuário já está autenticado
await this.checkAuthentication();
// 2. Coletar configurações do projeto
const config = await this.collectConfig();
// 3. Validar ou criar projeto/API key
if (config.existingKey) {
const isValid = await this.validateApiKey(config.existingKey);
if (!isValid) {
throw new Error('API key fornecida é inválida');
}
config.apiKey = config.existingKey;
console.log('✅ API key validada com sucesso!');
} else {
const result = await this.createProjectAndApiKey(config);
config.projectId = result.projectId;
config.apiKey = result.apiKey;
}
// 4. Configurar projeto localmente
await this.setupProject(config);
console.log('\n🎉 Configuração concluída com sucesso!');
console.log('\n📖 Próximos passos:');
console.log('1. Copie as variáveis do .env.api-stats para seu .env');
console.log('2. Execute o exemplo gerado para testar');
console.log('3. Acesse o dashboard para visualizar os logs');
console.log(`4. Dashboard: ${this.baseUrl.replace('/api', '')}\n`);
} catch (error) {
console.error('\n❌ Erro na configuração:', error.message);
console.log('\n💡 Precisa de ajuda? Verifique:');
console.log('- Se o servidor API Stats está rodando');
console.log('- Se as credenciais estão corretas');
console.log('- Se você tem permissão para criar projetos');
process.exit(1);
}
}
async checkAuthentication() {
console.log('🔐 Verificando autenticação...\n');
// Verificar se já existe token armazenado
const storedData = this.getStoredAuthData();
if (storedData && storedData.token) {
const isValid = await this.validateToken(storedData.token);
if (isValid) {
this.authToken = storedData.token;
this.deviceId = storedData.deviceId;
console.log('✅ Já autenticado!\n');
return;
} else {
this.clearStoredAuthData();
}
}
// Perguntar se quer fazer login ou criar conta
console.log('Você precisa estar autenticado para criar projetos.');
const action = await this.question('Escolha uma opção:\n1. Fazer login\n2. Criar conta\n3. Usar API key existente\nOpção [1]: ', '1');
switch (action) {
case '1':
await this.login();
break;
case '2':
await this.register();
break;
case '3':
// Pular autenticação, usuário usará API key existente
return;
default:
await this.login();
}
}
async login() {
console.log('\n📝 Login:');
const username = await this.question('Username ou email: ');
const password = await this.question('Senha: ', '', true); // true = esconder input
try {
const response = await axios.post(`${this.baseUrl}/auth/login`, {
username,
password
}, {
timeout: 10000,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'API-Stats-CLI/1.0'
}
});
this.authToken = response.data.access_token;
this.deviceId = response.data.deviceId;
this.storeAuthData(this.authToken, this.deviceId);
console.log('✅ Login realizado com sucesso!\n');
} catch (error) {
if (error.response?.status === 401) {
throw new Error('Credenciais inválidas');
}
throw new Error(`Erro no login: ${error.response?.data?.message || error.message}`);
}
}
async register() {
console.log('\n📝 Criar conta:');
const username = await this.question('Username: ');
const email = await this.question('Email (@grupoloyalty.com.br ou @eway.dev): ');
const password = await this.question('Senha: ', '', true);
const confirmPassword = await this.question('Confirmar senha: ', '', true);
try {
await axios.post(`${this.baseUrl}/auth/register`, {
username,
email,
password,
confirmPassword
}, {
timeout: 10000,
headers: { 'Content-Type': 'application/json' }
});
console.log('✅ Conta criada com sucesso!');
console.log('Fazendo login automaticamente...\n');
// Fazer login automaticamente
const loginResponse = await axios.post(`${this.baseUrl}/auth/login`, {
username,
password
}, {
timeout: 10000,
headers: {
'Content-Type': 'application/json',
'User-Agent': 'API-Stats-CLI/1.0'
}
});
this.authToken = loginResponse.data.access_token;
this.deviceId = loginResponse.data.deviceId;
this.storeAuthData(this.authToken, this.deviceId);
console.log('✅ Login realizado com sucesso!\n');
} catch (error) {
throw new Error(`Erro ao criar conta: ${error.response?.data?.message || error.message}`);
}
}
async validateToken(token) {
try {
const response = await axios.get(`${this.baseUrl}/auth/profile`, {
timeout: 5000,
headers: {
'Authorization': `Bearer ${token}`,
'User-Agent': 'API-Stats-CLI/1.0'
}
});
return response.status === 200;
} catch (error) {
return false;
}
}
getStoredAuthData() {
try {
const os = require('os');
const path = require('path');
const fs = require('fs');
const authPath = path.join(os.homedir(), '.api-stats-auth');
if (fs.existsSync(authPath)) {
const data = fs.readFileSync(authPath, 'utf8').trim();
try {
return JSON.parse(data);
} catch {
// Se não for JSON válido, tratar como token simples (compatibilidade)
return { token: data, deviceId: null };
}
}
// Verificar formato antigo
const tokenPath = path.join(os.homedir(), '.api-stats-token');
if (fs.existsSync(tokenPath)) {
const token = fs.readFileSync(tokenPath, 'utf8').trim();
return { token, deviceId: null };
}
} catch (error) {
// Ignorar erros de leitura
}
return null;
}
storeAuthData(token, deviceId) {
try {
const os = require('os');
const path = require('path');
const fs = require('fs');
const authPath = path.join(os.homedir(), '.api-stats-auth');
const authData = JSON.stringify({ token, deviceId });
fs.writeFileSync(authPath, authData);
// Limpar arquivo antigo se existir
const oldTokenPath = path.join(os.homedir(), '.api-stats-token');
if (fs.existsSync(oldTokenPath)) {
fs.unlinkSync(oldTokenPath);
}
} catch (error) {
console.warn('⚠️ Não foi possível salvar dados de autenticação para uso futuro');
}
}
clearStoredAuthData() {
try {
const os = require('os');
const path = require('path');
const fs = require('fs');
const authPath = path.join(os.homedir(), '.api-stats-auth');
if (fs.existsSync(authPath)) {
fs.unlinkSync(authPath);
}
// Limpar arquivo antigo também
const tokenPath = path.join(os.homedir(), '.api-stats-token');
if (fs.existsSync(tokenPath)) {
fs.unlinkSync(tokenPath);
}
} catch (error) {
// Ignorar erros
}
}
// Métodos de compatibilidade (mantidos para não quebrar código existente)
getStoredToken() {
const authData = this.getStoredAuthData();
return authData ? authData.token : null;
}
storeToken(token) {
this.storeAuthData(token, this.deviceId);
}
clearStoredToken() {
this.clearStoredAuthData();
}
async collectConfig() {
console.log('📋 Configuração do Projeto\n');
const config = {};
// Verificar se deve usar API key existente
if (!this.authToken) {
const useExisting = await this.questionBoolean('Usar API key existente? (caso contrário será criada uma nova)', false);
if (useExisting) {
config.existingKey = await this.question('API Key: ');
// Validar API key imediatamente
const isValid = await this.validateApiKey(config.existingKey);
if (!isValid) {
throw new Error('API key inválida');
}
console.log('✅ API key validada!');
return config;
}
}
// Configurações do projeto (para criação automática)
config.service = await this.question('Nome do serviço/projeto: ', 'my-api');
config.url = await this.question('URL base da API (ex: https://api.exemplo.com): ', 'https://api.example.com');
config.description = await this.question('Descrição (opcional): ', `API logs para ${config.service}`);
config.environment = await this.question('Ambiente [development/staging/production]: ', 'development');
// Detecção automática de framework
console.log('\n🔍 Detectando framework...');
const detectedFramework = this.detectFramework();
if (detectedFramework) {
console.log(`✅ Framework detectado: ${detectedFramework}`);
const useDetected = await this.questionBoolean(`Usar ${detectedFramework}?`, true);
config.framework = useDetected ? detectedFramework : await this.selectFramework();
} else {
console.log('❓ Framework não detectado automaticamente');
config.framework = await this.selectFramework();
}
// Configurações avançadas
console.log('\n⚙️ Configurações Avançadas:');
config.autoInstrument = await this.questionBoolean('Habilitar auto-instrumentação?', true);
config.captureBody = await this.questionBoolean('Capturar body das requisições?', false);
config.captureHeaders = await this.questionBoolean('Capturar headers das requisições?', true);
console.log('\n✅ Configuração coletada!');
return config;
}
async validateApiKey(apiKey) {
try {
const response = await axios.post(`${this.baseUrl}/logs`, [], {
headers: {
'x-api-key': apiKey,
'Content-Type': 'application/json'
},
timeout: 5000
});
return response.status === 200 || response.status === 201;
} catch (error) {
if (error.response?.status === 401) {
return false; // API key inválida
}
// Outros erros podem ser de conectividade, assumir válida
console.log('⚠️ Não foi possível validar completamente (servidor offline?)');
return true;
}
}
async createProjectAndApiKey(config) {
try {
console.log('📦 Criando projeto...');
// Headers com autenticação
const headers = {
'Content-Type': 'application/json',
'User-Agent': 'API-Stats-CLI/1.0'
};
if (this.authToken) {
headers['Authorization'] = `Bearer ${this.authToken}`;
if (this.deviceId) {
headers['x-device-id'] = this.deviceId;
}
}
// 1. Criar projeto (agora requer autenticação)
const projectResponse = await axios.post(`${this.baseUrl}/projects`, {
name: config.service,
url: config.url,
description: config.description || `Projeto para ${config.service}`
}, {
timeout: 10000,
headers
});
const projectId = projectResponse.data._id || projectResponse.data.id;
console.log(`✅ Projeto criado: ${projectId}`);
// 2. Gerar API key
console.log('🔑 Gerando API key...');
const apiKeyResponse = await axios.post(`${this.baseUrl}/api-keys/generate`, {
projectId,
description: `API key para ${config.service} - Gerada pelo CLI`
}, {
timeout: 10000,
headers
});
const apiKey = apiKeyResponse.data.key;
console.log(`✅ API key gerada: ${apiKey.substring(0, 8)}...`);
return { projectId, apiKey };
} catch (error) {
console.error('❌ Erro ao criar projeto/API key:', error.response?.data || error.message);
if (error.response?.status === 401) {
throw new Error('Não autorizado. Faça login novamente.');
}
if (error.response?.status === 403) {
throw new Error('Acesso negado. Verifique suas permissões.');
}
if (error.response?.status === 404) {
throw new Error(`
Servidor API Stats não encontrado em ${this.baseUrl}
💡 Soluções:
1. Verifique se o servidor está rodando
2. Configure a URL correta: export API_STATS_URL=http://seu-servidor:porta
`.trim());
}
throw new Error('Falha ao criar projeto automaticamente.');
}
}
async setupProject(config) {
console.log('\n⚙️ Configurando projeto...');
// 1. Criar arquivo .env
await this.createEnvFile(config);
// 2. Criar exemplo de integração baseado no framework
await this.createIntegrationExample(config);
// 3. Criar arquivo de configuração
await this.createConfigFile(config);
// 4. Atualizar package.json se existir
await this.updatePackageJson();
// 5. Criar README com instruções
await this.createSetupInstructions(config);
}
async createEnvFile(config) {
const envContent = `# API Stats Logger Configuration
API_STATS_ENABLED=true
API_STATS_API_KEY=${config.apiKey}
API_STATS_URL=${this.baseUrl}/logs
API_STATS_SERVICE=${config.service}
API_STATS_ENVIRONMENT=${config.environment}
API_STATS_BATCH_SIZE=10
API_STATS_FLUSH_INTERVAL=2000
# Optional: Capture settings
API_STATS_CAPTURE_BODY=${config.captureBody}
API_STATS_CAPTURE_HEADERS=${config.captureHeaders}
# Project Info (for reference)
API_STATS_PROJECT_ID=${config.projectId || 'auto-generated'}
`;
const envPath = path.join(process.cwd(), '.env.api-stats');
fs.writeFileSync(envPath, envContent);
console.log('📝 Criado: .env.api-stats');
console.log(' Copie as variáveis para seu .env principal');
}
async createIntegrationExample(config) {
let example = '';
switch (config.framework) {
case 'express':
example = this.getExpressExample(config);
break;
case 'nestjs':
example = this.getNestJSExample(config);
break;
case 'fastify':
example = this.getFastifyExample(config);
break;
case 'koa':
example = this.getKoaExample(config);
break;
default:
example = this.getGenericExample(config);
}
const examplePath = path.join(process.cwd(), `api-stats-${config.framework}-example.js`);
fs.writeFileSync(examplePath, example);
console.log(`📝 Criado: api-stats-${config.framework}-example.js`);
}
getExpressExample(config) {
return `// Express + API Stats Logger Integration
const express = require('express');
const ApiStatsLogger = require('api-stats-logger');
const app = express();
${config.autoInstrument ? `
// Opção 1: Auto-instrumentação (mais fácil)
const logger = ApiStatsLogger.init({
autoDetect: true,
captureHTTP: true,
captureBody: ${config.captureBody},
captureHeaders: ${config.captureHeaders}
});
` : `
// Opção 2: Configuração manual
const logger = new ApiStatsLogger();
app.use(ApiStatsLogger.expressMiddleware({
logger,
captureBody: ${config.captureBody},
captureHeaders: ${config.captureHeaders},
skipPaths: ['/health', '/metrics']
}));
`}
// Middleware padrão
app.use(express.json());
// Exemplo de rota com logging manual
app.get('/users/:id', async (req, res) => {
const { id } = req.params;
logger.info('Buscando usuário', { userId: id });
try {
// Sua lógica aqui
const user = { id, name: 'João' };
logger.info('Usuário encontrado', { userId: id, userName: user.name });
res.json(user);
} catch (error) {
logger.error('Erro ao buscar usuário', { userId: id, error: error.message });
res.status(500).json({ error: 'Internal server error' });
}
});
// Health check
app.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info('Servidor iniciado', { port: PORT, framework: 'express' });
console.log(\`🚀 Server running on port \${PORT}\`);
});
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Shutting down...');
await logger.close();
process.exit(0);
});
`;
}
getNestJSExample(config) {
return `// NestJS + API Stats Logger Integration
// Add this to your main.ts file
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ApiStatsLogger } from 'api-stats-logger';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// Configurar API Stats Logger
const logger = new ApiStatsLogger({
service: '${config.service}',
environment: '${config.environment}'
});
// Adicionar middleware
app.use(ApiStatsLogger.nestMiddleware({
logger,
captureBody: ${config.captureBody},
captureHeaders: ${config.captureHeaders},
skipRoutes: ['/health', '/metrics']
}));
await app.listen(3000);
logger.info('NestJS application started', {
port: 3000,
framework: 'nestjs'
});
}
bootstrap();
// Exemplo de uso em um controller:
/*
import { Controller, Get, Logger } from '@nestjs/common';
import { ApiStatsLogger } from 'api-stats-logger';
@Controller('users')
export class UsersController {
private readonly logger = new ApiStatsLogger({
service: 'users-service'
});
@Get()
findAll() {
this.logger.info('Listing all users');
try {
// Sua lógica aqui
const users = [];
this.logger.info('Users retrieved successfully', { count: users.length });
return users;
} catch (error) {
this.logger.error('Error retrieving users', { error: error.message });
throw error;
}
}
}
*/
`;
}
getFastifyExample(config) {
return `// Fastify + API Stats Logger Integration
const fastify = require('fastify')({ logger: false });
const ApiStatsLogger = require('api-stats-logger');
// Configurar API Stats Logger
const logger = new ApiStatsLogger({
service: '${config.service}',
environment: '${config.environment}'
});
// Hooks para logging automático
fastify.addHook('onRequest', async (request, reply) => {
request.startTime = Date.now();
request.requestId = \`req_\${Date.now()}_\${Math.random().toString(36).substr(2, 9)}\`;
logger.info('Request started', {
requestId: request.requestId,
method: request.method,
url: request.url,
ip: request.ip
});
});
fastify.addHook('onResponse', async (request, reply) => {
const duration = Date.now() - request.startTime;
const level = reply.statusCode >= 500 ? 'error' :
reply.statusCode >= 400 ? 'warn' : 'info';
logger.log({
level,
message: \`\${request.method} \${request.url} \${reply.statusCode}\`,
metadata: {
requestId: request.requestId,
statusCode: reply.statusCode,
duration,
ip: request.ip
}
});
});
// Rotas
fastify.get('/users/:id', async (request, reply) => {
const { id } = request.params;
logger.info('Getting user', { userId: id });
try {
// Sua lógica aqui
const user = { id, name: 'João' };
logger.info('User found', { userId: id });
return user;
} catch (error) {
logger.error('Error getting user', { userId: id, error: error.message });
reply.status(500);
return { error: 'Internal server error' };
}
});
fastify.get('/health', async (request, reply) => {
return { status: 'ok', timestamp: new Date().toISOString() };
});
// Start server
const start = async () => {
try {
await fastify.listen({ port: 3000 });
logger.info('Server started', { port: 3000, framework: 'fastify' });
console.log('🚀 Server running on port 3000');
} catch (err) {
logger.error('Error starting server', { error: err.message });
process.exit(1);
}
};
start();
// Graceful shutdown
process.on('SIGINT', async () => {
await logger.close();
await fastify.close();
process.exit(0);
});
`;
}
getKoaExample(config) {
return `// Koa + API Stats Logger Integration
const Koa = require('koa');
const Router = require('@koa/router');
const bodyParser = require('koa-bodyparser');
const ApiStatsLogger = require('api-stats-logger');
const app = new Koa();
const router = new Router();
// Configurar logger
const logger = new ApiStatsLogger({
service: '${config.service}',
environment: '${config.environment}'
});
// Middleware de logging
app.use(async (ctx, next) => {
const startTime = Date.now();
const requestId = \`koa_\${Date.now()}_\${Math.random().toString(36).substr(2, 9)}\`;
ctx.requestId = requestId;
logger.info('Request started', {
requestId,
method: ctx.method,
url: ctx.url,
ip: ctx.ip
});
try {
await next();
} catch (error) {
logger.error('Request error', {
requestId,
error: error.message,
stack: error.stack
});
throw error;
}
const duration = Date.now() - startTime;
const level = ctx.status >= 500 ? 'error' :
ctx.status >= 400 ? 'warn' : 'info';
logger.log({
level,
message: \`\${ctx.method} \${ctx.url} \${ctx.status}\`,
metadata: {
requestId,
statusCode: ctx.status,
duration,
ip: ctx.ip
}
});
});
app.use(bodyParser());
// Rotas
router.get('/users/:id', async (ctx) => {
const { id } = ctx.params;
logger.info('Getting user', { userId: id });
try {
// Sua lógica aqui
const user = { id, name: 'João' };
logger.info('User found', { userId: id });
ctx.body = user;
} catch (error) {
logger.error('Error getting user', { userId: id, error: error.message });
ctx.status = 500;
ctx.body = { error: 'Internal server error' };
}
});
router.get('/health', async (ctx) => {
ctx.body = { status: 'ok', timestamp: new Date().toISOString() };
});
app.use(router.routes());
app.use(router.allowedMethods());
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
logger.info('Server started', { port: PORT, framework: 'koa' });
console.log(\`🚀 Server running on port \${PORT}\`);
});
// Graceful shutdown
process.on('SIGINT', async () => {
await logger.close();
process.exit(0);
});
`;
}
getGenericExample(config) {
return `// Generic API Stats Logger Integration
const ApiStatsLogger = require('api-stats-logger');
// Configuração básica
const logger = new ApiStatsLogger({
service: '${config.service}',
environment: '${config.environment}'
});
// Exemplos de uso
logger.info('Application started', {
version: '1.0.0',
framework: '${config.framework}'
});
// Exemplo de logging de operação
async function processUser(userId) {
const startTime = Date.now();
logger.info('Processing user', { userId });
try {
// Sua lógica aqui
await new Promise(resolve => setTimeout(resolve, 100));
const duration = Date.now() - startTime;
logger.info('User processed successfully', { userId, duration });
return { id: userId, processed: true };
} catch (error) {
const duration = Date.now() - startTime;
logger.error('Error processing user', {
userId,
error: error.message,
duration
});
throw error;
}
}
// Teste
processUser('123')
.then(result => console.log('Result:', result))
.catch(error => console.error('Error:', error));
// Graceful shutdown
process.on('SIGINT', async () => {
console.log('Shutting down...');
await logger.close();
process.exit(0);
});
`;
}
async createConfigFile(config) {
const configContent = `// API Stats Logger Configuration
module.exports = {
// Basic settings
service: '${config.service}',
environment: '${config.environment}',
framework: '${config.framework}',
// Features
autoInstrument: ${config.autoInstrument},
captureBody: ${config.captureBody},
captureHeaders: ${config.captureHeaders},
// Performance settings
batchSize: 10,
flushInterval: 2000,
maxRetries: 3,
// Skip patterns
skipPaths: ['/health', '/metrics', '/favicon.ico'],
skipMethods: ['OPTIONS'],
// Security
maxBodySize: 1024 * 10, // 10KB
sensitiveHeaders: ['authorization', 'cookie', 'x-api-key']
};
`;
const configPath = path.join(process.cwd(), 'api-stats.config.js');
fs.writeFileSync(configPath, configContent);
console.log('📝 Criado: api-stats.config.js');
}
async updatePackageJson() {
const packagePath = path.join(process.cwd(), 'package.json');
if (fs.existsSync(packagePath)) {
const packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8'));
if (!packageJson.dependencies) packageJson.dependencies = {};
if (!packageJson.dependencies['api-stats-logger']) {
packageJson.dependencies['api-stats-logger'] = '^1.0.0';
}
if (!packageJson.scripts) packageJson.scripts = {};
if (!packageJson.scripts['logs:stats']) {
packageJson.scripts['logs:stats'] = 'node -e "const logger = require(\'api-stats-logger\'); logger.getStats && console.log(\'Stats:\', logger.getStats());"';
}
fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2));
console.log('📝 Atualizado: package.json');
}
}
async createSetupInstructions(config) {
const instructions = `# API Stats Logger - Setup Completo
## ✅ Configuração realizada:
- **Framework**: ${config.framework}
- **Serviço**: ${config.service}
- **Ambiente**: ${config.environment}
- **Auto-instrumentação**: ${config.autoInstrument ? 'Ativada' : 'Desativada'}
- **Captura de body**: ${config.captureBody ? 'Ativada' : 'Desativada'}
- **Captura de headers**: ${config.captureHeaders ? 'Ativada' : 'Desativada'}
## 📋 Próximos passos:
### 1. Instalar dependências
\`\`\`bash
npm install api-stats-logger
# ou
yarn add api-stats-logger
\`\`\`
### 2. Configurar variáveis de ambiente
Copie as variáveis do arquivo \`.env.api-stats\` para seu \`.env\` principal:
\`\`\`env
API_STATS_API_KEY=${config.apiKey}
API_STATS_SERVICE=${config.service}
API_STATS_ENVIRONMENT=${config.environment}
\`\`\`
### 3. Integrar no seu código
Veja o arquivo de exemplo: \`api-stats-${config.framework}-example.js\`
### 4. Verificar logs
- Execute sua aplicação
- Faça algumas requisições
- Verifique os logs no dashboard: http://localhost:3000
## 🔧 Configurações avançadas
Edite o arquivo \`api-stats.config.js\` para personalizar:
- Paths para ignorar
- Tamanho máximo do body
- Intervals de flush
- Headers sensíveis
## 📊 Monitoramento
Verifique estatísticas do logger:
\`\`\`bash
npm run logs:stats
\`\`\`
## 🆘 Suporte
- Documentação: [Link para docs]
- GitHub Issues: [Link para issues]
- Discord: [Link para Discord]
---
Gerado pelo API Stats CLI em ${new Date().toISOString()}
`;
const readmePath = path.join(process.cwd(), 'API-STATS-SETUP.md');
fs.writeFileSync(readmePath, instructions);
console.log('📝 Criado: API-STATS-SETUP.md');
}
question(query, defaultValue = '', hideInput = false) {
return new Promise((resolve) => {
if (hideInput) {
// Para senhas, usar método que esconde input
process.stdout.write(query);
process.stdin.setEncoding('utf8');
process.stdin.setRawMode(true);
let password = '';
const onData = (char) => {
const byte = char.charCodeAt(0);
if (byte === 13) { // Enter
console.log();
process.stdin.setRawMode(false);
process.stdin.removeListener('data', onData);
resolve(password || defaultValue);
} else if (byte === 127) { // Backspace
if (password.length > 0) {
password = password.slice(0, -1);
process.stdout.write('\b \b');
}
} else if (byte >= 32 && byte <= 126) { // Caracteres imprimíveis
password += char;
process.stdout.write('*');
}
};
process.stdin.on('data', onData);
} else {
// Input normal
this.rl.question(query, (answer) => {
resolve(answer.trim() || defaultValue);
});
}
});
}
questionBoolean(query, defaultValue = false) {
return new Promise((resolve) => {
this.rl.question(query, (answer) => {
const input = answer.trim().toLowerCase();
if (input === 'y' || input === 'yes' || input === 'sim' || input === 's') {
resolve(true);
} else if (input === 'n' || input === 'no' || input === 'não') {
resolve(false);
} else {
resolve(defaultValue);
}
});
});
}
detectFramework() {
const fs = require('fs');
const path = require('path');
try {
const packageJsonPath = path.join(process.cwd(), 'package.json');
if (fs.existsSync(packageJsonPath)) {
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
if (deps['@nestjs/core'] || deps['@nestjs/common']) return 'nestjs';
if (deps['express']) return 'express';
if (deps['fastify']) return 'fastify';
if (deps['koa']) return 'koa';
}
} catch (error) {
// Ignorar erros de leitura
}
return null;
}
async selectFramework() {
console.log('\nFrameworks disponíveis:');
console.log('1. Express');
console.log('2. NestJS');
console.log('3. Fastify');
console.log('4. Koa');
console.log('5. Outro/Generic');
const choice = await this.question('Escolha o framework [1-5]: ', '1');
switch (choice) {
case '1': return 'express';
case '2': return 'nestjs';
case '3': return 'fastify';
case '4': return 'koa';
case '5': return 'generic';
default: return 'express';
}
}
}
// Se executado diretamente
if (require.main === module) {
const cli = new ApiStatsCLI();
cli.run().catch(console.error);
}
module.exports = ApiStatsCLI;