UNPKG

@erickzao/api-stats-cli

Version:

CLI oficial para configuração automática do API Stats Logger

1,079 lines (896 loc) 30.8 kB
#!/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;