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.

368 lines (298 loc) 11.8 kB
const fs = require('fs'); const path = require('path'); const Handlebars = require('handlebars'); class CacheModuleInitializer { constructor(projectPath, config = {}) { this.projectPath = projectPath; this.config = { redisUrl: config.redisUrl || process.env.REDIS_URL, redisHost: config.redisHost || process.env.REDIS_HOST || 'localhost', redisPort: config.redisPort || process.env.REDIS_PORT || 6379, redisPassword: config.redisPassword || process.env.REDIS_PASSWORD, redisDb: config.redisDb || process.env.REDIS_DB || 0, redisClusterNodes: config.redisClusterNodes || process.env.REDIS_CLUSTER_NODES, cacheTtlDefault: config.cacheTtlDefault || process.env.CACHE_TTL_DEFAULT || 3600, cacheMaxMemory: config.cacheMaxMemory || process.env.CACHE_MAX_MEMORY || '256mb', cacheEvictionPolicy: config.cacheEvictionPolicy || process.env.CACHE_EVICTION_POLICY || 'allkeys-lru', enableCacheCompression: config.enableCacheCompression !== false, cacheKeyPrefix: config.cacheKeyPrefix || process.env.CACHE_KEY_PREFIX || 'app:', ...config }; this.modulePath = path.join(__dirname); } async initialize() { console.log('🚀 Inicializando módulo de caché...'); try { // 1. Configurar estructura de directorios await this.setupDirectories(); // 2. Generar configuración de Redis await this.generateRedisConfig(); // 3. Generar servicio de caché await this.generateCacheService(); // 4. Generar manager de caché await this.generateCacheManager(); // 5. Generar decorador cacheable await this.generateCacheableDecorator(); // 6. Generar interceptor de caché await this.generateCacheInterceptor(); // 7. Generar interfaces await this.generateInterfaces(); // 8. Generar estrategias await this.generateStrategies(); // 9. Generar controlador await this.generateController(); // 10. Generar módulo await this.generateModule(); // 11. Actualizar package.json await this.updatePackageJson(); console.log('✅ Módulo de caché inicializado correctamente'); return { success: true, message: 'Cache module initialized successfully', files: this.getGeneratedFiles(), config: this.config }; } catch (error) { console.error('❌ Error inicializando módulo de caché:', error); throw error; } } async setupDirectories() { const dirs = [ 'src/cache', 'src/cache/interfaces', 'src/cache/strategies', 'src/cache/decorators', 'src/cache/interceptors' ]; for (const dir of dirs) { const fullPath = path.join(this.projectPath, dir); if (!fs.existsSync(fullPath)) { fs.mkdirSync(fullPath, { recursive: true }); } } } async generateRedisConfig() { const templatePath = path.join(this.modulePath, 'templates', 'redis.config.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/cache/redis.config.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config, hasRedisUrl: !!this.config.redisUrl, hasClusterNodes: !!this.config.redisClusterNodes, hasPassword: !!this.config.redisPassword }); fs.writeFileSync(outputPath, content); } async generateCacheService() { const templatePath = path.join(this.modulePath, 'templates', 'cache.service.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/cache/cache.service.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config, enableCompression: this.config.enableCacheCompression }); fs.writeFileSync(outputPath, content); } async generateCacheManager() { const templatePath = path.join(this.modulePath, 'templates', 'cache.manager.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/cache/cache.manager.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config }); fs.writeFileSync(outputPath, content); } async generateCacheableDecorator() { const templatePath = path.join(this.modulePath, 'templates', 'cacheable.decorator.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/cache/decorators/cacheable.decorator.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config }); fs.writeFileSync(outputPath, content); } async generateCacheInterceptor() { const templatePath = path.join(this.modulePath, 'templates', 'cache.interceptor.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/cache/interceptors/cache.interceptor.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config }); fs.writeFileSync(outputPath, content); } async generateInterfaces() { const interfaceTemplate = `export interface CacheOptions { ttl?: number; key?: string; tags?: string[]; compress?: boolean; serialize?: boolean; } export interface CacheStats { hits: number; misses: number; hitRate: number; memoryUsage: number; keyCount: number; connections: number; } export interface CacheKey { key: string; ttl: number; size: number; type: string; tags?: string[]; } export interface CacheInvalidationOptions { keys?: string[]; tags?: string[]; pattern?: string; all?: boolean; } export interface CacheWarmingOptions { keys: string[]; data: Record<string, any>; ttl?: number; overwrite?: boolean; }`; const outputPath = path.join(this.projectPath, 'src/cache/interfaces/cache.interface.ts'); fs.writeFileSync(outputPath, interfaceTemplate); } async generateStrategies() { const strategyTemplate = `export interface CacheStrategy { get<T>(key: string): Promise<T | null>; set<T>(key: string, value: T, ttl?: number): Promise<void>; delete(key: string): Promise<void>; clear(): Promise<void>; } export abstract class BaseCacheStrategy implements CacheStrategy { abstract get<T>(key: string): Promise<T | null>; abstract set<T>(key: string, value: T, ttl?: number): Promise<void>; abstract delete(key: string): Promise<void>; abstract clear(): Promise<void>; protected generateKey(key: string, prefix?: string): string { return prefix ? \`\${prefix}:\${key}\` : key; } protected serializeValue(value: any): string { return JSON.stringify(value); } protected deserializeValue<T>(value: string): T { return JSON.parse(value); } }`; const outputPath = path.join(this.projectPath, 'src/cache/strategies/cache-strategy.interface.ts'); fs.writeFileSync(outputPath, strategyTemplate); } async generateController() { const templatePath = path.join(this.modulePath, 'templates', 'cache.controller.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/cache/cache.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', 'cache.module.ts.hbs'); const outputPath = path.join(this.projectPath, 'src/cache/cache.module.ts'); const template = fs.readFileSync(templatePath, 'utf8'); const compiledTemplate = Handlebars.compile(template); const content = compiledTemplate({ config: this.config, hasRedisUrl: !!this.config.redisUrl, hasClusterNodes: !!this.config.redisClusterNodes }); fs.writeFileSync(outputPath, content); } 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 = { 'redis': '^4.6.0', '@nestjs/cache-manager': '^2.1.0', 'cache-manager': '^5.2.0', 'cache-manager-redis-store': '^3.0.1' }; // Agregar dependencias opcionales if (this.config.enableCacheCompression) { dependencies['lz4'] = '^0.6.5'; dependencies['snappy'] = '^7.2.2'; } packageJson.dependencies = { ...packageJson.dependencies, ...dependencies }; // Agregar scripts de caché packageJson.scripts = { ...packageJson.scripts, 'cache:warm': 'node scripts/warm-cache.js', 'cache:monitor': 'node scripts/monitor-cache.js', 'cache:clear': 'node scripts/clear-cache.js', 'cache:stats': 'node scripts/cache-stats.js' }; fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2)); console.log('📦 package.json actualizado con dependencias de caché'); } getGeneratedFiles() { return [ 'src/cache/redis.config.ts', 'src/cache/cache.service.ts', 'src/cache/cache.manager.ts', 'src/cache/decorators/cacheable.decorator.ts', 'src/cache/interceptors/cache.interceptor.ts', 'src/cache/interfaces/cache.interface.ts', 'src/cache/strategies/cache-strategy.interface.ts', 'src/cache/cache.controller.ts', 'src/cache/cache.module.ts' ]; } } module.exports = CacheModuleInitializer; // CLI usage if (require.main === module) { const projectPath = process.argv[2] || process.cwd(); const config = { redisHost: process.argv[3] || 'localhost', redisPort: parseInt(process.argv[4]) || 6379, enableCacheCompression: process.argv.includes('--compression'), redisUrl: process.env.REDIS_URL, redisPassword: process.env.REDIS_PASSWORD }; const initializer = new CacheModuleInitializer(projectPath, config); initializer.initialize() .then(result => { console.log('\n🎉 Módulo de caché configurado exitosamente!'); console.log('\n📁 Archivos generados:'); result.files.forEach(file => console.log(` - ${file}`)); console.log('\n⚙️ Configuración aplicada:'); console.log(` - Redis Host: ${result.config.redisHost}`); console.log(` - Redis Port: ${result.config.redisPort}`); console.log(` - Default TTL: ${result.config.cacheTtlDefault}s`); console.log(` - Key Prefix: ${result.config.cacheKeyPrefix}`); console.log(` - Compression: ${result.config.enableCacheCompression}`); if (result.config.redisUrl) { console.log(` - Redis URL: Configurado`); } console.log('\n🚀 Próximos pasos:'); console.log(' 1. Ejecutar: npm install'); console.log(' 2. Configurar Redis server'); console.log(' 3. Configurar variables de entorno en .env'); console.log(' 4. Importar CacheModule en tu AppModule'); console.log(' 5. Usar @Cacheable() decorator en tus métodos'); }) .catch(error => { console.error('❌ Error:', error.message); process.exit(1); }); }