UNPKG

backend-mcp

Version:

Generador automático de backends con Node.js, Express, Prisma y módulos configurables. Servidor MCP compatible con npx para agentes IA. Soporta PostgreSQL, MySQL, MongoDB y SQLite.

375 lines (305 loc) 11.7 kB
const fs = require('fs'); const path = require('path'); const Handlebars = require('handlebars'); class LoggingModuleInitializer { constructor(projectPath, config = {}) { this.projectPath = projectPath; this.config = { logLevel: config.logLevel || process.env.LOG_LEVEL || 'info', logFormat: config.logFormat || process.env.LOG_FORMAT || 'json', enableConsoleLog: config.enableConsoleLog !== false, enableFileLog: config.enableFileLog !== false, enableDatabaseLog: config.enableDatabaseLog !== false, logFilePath: config.logFilePath || process.env.LOG_FILE_PATH || './logs', logMaxSize: config.logMaxSize || process.env.LOG_MAX_SIZE || '20m', logMaxFiles: config.logMaxFiles || process.env.LOG_MAX_FILES || '14d', logDatePattern: config.logDatePattern || process.env.LOG_DATE_PATTERN || 'YYYY-MM-DD', sentryDsn: config.sentryDsn || process.env.SENTRY_DSN, elasticsearchUrl: config.elasticsearchUrl || process.env.ELASTICSEARCH_URL, ...config }; this.modulePath = path.join(__dirname); } async initialize() { console.log('🔧 Inicializando módulo de logging...'); try { // 1. Configurar estructura de directorios await this.setupDirectories(); // 2. Generar configuración de Winston await this.generateWinstonConfig(); // 3. Generar servicio de logging await this.generateLoggerService(); // 4. Generar middleware de auditoría await this.generateAuditMiddleware(); // 5. Generar interceptor de logging await this.generateLoggingInterceptor(); // 6. Generar servicio de auditoría await this.generateAuditService(); // 7. Generar controlador await this.generateController(); // 8. Generar módulo await this.generateModule(); // 9. Configurar base de datos await this.setupDatabase(); // 10. Actualizar package.json await this.updatePackageJson(); console.log('✅ Módulo de logging inicializado correctamente'); return { success: true, message: 'Logging module initialized successfully', files: this.getGeneratedFiles(), config: this.config }; } catch (error) { console.error('❌ Error inicializando módulo de logging:', error); throw error; } } async setupDirectories() { const dirs = [ 'src/logging', 'src/logging/interfaces', 'src/logging/dto', 'logs' ]; for (const dir of dirs) { const fullPath = path.join(this.projectPath, dir); if (!fs.existsSync(fullPath)) { fs.mkdirSync(fullPath, { recursive: true }); } } } async generateWinstonConfig() { const templatePath = path.join(this.modulePath, 'templates', 'winston.config.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/logging/winston.config.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config, enableConsoleLog: this.config.enableConsoleLog, enableFileLog: this.config.enableFileLog, enableDatabaseLog: this.config.enableDatabaseLog, hasSentry: !!this.config.sentryDsn, hasElasticsearch: !!this.config.elasticsearchUrl }); fs.writeFileSync(outputPath, content); } async generateLoggerService() { const templatePath = path.join(this.modulePath, 'templates', 'logger.service.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/logging/logger.service.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config, enableDatabaseLog: this.config.enableDatabaseLog, hasSentry: !!this.config.sentryDsn }); fs.writeFileSync(outputPath, content); } async generateAuditMiddleware() { const templatePath = path.join(this.modulePath, 'templates', 'audit.middleware.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/logging/audit.middleware.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config }); fs.writeFileSync(outputPath, content); } async generateLoggingInterceptor() { const templatePath = path.join(this.modulePath, 'templates', 'logging.interceptor.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/logging/logging.interceptor.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config }); fs.writeFileSync(outputPath, content); } async generateAuditService() { const templatePath = path.join(this.modulePath, 'templates', 'audit.service.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/logging/audit.service.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config }); fs.writeFileSync(outputPath, content); } async generateController() { const templatePath = path.join(this.modulePath, 'templates', 'logging.controller.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/logging/logging.controller.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config }); fs.writeFileSync(outputPath, content); } async generateModule() { const templatePath = path.join(this.modulePath, 'templates', 'logging.module.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/logging/logging.module.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config, enableDatabaseLog: this.config.enableDatabaseLog }); fs.writeFileSync(outputPath, content); } async setupDatabase() { const schemaPath = path.join(this.projectPath, 'prisma/schema.prisma'); if (!fs.existsSync(schemaPath)) { console.log('⚠️ Schema de Prisma no encontrado, saltando configuración de base de datos'); return; } const auditLogModel = ` model AuditLog { id String @id @default(cuid()) userId String? action String resource String resourceId String? oldValues Json? newValues Json? ipAddress String? userAgent String? timestamp DateTime @default(now()) metadata Json? @@map("audit_logs") } model ErrorLog { id String @id @default(cuid()) level String message String stack String? context Json? userId String? requestId String? timestamp DateTime @default(now()) resolved Boolean @default(false) @@map("error_logs") } model PerformanceLog { id String @id @default(cuid()) endpoint String method String duration Int statusCode Int userId String? requestId String? timestamp DateTime @default(now()) metadata Json? @@map("performance_logs") } model RequestLog { id String @id @default(cuid()) method String url String statusCode Int duration Int userId String? ipAddress String? userAgent String? requestId String? timestamp DateTime @default(now()) requestBody Json? responseBody Json? @@map("request_logs") }`; let schema = fs.readFileSync(schemaPath, 'utf8'); // Verificar si los modelos ya existen if (!schema.includes('model AuditLog')) { schema += auditLogModel; fs.writeFileSync(schemaPath, schema); console.log('📊 Modelos de logging agregados al schema de Prisma'); } } async updatePackageJson() { const packageJsonPath = path.join(this.projectPath, 'package.json'); if (!fs.existsSync(packageJsonPath)) { console.log('⚠️ package.json no encontrado'); return; } const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')); const dependencies = { 'winston': '^3.11.0', 'winston-daily-rotate-file': '^4.7.1', 'nest-winston': '^1.9.4' }; // Agregar dependencias opcionales if (this.config.sentryDsn) { dependencies['@sentry/node'] = '^7.77.0'; dependencies['@sentry/integrations'] = '^7.77.0'; } if (this.config.elasticsearchUrl) { dependencies['winston-elasticsearch'] = '^0.17.4'; } packageJson.dependencies = { ...packageJson.dependencies, ...dependencies }; // Agregar scripts de logging packageJson.scripts = { ...packageJson.scripts, 'logs:rotate': 'node scripts/rotate-logs.js', 'logs:analyze': 'node scripts/analyze-logs.js', 'logs:export': 'node scripts/export-audit.js', 'logs:cleanup': 'node scripts/cleanup-logs.js' }; fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); console.log('📦 package.json actualizado con dependencias de logging'); } getGeneratedFiles() { return [ 'src/logging/winston.config.ts', 'src/logging/logger.service.ts', 'src/logging/audit.middleware.ts', 'src/logging/logging.interceptor.ts', 'src/logging/audit.service.ts', 'src/logging/logging.controller.ts', 'src/logging/logging.module.ts' ]; } } module.exports = LoggingModuleInitializer; // CLI usage if (require.main === module) { const projectPath = process.argv[2] || process.cwd(); const config = { logLevel: process.argv[3] || 'info', enableConsoleLog: process.argv.includes('--console'), enableFileLog: process.argv.includes('--file'), enableDatabaseLog: process.argv.includes('--database'), sentryDsn: process.env.SENTRY_DSN, elasticsearchUrl: process.env.ELASTICSEARCH_URL }; const initializer = new LoggingModuleInitializer(projectPath, config); initializer.initialize() .then(result => { console.log('\n🎉 Módulo de logging configurado exitosamente!'); console.log('\n📁 Archivos generados:'); result.files.forEach(file => console.log(` - ${file}`)); console.log('\n⚙️ Configuración aplicada:'); console.log(` - Log Level: ${result.config.logLevel}`); console.log(` - Console Log: ${result.config.enableConsoleLog}`); console.log(` - File Log: ${result.config.enableFileLog}`); console.log(` - Database Log: ${result.config.enableDatabaseLog}`); if (result.config.sentryDsn) { console.log(` - Sentry: Configurado`); } if (result.config.elasticsearchUrl) { console.log(` - Elasticsearch: Configurado`); } console.log('\n🚀 Próximos pasos:'); console.log(' 1. Ejecutar: npm install'); console.log(' 2. Ejecutar: npx prisma generate'); console.log(' 3. Ejecutar: npx prisma db push'); console.log(' 4. Configurar variables de entorno en .env'); console.log(' 5. Importar LoggingModule en tu AppModule'); }) .catch(error => { console.error('❌ Error:', error.message); process.exit(1); }); }