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
JavaScript
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);
});
}