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.

379 lines (321 loc) 11.2 kB
const fs = require('fs'); const path = require('path'); const Handlebars = require('handlebars'); class EmailModuleInitializer { constructor(config = {}) { this.config = config; this.modulePath = __dirname; this.projectRoot = config.projectRoot || process.cwd(); this.templatesDir = path.join(this.modulePath, 'templates'); } async initialize() { try { console.log('🔧 Inicializando módulo Email...'); // 1. Detectar proveedor de email const provider = this.detectEmailProvider(); console.log(`📧 Proveedor detectado: ${provider}`); // 2. Configurar estructura de directorios this.setupDirectories(); // 3. Generar archivos de configuración await this.generateEmailConfig(provider); // 4. Generar servicio de email await this.generateEmailService(provider); // 5. Generar servicio de cola await this.generateQueueService(); // 6. Generar templates HTML await this.generateEmailTemplates(); // 7. Generar controlador y rutas await this.generateControllerAndRoutes(); // 8. Configurar base de datos await this.setupDatabase(); // 9. Actualizar package.json await this.updatePackageJson(provider); const result = { success: true, module: 'email', provider: provider, features: this.getEnabledFeatures(), files: this.getGeneratedFiles(), endpoints: this.getApiEndpoints(), templates: this.getEmailTemplates(), message: `Módulo Email inicializado con proveedor ${provider}` }; console.log('✅ Módulo Email inicializado correctamente'); return result; } catch (error) { console.error('❌ Error inicializando módulo Email:', error.message); throw error; } } detectEmailProvider() { const env = process.env; if (env.RESEND_API_KEY) { return 'resend'; } else if (env.SENDGRID_API_KEY) { return 'sendgrid'; } else if (env.SMTP_HOST && env.SMTP_USER) { return 'nodemailer'; } else { // Default a nodemailer con configuración básica return 'nodemailer'; } } setupDirectories() { const dirs = [ 'src/email', 'src/email/templates', 'src/email/providers', 'src/email/queue' ]; dirs.forEach(dir => { const fullPath = path.join(this.projectRoot, dir); if (!fs.existsSync(fullPath)) { fs.mkdirSync(fullPath, { recursive: true }); } }); } async generateEmailConfig(provider) { const templatePath = path.join(this.templatesDir, 'email.config.ts.hbs'); const outputPath = path.join(this.projectRoot, 'src/email/email.config.ts'); const context = { provider: provider, config: this.config, features: { queue: true, templates: true, tracking: this.config.enableTracking || false, attachments: this.config.enableAttachments || true } }; await this.renderTemplate(templatePath, outputPath, context); } async generateEmailService(provider) { const templatePath = path.join(this.templatesDir, 'email.service.ts.hbs'); const outputPath = path.join(this.projectRoot, 'src/email/email.service.ts'); const context = { provider: provider, config: this.config, features: { queue: true, templates: true, validation: true, logging: this.config.enableLogging !== false } }; await this.renderTemplate(templatePath, outputPath, context); } async generateQueueService() { const templatePath = path.join(this.templatesDir, 'queue.service.ts.hbs'); const outputPath = path.join(this.projectRoot, 'src/email/queue.service.ts'); const context = { config: this.config, queue: { defaultAttempts: 3, retryDelay: 5000, maxConcurrent: 10 } }; await this.renderTemplate(templatePath, outputPath, context); } async generateEmailTemplates() { const templates = [ { name: 'verify-email', subject: 'Verifica tu email', variables: ['userName', 'verificationLink', 'companyName'] }, { name: 'reset-password', subject: 'Resetea tu contraseña', variables: ['userName', 'resetLink', 'expirationTime'] }, { name: 'welcome', subject: 'Bienvenido', variables: ['userName', 'loginLink', 'supportEmail'] } ]; for (const template of templates) { const templatePath = path.join(this.templatesDir, 'html-templates', `${template.name}.html.hbs`); const outputPath = path.join(this.projectRoot, 'src/email/templates', `${template.name}.html`); const context = { template: template, config: this.config }; await this.renderTemplate(templatePath, outputPath, context); } } async generateControllerAndRoutes() { // Generar controlador const controllerPath = path.join(this.templatesDir, 'email.controller.ts.hbs'); const controllerOutput = path.join(this.projectRoot, 'src/email/email.controller.ts'); const controllerContext = { config: this.config, features: { auth: this.config.enableAuth !== false, validation: true, logging: this.config.enableLogging !== false } }; await this.renderTemplate(controllerPath, controllerOutput, controllerContext); // Generar rutas const routesPath = path.join(this.templatesDir, 'email.routes.ts.hbs'); const routesOutput = path.join(this.projectRoot, 'src/email/email.routes.ts'); await this.renderTemplate(routesPath, routesOutput, controllerContext); } async setupDatabase() { // Crear esquema de tablas para email const schema = ` -- Email module tables CREATE TABLE IF NOT EXISTS email_logs ( id SERIAL PRIMARY KEY, to_email VARCHAR(255) NOT NULL, from_email VARCHAR(255) NOT NULL, subject VARCHAR(500) NOT NULL, template_name VARCHAR(100), status VARCHAR(50) DEFAULT 'pending', provider VARCHAR(50), external_id VARCHAR(255), error_message TEXT, sent_at TIMESTAMP, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS email_templates ( id SERIAL PRIMARY KEY, name VARCHAR(100) UNIQUE NOT NULL, subject VARCHAR(500) NOT NULL, html_content TEXT NOT NULL, text_content TEXT, variables JSONB, is_active BOOLEAN DEFAULT true, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE TABLE IF NOT EXISTS email_queue ( id SERIAL PRIMARY KEY, to_email VARCHAR(255) NOT NULL, template_name VARCHAR(100), variables JSONB, priority VARCHAR(20) DEFAULT 'normal', attempts INTEGER DEFAULT 0, max_attempts INTEGER DEFAULT 3, status VARCHAR(50) DEFAULT 'pending', scheduled_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, processed_at TIMESTAMP, error_message TEXT, created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ); CREATE INDEX IF NOT EXISTS idx_email_logs_status ON email_logs(status); CREATE INDEX IF NOT EXISTS idx_email_logs_created_at ON email_logs(created_at); CREATE INDEX IF NOT EXISTS idx_email_queue_status ON email_queue(status); CREATE INDEX IF NOT EXISTS idx_email_queue_scheduled_at ON email_queue(scheduled_at); `; const schemaPath = path.join(this.projectRoot, 'src/email/schema.sql'); fs.writeFileSync(schemaPath, schema); } async updatePackageJson(provider) { const packagePath = path.join(this.projectRoot, 'package.json'); let packageJson = {}; if (fs.existsSync(packagePath)) { packageJson = JSON.parse(fs.readFileSync(packagePath, 'utf8')); } if (!packageJson.dependencies) { packageJson.dependencies = {}; } // Dependencias base const baseDependencies = { 'bull': '^4.12.0', 'ioredis': '^5.3.2', 'handlebars': '^4.7.8', 'validator': '^13.11.0' }; // Dependencias específicas del proveedor const providerDependencies = { nodemailer: { 'nodemailer': '^6.9.7', '@types/nodemailer': '^6.4.14' }, resend: { 'resend': '^2.1.0' }, sendgrid: { '@sendgrid/mail': '^7.7.0' } }; Object.assign(packageJson.dependencies, baseDependencies); Object.assign(packageJson.dependencies, providerDependencies[provider] || {}); fs.writeFileSync(packagePath, JSON.stringify(packageJson, null, 2)); } async renderTemplate(templatePath, outputPath, context) { if (!fs.existsSync(templatePath)) { console.warn(`⚠️ Template no encontrado: ${templatePath}`); return; } // Registrar helper ifEquals para Handlebars Handlebars.registerHelper('ifEquals', function(arg1, arg2, options) { return (arg1 == arg2) ? options.fn(this) : options.inverse(this); }); const templateContent = fs.readFileSync(templatePath, 'utf8'); const template = Handlebars.compile(templateContent); const result = template(context); const outputDir = path.dirname(outputPath); if (!fs.existsSync(outputDir)) { fs.mkdirSync(outputDir, { recursive: true }); } fs.writeFileSync(outputPath, result); } getEnabledFeatures() { return [ 'multiple-providers', 'html-templates', 'email-queue', 'template-variables', 'email-tracking', 'retry-mechanism', 'email-validation' ]; } getGeneratedFiles() { return [ 'src/email/email.config.ts', 'src/email/email.service.ts', 'src/email/queue.service.ts', 'src/email/email.controller.ts', 'src/email/email.routes.ts', 'src/email/templates/verify-email.html', 'src/email/templates/reset-password.html', 'src/email/templates/welcome.html', 'src/email/schema.sql' ]; } getApiEndpoints() { return [ { method: 'POST', path: '/email/send', description: 'Enviar email individual' }, { method: 'POST', path: '/email/send-bulk', description: 'Enviar emails masivos' }, { method: 'GET', path: '/email/templates', description: 'Listar templates' }, { method: 'POST', path: '/email/test', description: 'Probar configuración' }, { method: 'GET', path: '/email/queue/status', description: 'Estado de la cola' } ]; } getEmailTemplates() { return [ { name: 'verify-email', description: 'Verificación de email', variables: ['userName', 'verificationLink', 'companyName'] }, { name: 'reset-password', description: 'Reseteo de contraseña', variables: ['userName', 'resetLink', 'expirationTime'] }, { name: 'welcome', description: 'Bienvenida a nuevos usuarios', variables: ['userName', 'loginLink', 'supportEmail'] } ]; } } module.exports = EmailModuleInitializer;