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.

494 lines (407 loc) β€’ 14.1 kB
#!/usr/bin/env node /** * MCP Backend Project Initialization Script * Automates the setup of a new backend project with selected modules */ const fs = require('fs'); const path = require('path'); const { execSync } = require('child_process'); const readline = require('readline'); class ProjectInitializer { constructor() { this.projectName = ''; this.projectPath = ''; this.selectedModules = []; this.databaseType = 'postgres'; this.features = { docker: true, ci: true, monitoring: true, testing: true }; } async init() { console.log('πŸš€ MCP Backend Project Initializer'); console.log('===================================\n'); try { await this.promptProjectDetails(); await this.selectModules(); await this.selectFeatures(); await this.createProject(); await this.installDependencies(); await this.generateDocumentation(); console.log('\nπŸŽ‰ Project initialized successfully!'); console.log(`πŸ“ Project location: ${this.projectPath}`); console.log('\nπŸ“‹ Next steps:'); console.log('1. cd ' + this.projectName); console.log('2. npm run dev'); console.log('3. Open http://localhost:3000'); } catch (error) { console.error('❌ Error initializing project:', error.message); process.exit(1); } } async promptProjectDetails() { const rl = readline.createInterface({ input: process.stdin, output: process.stdout }); this.projectName = await this.question(rl, 'πŸ“ Project name: '); this.projectPath = path.join(process.cwd(), this.projectName); const dbChoice = await this.question(rl, 'πŸ—„οΈ Database type (postgres/mysql) [postgres]: '); this.databaseType = dbChoice.toLowerCase() || 'postgres'; rl.close(); } async selectModules() { console.log('\nπŸ“¦ Available modules:'); const availableModules = [ { name: 'auth', description: 'Authentication & Authorization', required: true }, { name: 'database', description: 'Database connection & ORM', required: true }, { name: 'crud', description: 'CRUD operations generator', recommended: true }, { name: 'email', description: 'Email service', recommended: true }, { name: 'websockets', description: 'Real-time communication', optional: true }, { name: 'cache', description: 'Caching layer', recommended: true }, { name: 'logging', description: 'Logging system', required: true }, { name: 'monitoring', description: 'Monitoring & observability', recommended: true }, { name: 'notifications', description: 'Push notifications', optional: true }, { name: 'validation', description: 'Data validation', recommended: true }, { name: 'testing', description: 'Testing framework', required: true }, { name: 'docker', description: 'Docker configuration', recommended: true }, { name: 'ci', description: 'CI/CD pipelines', recommended: true } ]; // Auto-select required modules this.selectedModules = availableModules .filter(module => module.required) .map(module => module.name); // Auto-select recommended modules (can be customized) const recommendedModules = availableModules .filter(module => module.recommended) .map(module => module.name); this.selectedModules.push(...recommendedModules); console.log('βœ… Selected modules:', this.selectedModules.join(', ')); } async selectFeatures() { console.log('\nβš™οΈ Project features:'); console.log('βœ… Docker support: enabled'); console.log('βœ… CI/CD pipelines: enabled'); console.log('βœ… Monitoring: enabled'); console.log('βœ… Testing: enabled'); } async createProject() { console.log('\nπŸ—οΈ Creating project structure...'); // Create project directory if (fs.existsSync(this.projectPath)) { throw new Error(`Directory ${this.projectName} already exists`); } fs.mkdirSync(this.projectPath, { recursive: true }); // Create basic structure const directories = [ 'src', 'src/controllers', 'src/services', 'src/middleware', 'src/routes', 'src/types', 'src/utils', 'config', 'tests', 'docs', 'scripts' ]; directories.forEach(dir => { fs.mkdirSync(path.join(this.projectPath, dir), { recursive: true }); }); // Generate package.json await this.generatePackageJson(); // Generate environment files await this.generateEnvironmentFiles(); // Generate TypeScript config await this.generateTSConfig(); // Initialize modules await this.initializeModules(); // Generate main application files await this.generateAppFiles(); } async generatePackageJson() { const packageJson = { name: this.projectName, version: '1.0.0', description: 'Backend application generated with MCP Backend Generator', main: 'dist/index.js', scripts: { 'dev': 'tsx watch src/index.ts', 'build': 'tsc', 'start': 'node dist/index.js', 'test': 'jest', 'test:watch': 'jest --watch', 'test:coverage': 'jest --coverage', 'lint': 'eslint src/**/*.ts', 'lint:fix': 'eslint src/**/*.ts --fix', 'format': 'prettier --write src/**/*.ts', 'db:migrate': 'prisma migrate dev', 'db:generate': 'prisma generate', 'db:seed': 'tsx prisma/seeds/seed.ts', 'docker:build': 'docker build -t ' + this.projectName + ' .', 'docker:run': 'docker run -p 3000:3000 ' + this.projectName }, dependencies: { 'express': '^4.18.2', 'cors': '^2.8.5', 'helmet': '^7.0.0', 'dotenv': '^16.3.1', 'bcrypt': '^5.1.0', 'jsonwebtoken': '^9.0.2', 'joi': '^17.9.2', 'winston': '^3.10.0' }, devDependencies: { '@types/node': '^20.5.0', '@types/express': '^4.17.17', '@types/cors': '^2.8.13', '@types/bcrypt': '^5.0.0', '@types/jsonwebtoken': '^9.0.2', '@types/jest': '^29.5.4', 'typescript': '^5.1.6', 'tsx': '^3.12.7', 'jest': '^29.6.2', 'ts-jest': '^29.1.1', 'eslint': '^8.47.0', '@typescript-eslint/parser': '^6.4.0', '@typescript-eslint/eslint-plugin': '^6.4.0', 'prettier': '^3.0.1' }, keywords: ['backend', 'api', 'nodejs', 'typescript', 'mcp'], author: 'MCP Backend Generator', license: 'MIT' }; // Add database-specific dependencies if (this.databaseType === 'postgres') { packageJson.dependencies['pg'] = '^8.11.0'; packageJson.dependencies['@prisma/client'] = '^5.0.0'; packageJson.devDependencies['@types/pg'] = '^8.10.0'; packageJson.devDependencies['prisma'] = '^5.0.0'; } else if (this.databaseType === 'mysql') { packageJson.dependencies['mysql2'] = '^3.6.0'; packageJson.dependencies['@prisma/client'] = '^5.0.0'; packageJson.devDependencies['@types/mysql2'] = '^3.0.0'; packageJson.devDependencies['prisma'] = '^5.0.0'; } fs.writeFileSync( path.join(this.projectPath, 'package.json'), JSON.stringify(packageJson, null, 2) ); } async generateEnvironmentFiles() { const envContent = `# Application NODE_ENV=development PORT=3000 API_VERSION=v1 # Database DATABASE_URL="${this.databaseType}://username:password@localhost:${this.databaseType === 'postgres' ? '5432' : '3306'}/${this.projectName}" ${this.databaseType.toUpperCase()}_HOST=localhost ${this.databaseType.toUpperCase()}_PORT=${this.databaseType === 'postgres' ? '5432' : '3306'} ${this.databaseType.toUpperCase()}_${this.databaseType === 'postgres' ? 'DB' : 'DATABASE'}=${this.projectName} ${this.databaseType.toUpperCase()}_USER=username ${this.databaseType.toUpperCase()}_PASSWORD=password # JWT JWT_SECRET=your-super-secret-jwt-key-change-this-in-production JWT_EXPIRES_IN=7d # Email SMTP_HOST=smtp.gmail.com SMTP_PORT=587 SMTP_USER=your-email@gmail.com SMTP_PASS=your-app-password # Redis (if using cache) REDIS_URL=redis://localhost:6379 # Monitoring MONITORING_ENABLED=true PROMETHEUS_PORT=9090 GRAFANA_PORT=3001 # Logging LOG_LEVEL=info LOG_FILE=logs/app.log`; fs.writeFileSync(path.join(this.projectPath, '.env'), envContent); fs.writeFileSync(path.join(this.projectPath, '.env.example'), envContent); } async generateTSConfig() { const tsConfig = { compilerOptions: { target: 'ES2020', module: 'commonjs', lib: ['ES2020'], outDir: './dist', rootDir: './src', strict: true, esModuleInterop: true, skipLibCheck: true, forceConsistentCasingInFileNames: true, resolveJsonModule: true, declaration: true, declarationMap: true, sourceMap: true, experimentalDecorators: true, emitDecoratorMetadata: true }, include: ['src/**/*'], exclude: ['node_modules', 'dist', 'tests'] }; fs.writeFileSync( path.join(this.projectPath, 'tsconfig.json'), JSON.stringify(tsConfig, null, 2) ); } async initializeModules() { console.log('πŸ”§ Initializing modules...'); const mcpPath = path.join(__dirname, '..'); const orchestratorPath = path.join(mcpPath, 'orchestrator.js'); if (fs.existsSync(orchestratorPath)) { // Use the orchestrator to initialize modules const { Orchestrator } = require(orchestratorPath); const orchestrator = new Orchestrator(); for (const moduleName of this.selectedModules) { console.log(` πŸ“¦ Initializing ${moduleName}...`); try { await orchestrator.initializeModule(moduleName, { projectPath: this.projectPath, databaseType: this.databaseType }); } catch (error) { console.warn(` ⚠️ Warning: Could not initialize ${moduleName}:`, error.message); } } } else { console.warn('⚠️ Orchestrator not found, skipping module initialization'); } } async generateAppFiles() { // Generate main app file const appContent = `import express from 'express'; import cors from 'cors'; import helmet from 'helmet'; import dotenv from 'dotenv'; import { errorHandler } from './middleware/errorHandler'; import { logger } from './utils/logger'; import routes from './routes'; // Load environment variables dotenv.config(); const app = express(); const PORT = process.env.PORT || 3000; // Middleware app.use(helmet()); app.use(cors()); app.use(express.json({ limit: '10mb' })); app.use(express.urlencoded({ extended: true })); // Routes app.use('/api/v1', routes); // Health check app.get('/health', (req, res) => { res.json({ status: 'OK', timestamp: new Date().toISOString() }); }); // Error handling app.use(errorHandler); // Start server app.listen(PORT, () => { logger.info(\`πŸš€ Server running on port \${PORT}\`); logger.info(\`πŸ“ Environment: \${process.env.NODE_ENV}\`); logger.info(\`πŸ”— API: http://localhost:\${PORT}/api/v1\`); }); export default app;`; fs.writeFileSync(path.join(this.projectPath, 'src/app.ts'), appContent); // Generate index file const indexContent = `import app from './app'; // Handle unhandled promise rejections process.on('unhandledRejection', (err: Error) => { console.error('Unhandled Promise Rejection:', err); process.exit(1); }); // Handle uncaught exceptions process.on('uncaughtException', (err: Error) => { console.error('Uncaught Exception:', err); process.exit(1); }); export default app;`; fs.writeFileSync(path.join(this.projectPath, 'src/index.ts'), indexContent); } async installDependencies() { console.log('\nπŸ“¦ Installing dependencies...'); try { process.chdir(this.projectPath); execSync('npm install', { stdio: 'inherit' }); } catch (error) { console.warn('⚠️ Could not install dependencies automatically. Run "npm install" manually.'); } } async generateDocumentation() { const readmeContent = `# ${this.projectName} Backend application generated with MCP Backend Generator. ## Features ${this.selectedModules.map(module => `- βœ… ${module.charAt(0).toUpperCase() + module.slice(1)}`).join('\n')} ## Quick Start 1. Install dependencies: \`\`\`bash npm install \`\`\` 2. Set up environment variables: \`\`\`bash cp .env.example .env # Edit .env with your configuration \`\`\` 3. Set up database: \`\`\`bash npm run db:migrate npm run db:seed \`\`\` 4. Start development server: \`\`\`bash npm run dev \`\`\` ## API Documentation The API will be available at \`http://localhost:3000/api/v1\` ### Health Check \`\`\` GET /health \`\`\` ## Scripts - \`npm run dev\` - Start development server - \`npm run build\` - Build for production - \`npm run start\` - Start production server - \`npm test\` - Run tests - \`npm run lint\` - Lint code - \`npm run format\` - Format code ## Database This project uses ${this.databaseType.charAt(0).toUpperCase() + this.databaseType.slice(1)} as the database. ### Migrations \`\`\`bash npm run db:migrate # Run migrations npm run db:generate # Generate Prisma client npm run db:seed # Seed database \`\`\` ## Docker \`\`\`bash npm run docker:build # Build Docker image npm run docker:run # Run Docker container \`\`\` ## Contributing 1. Fork the repository 2. Create a feature branch 3. Make your changes 4. Add tests 5. Submit a pull request ## License MIT`; fs.writeFileSync(path.join(this.projectPath, 'README.md'), readmeContent); } question(rl, prompt) { return new Promise((resolve) => { rl.question(prompt, (answer) => { resolve(answer.trim()); }); }); } } // Run the initializer if (require.main === module) { const initializer = new ProjectInitializer(); initializer.init().catch(console.error); } module.exports = ProjectInitializer;