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
JavaScript
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
userId String?
action String
resource String
resourceId String?
oldValues Json?
newValues Json?
ipAddress String?
userAgent String?
timestamp DateTime
metadata Json?
@
}
model ErrorLog {
id String
level String
message String
stack String?
context Json?
userId String?
requestId String?
timestamp DateTime
resolved Boolean
@
}
model PerformanceLog {
id String
endpoint String
method String
duration Int
statusCode Int
userId String?
requestId String?
timestamp DateTime
metadata Json?
@
}
model RequestLog {
id String
method String
url String
statusCode Int
duration Int
userId String?
ipAddress String?
userAgent String?
requestId String?
timestamp DateTime
requestBody Json?
responseBody Json?
@
}`;
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);
});
}