build-app-with 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +240 -0
- package/bin/cli.js +18 -0
- package/index.js +3 -0
- package/package.json +80 -0
- package/src/__tests__/core/error-handler.test.js +99 -0
- package/src/__tests__/core/logger.test.js +93 -0
- package/src/__tests__/e2e/cli-integration.test.js +220 -0
- package/src/__tests__/e2e/framework-generation.test.js +249 -0
- package/src/__tests__/setup.js +70 -0
- package/src/config/package-mappings.js +110 -0
- package/src/constants/index.js +42 -0
- package/src/core/error-handler.js +89 -0
- package/src/core/logger.js +89 -0
- package/src/core/package-manager.js +114 -0
- package/src/create-app.js +90 -0
- package/src/generators/express/index.js +52 -0
- package/src/generators/express/project-generator.js +367 -0
- package/src/generators/express/prompts.js +74 -0
- package/src/generators/express/simple-generator.js +275 -0
- package/src/generators/express/templates/app.js +73 -0
- package/src/generators/express/templates/config/database.js +122 -0
- package/src/generators/express/templates/config.js +37 -0
- package/src/generators/express/templates/controllers.js +49 -0
- package/src/generators/express/templates/docker.js +72 -0
- package/src/generators/express/templates/middleware/errorHandler.js +49 -0
- package/src/generators/express/templates/middleware.js +59 -0
- package/src/generators/express/templates/models.js +77 -0
- package/src/generators/express/templates/package-json.js +55 -0
- package/src/generators/express/templates/readme.js +310 -0
- package/src/generators/express/templates/routes.js +36 -0
- package/src/generators/express/templates/server.js +59 -0
- package/src/generators/express/templates/services.js +55 -0
- package/src/generators/express/templates/tests.js +46 -0
- package/src/generators/express/templates/utils/logger.js +54 -0
- package/src/generators/fastify/index.js +46 -0
- package/src/generators/fastify/project-generator.js +373 -0
- package/src/generators/fastify/prompts.js +76 -0
- package/src/generators/fastify/templates/app.js +179 -0
- package/src/generators/fastify/templates/config.js +33 -0
- package/src/generators/fastify/templates/docker.js +73 -0
- package/src/generators/fastify/templates/models.js +77 -0
- package/src/generators/fastify/templates/package-json.js +57 -0
- package/src/generators/fastify/templates/plugins.js +38 -0
- package/src/generators/fastify/templates/readme.js +328 -0
- package/src/generators/fastify/templates/routes.js +32 -0
- package/src/generators/fastify/templates/server.js +71 -0
- package/src/generators/fastify/templates/services.js +50 -0
- package/src/generators/fastify/templates/tests.js +60 -0
- package/src/generators/nextjs/dependency-manager.js +99 -0
- package/src/generators/nextjs/file-generator.js +256 -0
- package/src/generators/nextjs/nextjs-generator.js +177 -0
- package/src/generators/nextjs/nextjs-project-generator.js +896 -0
- package/src/generators/nextjs/package-mappings.js +51 -0
- package/src/generators/nextjs/templates.js +272 -0
- package/src/generators/package-json-generator.js +117 -0
- package/src/generators/vite/components/CreditComponent.jsx +41 -0
- package/src/generators/vite/components/app-component.js +359 -0
- package/src/generators/vite/components/main-file.js +88 -0
- package/src/generators/vite/eslint-config-generator.js +20 -0
- package/src/generators/vite/file-generator.js +796 -0
- package/src/generators/vite/prettier-config-generator.js +10 -0
- package/src/generators/vite/structures/domain-driven-structure.js +465 -0
- package/src/generators/vite/structures/feature-based-structure.js +342 -0
- package/src/generators/vite/structures/simple-structure.js +62 -0
- package/src/generators/vite/styles/index-css.js +130 -0
- package/src/generators/vite/tailwind-config-generator.js +14 -0
- package/src/generators/vite/vite-config-generator.js +22 -0
- package/src/generators/vite/vite-project-generator.js +263 -0
- package/src/generators/vite-project-generator.js +136 -0
- package/src/prompts/index.js +262 -0
- package/src/types/index.js +113 -0
- package/src/utils/answer-helpers.js +24 -0
- package/src/utils/credits.js +192 -0
- package/src/utils/dependencies.js +25 -0
- package/src/utils/messages.js +27 -0
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Simplified Express.js project generator
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import path from 'path';
|
|
7
|
+
import { logger } from '../../core/logger.js';
|
|
8
|
+
import { generateCreditsSection } from '../../utils/credits.js';
|
|
9
|
+
|
|
10
|
+
export async function generateSimpleExpressProject(projectPath, answers) {
|
|
11
|
+
try {
|
|
12
|
+
// Create project directory first
|
|
13
|
+
logger.debug('Creating project directory...');
|
|
14
|
+
await fs.ensureDir(projectPath);
|
|
15
|
+
logger.debug('Project directory created');
|
|
16
|
+
|
|
17
|
+
logger.debug('Generating package.json...');
|
|
18
|
+
const packageJson = {
|
|
19
|
+
name: answers.projectName,
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
description: `A production-ready Express.js application - ${answers.projectName}`,
|
|
22
|
+
main: 'src/server.js',
|
|
23
|
+
type: 'module',
|
|
24
|
+
scripts: {
|
|
25
|
+
start: 'node src/server.js',
|
|
26
|
+
dev: 'nodemon src/server.js',
|
|
27
|
+
test: 'jest'
|
|
28
|
+
},
|
|
29
|
+
keywords: ['express', 'nodejs', 'api', 'backend'],
|
|
30
|
+
author: 'Generated by build-app-with',
|
|
31
|
+
license: 'MIT',
|
|
32
|
+
engines: {
|
|
33
|
+
node: '>=18.0.0'
|
|
34
|
+
},
|
|
35
|
+
dependencies: {
|
|
36
|
+
'express': '^4.18.2',
|
|
37
|
+
'cors': '^2.8.5',
|
|
38
|
+
'helmet': '^7.1.0',
|
|
39
|
+
'morgan': '^1.10.0',
|
|
40
|
+
'dotenv': '^16.3.1',
|
|
41
|
+
'mongoose': '^8.0.3',
|
|
42
|
+
'jsonwebtoken': '^9.0.2',
|
|
43
|
+
'bcryptjs': '^2.4.3'
|
|
44
|
+
},
|
|
45
|
+
devDependencies: {
|
|
46
|
+
'nodemon': '^3.0.2',
|
|
47
|
+
'jest': '^29.7.0',
|
|
48
|
+
'supertest': '^6.3.3'
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
await fs.writeJSON(path.join(projectPath, 'package.json'), packageJson, { spaces: 2 });
|
|
53
|
+
|
|
54
|
+
logger.info('Creating src directory...');
|
|
55
|
+
await fs.ensureDir(path.join(projectPath, 'src'));
|
|
56
|
+
|
|
57
|
+
logger.info('Generating app.js...');
|
|
58
|
+
const appJs = `import express from 'express';
|
|
59
|
+
import cors from 'cors';
|
|
60
|
+
import helmet from 'helmet';
|
|
61
|
+
import morgan from 'morgan';
|
|
62
|
+
import dotenv from 'dotenv';
|
|
63
|
+
|
|
64
|
+
// Load environment variables
|
|
65
|
+
dotenv.config();
|
|
66
|
+
|
|
67
|
+
const app = express();
|
|
68
|
+
|
|
69
|
+
// Security middleware
|
|
70
|
+
app.use(helmet());
|
|
71
|
+
|
|
72
|
+
// CORS configuration
|
|
73
|
+
app.use(cors({
|
|
74
|
+
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
|
75
|
+
credentials: true
|
|
76
|
+
}));
|
|
77
|
+
|
|
78
|
+
// Logging middleware
|
|
79
|
+
app.use(morgan('combined'));
|
|
80
|
+
|
|
81
|
+
// Body parsing middleware
|
|
82
|
+
app.use(express.json({ limit: '10mb' }));
|
|
83
|
+
app.use(express.urlencoded({ extended: true }));
|
|
84
|
+
|
|
85
|
+
// Health check endpoint
|
|
86
|
+
app.get('/health', (req, res) => {
|
|
87
|
+
res.status(200).json({
|
|
88
|
+
status: 'OK',
|
|
89
|
+
timestamp: new Date().toISOString(),
|
|
90
|
+
uptime: process.uptime(),
|
|
91
|
+
environment: process.env.NODE_ENV || 'development'
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// API routes
|
|
96
|
+
app.get('/api', (req, res) => {
|
|
97
|
+
res.json({
|
|
98
|
+
message: 'Welcome to ${answers.projectName} API',
|
|
99
|
+
version: '1.0.0',
|
|
100
|
+
endpoints: {
|
|
101
|
+
health: '/health',
|
|
102
|
+
api: '/api'
|
|
103
|
+
}
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Error handling middleware
|
|
108
|
+
app.use((err, req, res, next) => {
|
|
109
|
+
console.error(err.stack);
|
|
110
|
+
res.status(500).json({
|
|
111
|
+
success: false,
|
|
112
|
+
error: 'Something went wrong!',
|
|
113
|
+
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
|
114
|
+
});
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
// 404 handler
|
|
118
|
+
app.use('*', (req, res) => {
|
|
119
|
+
res.status(404).json({
|
|
120
|
+
success: false,
|
|
121
|
+
error: \`Route \${req.originalUrl} not found\`
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export default app;
|
|
126
|
+
`;
|
|
127
|
+
|
|
128
|
+
await fs.writeFile(path.join(projectPath, 'src', 'app.js'), appJs);
|
|
129
|
+
|
|
130
|
+
logger.info('Generating server.js...');
|
|
131
|
+
const serverJs = `import app from './app.js';
|
|
132
|
+
|
|
133
|
+
const PORT = process.env.PORT || 3000;
|
|
134
|
+
const NODE_ENV = process.env.NODE_ENV || 'development';
|
|
135
|
+
|
|
136
|
+
app.listen(PORT, () => {
|
|
137
|
+
console.log(\`🚀 Server running on port \${PORT} in \${NODE_ENV} mode\`);
|
|
138
|
+
console.log(\`📊 Health check: http://localhost:\${PORT}/health\`);
|
|
139
|
+
console.log(\`🔗 API endpoint: http://localhost:\${PORT}/api\`);
|
|
140
|
+
});
|
|
141
|
+
`;
|
|
142
|
+
|
|
143
|
+
await fs.writeFile(path.join(projectPath, 'src', 'server.js'), serverJs);
|
|
144
|
+
|
|
145
|
+
logger.info('Generating .env.example...');
|
|
146
|
+
const envExample = `# Server Configuration
|
|
147
|
+
PORT=3000
|
|
148
|
+
NODE_ENV=development
|
|
149
|
+
|
|
150
|
+
# Database Configuration
|
|
151
|
+
MONGODB_URI=mongodb://localhost:27017/${answers.projectName}
|
|
152
|
+
|
|
153
|
+
# Authentication
|
|
154
|
+
JWT_SECRET=your-super-secret-jwt-key
|
|
155
|
+
JWT_EXPIRES_IN=7d
|
|
156
|
+
|
|
157
|
+
# CORS
|
|
158
|
+
CORS_ORIGIN=http://localhost:3000
|
|
159
|
+
`;
|
|
160
|
+
|
|
161
|
+
await fs.writeFile(path.join(projectPath, '.env.example'), envExample);
|
|
162
|
+
|
|
163
|
+
logger.info('Generating .gitignore...');
|
|
164
|
+
const gitignore = `# Dependencies
|
|
165
|
+
node_modules/
|
|
166
|
+
npm-debug.log*
|
|
167
|
+
yarn-debug.log*
|
|
168
|
+
yarn-error.log*
|
|
169
|
+
|
|
170
|
+
# Environment variables
|
|
171
|
+
.env
|
|
172
|
+
.env.local
|
|
173
|
+
.env.development.local
|
|
174
|
+
.env.test.local
|
|
175
|
+
.env.production.local
|
|
176
|
+
|
|
177
|
+
# Logs
|
|
178
|
+
logs
|
|
179
|
+
*.log
|
|
180
|
+
|
|
181
|
+
# Runtime data
|
|
182
|
+
pids
|
|
183
|
+
*.pid
|
|
184
|
+
*.seed
|
|
185
|
+
*.pid.lock
|
|
186
|
+
|
|
187
|
+
# Coverage directory
|
|
188
|
+
coverage/
|
|
189
|
+
|
|
190
|
+
# IDE
|
|
191
|
+
.vscode/
|
|
192
|
+
.idea/
|
|
193
|
+
|
|
194
|
+
# OS
|
|
195
|
+
.DS_Store
|
|
196
|
+
Thumbs.db
|
|
197
|
+
|
|
198
|
+
# Build output
|
|
199
|
+
dist/
|
|
200
|
+
build/
|
|
201
|
+
`;
|
|
202
|
+
|
|
203
|
+
await fs.writeFile(path.join(projectPath, '.gitignore'), gitignore);
|
|
204
|
+
|
|
205
|
+
logger.info('Generating README...');
|
|
206
|
+
|
|
207
|
+
// Generate credits asynchronously
|
|
208
|
+
const allFeatures = ['express', 'mongodb', 'jwt', 'cors', 'helmet', 'morgan'];
|
|
209
|
+
const credits = await Promise.resolve(generateCreditsSection('express', allFeatures));
|
|
210
|
+
|
|
211
|
+
const readme = `# ${answers.projectName}
|
|
212
|
+
|
|
213
|
+
A production-ready Express.js application built with modern best practices.
|
|
214
|
+
|
|
215
|
+
## 🚀 Features
|
|
216
|
+
|
|
217
|
+
- ⚡ **Express.js** - Fast, unopinionated web framework
|
|
218
|
+
- 🔒 **Security** - Helmet, CORS, JWT authentication
|
|
219
|
+
- 🗄️ **Database** - MongoDB with Mongoose
|
|
220
|
+
- 📝 **Logging** - Morgan HTTP request logger
|
|
221
|
+
- ✅ **Validation** - Request validation and sanitization
|
|
222
|
+
- 🧪 **Testing** - Jest and Supertest setup
|
|
223
|
+
|
|
224
|
+
## 🚀 Getting Started
|
|
225
|
+
|
|
226
|
+
1. **Install dependencies**
|
|
227
|
+
\`\`\`bash
|
|
228
|
+
npm install
|
|
229
|
+
\`\`\`
|
|
230
|
+
|
|
231
|
+
2. **Set up environment variables**
|
|
232
|
+
\`\`\`bash
|
|
233
|
+
cp .env.example .env
|
|
234
|
+
# Edit .env with your configuration
|
|
235
|
+
\`\`\`
|
|
236
|
+
|
|
237
|
+
3. **Start the development server**
|
|
238
|
+
\`\`\`bash
|
|
239
|
+
npm run dev
|
|
240
|
+
\`\`\`
|
|
241
|
+
|
|
242
|
+
The server will start on \`http://localhost:3000\`
|
|
243
|
+
|
|
244
|
+
## 📚 API Endpoints
|
|
245
|
+
|
|
246
|
+
- **Health Check**: \`GET /health\`
|
|
247
|
+
- **API Info**: \`GET /api\`
|
|
248
|
+
|
|
249
|
+
## 📝 Available Scripts
|
|
250
|
+
|
|
251
|
+
- \`npm start\` - Start production server
|
|
252
|
+
- \`npm run dev\` - Start development server with nodemon
|
|
253
|
+
- \`npm test\` - Run tests
|
|
254
|
+
|
|
255
|
+
## License
|
|
256
|
+
|
|
257
|
+
MIT
|
|
258
|
+
|
|
259
|
+
${credits}
|
|
260
|
+
`;
|
|
261
|
+
|
|
262
|
+
await fs.writeFile(path.join(projectPath, 'README.md'), readme);
|
|
263
|
+
|
|
264
|
+
logger.success('Project structure generated successfully!');
|
|
265
|
+
|
|
266
|
+
logger.info('Project structure created! Next steps:');
|
|
267
|
+
logger.info(` cd ${path.basename(projectPath)}`);
|
|
268
|
+
logger.info(' npm install');
|
|
269
|
+
logger.info(' npm run dev');
|
|
270
|
+
|
|
271
|
+
} catch (error) {
|
|
272
|
+
logger.error('Failed to generate project structure');
|
|
273
|
+
throw error;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate main Express app.js file
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { FEATURES, DATABASES } from '../../../types/index.js';
|
|
6
|
+
|
|
7
|
+
export function generateAppJs(projectPath, answers) {
|
|
8
|
+
const isTypeScript = answers.typescript !== false;
|
|
9
|
+
const ext = isTypeScript ? 'ts' : 'js';
|
|
10
|
+
|
|
11
|
+
let content = `import express from 'express';
|
|
12
|
+
import cors from 'cors';
|
|
13
|
+
import helmet from 'helmet';
|
|
14
|
+
import morgan from 'morgan';
|
|
15
|
+
import dotenv from 'dotenv';
|
|
16
|
+
import { errorHandler } from './middleware/errorHandler.js';
|
|
17
|
+
import { notFound } from './middleware/notFound.js';
|
|
18
|
+
import { logger } from './utils/logger.js';
|
|
19
|
+
|
|
20
|
+
// Load environment variables
|
|
21
|
+
dotenv.config();
|
|
22
|
+
|
|
23
|
+
const app = express();
|
|
24
|
+
|
|
25
|
+
// Security middleware
|
|
26
|
+
app.use(helmet());
|
|
27
|
+
|
|
28
|
+
// CORS configuration
|
|
29
|
+
app.use(cors({
|
|
30
|
+
origin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
|
31
|
+
credentials: true
|
|
32
|
+
}));
|
|
33
|
+
|
|
34
|
+
// Logging middleware
|
|
35
|
+
app.use(morgan('combined', {
|
|
36
|
+
stream: { write: (message) => logger.info(message.trim()) }
|
|
37
|
+
}));
|
|
38
|
+
|
|
39
|
+
// Body parsing middleware
|
|
40
|
+
app.use(express.json({ limit: '10mb' }));
|
|
41
|
+
app.use(express.urlencoded({ extended: true }));
|
|
42
|
+
|
|
43
|
+
// Health check endpoint
|
|
44
|
+
app.get('/health', (req, res) => {
|
|
45
|
+
res.status(200).json({
|
|
46
|
+
status: 'OK',
|
|
47
|
+
timestamp: new Date().toISOString(),
|
|
48
|
+
uptime: process.uptime(),
|
|
49
|
+
environment: process.env.NODE_ENV || 'development'
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// API routes
|
|
54
|
+
app.use('/api', (req, res) => {
|
|
55
|
+
res.json({
|
|
56
|
+
message: 'Welcome to ${answers.projectName} API',
|
|
57
|
+
version: '1.0.0',
|
|
58
|
+
endpoints: {
|
|
59
|
+
health: '/health',
|
|
60
|
+
api: '/api'
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Error handling middleware
|
|
66
|
+
app.use(notFound);
|
|
67
|
+
app.use(errorHandler);
|
|
68
|
+
|
|
69
|
+
export default app;
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
return content;
|
|
73
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Database configuration template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import { DATABASES } from '../../../types/index.js';
|
|
6
|
+
|
|
7
|
+
export function generateDatabaseConfig(answers) {
|
|
8
|
+
if (answers.database === DATABASES.MONGODB) {
|
|
9
|
+
return `import mongoose from 'mongoose';
|
|
10
|
+
import { logger } from '../utils/logger.js';
|
|
11
|
+
|
|
12
|
+
export async function connectDatabase() {
|
|
13
|
+
try {
|
|
14
|
+
const mongoUri = process.env.MONGODB_URI || 'mongodb://localhost:27017/${answers.projectName}';
|
|
15
|
+
|
|
16
|
+
await mongoose.connect(mongoUri, {
|
|
17
|
+
useNewUrlParser: true,
|
|
18
|
+
useUnifiedTopology: true,
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
logger.info('MongoDB connected successfully');
|
|
22
|
+
} catch (error) {
|
|
23
|
+
logger.error('MongoDB connection error:', error);
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// Handle connection events
|
|
29
|
+
mongoose.connection.on('connected', () => {
|
|
30
|
+
logger.info('Mongoose connected to MongoDB');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
mongoose.connection.on('error', (err) => {
|
|
34
|
+
logger.error('Mongoose connection error:', err);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
mongoose.connection.on('disconnected', () => {
|
|
38
|
+
logger.warn('Mongoose disconnected');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Graceful shutdown
|
|
42
|
+
process.on('SIGINT', async () => {
|
|
43
|
+
await mongoose.connection.close();
|
|
44
|
+
logger.info('Mongoose connection closed through app termination');
|
|
45
|
+
process.exit(0);
|
|
46
|
+
});
|
|
47
|
+
`;
|
|
48
|
+
} else if (answers.database === DATABASES.POSTGRESQL || answers.database === DATABASES.SQLITE) {
|
|
49
|
+
return `import { PrismaClient } from '@prisma/client';
|
|
50
|
+
import { logger } from '../utils/logger.js';
|
|
51
|
+
|
|
52
|
+
const prisma = new PrismaClient({
|
|
53
|
+
log: process.env.NODE_ENV === 'development' ? ['query', 'info', 'warn', 'error'] : ['error'],
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
export async function connectDatabase() {
|
|
57
|
+
try {
|
|
58
|
+
await prisma.$connect();
|
|
59
|
+
logger.info('Database connected successfully');
|
|
60
|
+
} catch (error) {
|
|
61
|
+
logger.error('Database connection error:', error);
|
|
62
|
+
process.exit(1);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Graceful shutdown
|
|
67
|
+
process.on('SIGINT', async () => {
|
|
68
|
+
await prisma.$disconnect();
|
|
69
|
+
logger.info('Database connection closed');
|
|
70
|
+
process.exit(0);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
export { prisma };
|
|
74
|
+
`;
|
|
75
|
+
} else if (answers.database === DATABASES.MYSQL) {
|
|
76
|
+
return `import { Sequelize } from 'sequelize';
|
|
77
|
+
import { logger } from '../utils/logger.js';
|
|
78
|
+
|
|
79
|
+
const sequelize = new Sequelize(
|
|
80
|
+
process.env.DB_NAME || '${answers.projectName}',
|
|
81
|
+
process.env.DB_USER || 'root',
|
|
82
|
+
process.env.DB_PASSWORD || 'password',
|
|
83
|
+
{
|
|
84
|
+
host: process.env.DB_HOST || 'localhost',
|
|
85
|
+
port: process.env.DB_PORT || 3306,
|
|
86
|
+
dialect: 'mysql',
|
|
87
|
+
logging: process.env.NODE_ENV === 'development' ? console.log : false,
|
|
88
|
+
}
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
export async function connectDatabase() {
|
|
92
|
+
try {
|
|
93
|
+
await sequelize.authenticate();
|
|
94
|
+
logger.info('MySQL connected successfully');
|
|
95
|
+
|
|
96
|
+
// Sync models in development
|
|
97
|
+
if (process.env.NODE_ENV === 'development') {
|
|
98
|
+
await sequelize.sync({ alter: true });
|
|
99
|
+
}
|
|
100
|
+
} catch (error) {
|
|
101
|
+
logger.error('MySQL connection error:', error);
|
|
102
|
+
process.exit(1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Graceful shutdown
|
|
107
|
+
process.on('SIGINT', async () => {
|
|
108
|
+
await sequelize.close();
|
|
109
|
+
logger.info('MySQL connection closed');
|
|
110
|
+
process.exit(0);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
export { sequelize };
|
|
114
|
+
`;
|
|
115
|
+
} else {
|
|
116
|
+
return `// No database configuration needed
|
|
117
|
+
export async function connectDatabase() {
|
|
118
|
+
console.log('No database configured');
|
|
119
|
+
}
|
|
120
|
+
`;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate config template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function generateConfig(projectPath, answers) {
|
|
6
|
+
const configContent = `import dotenv from 'dotenv';
|
|
7
|
+
|
|
8
|
+
// Load environment variables
|
|
9
|
+
dotenv.config();
|
|
10
|
+
|
|
11
|
+
export const config = {
|
|
12
|
+
port: process.env.PORT || 3000,
|
|
13
|
+
nodeEnv: process.env.NODE_ENV || 'development',
|
|
14
|
+
corsOrigin: process.env.CORS_ORIGIN || 'http://localhost:3000',
|
|
15
|
+
database: {
|
|
16
|
+
url: process.env.DATABASE_URL || process.env.MONGODB_URI,
|
|
17
|
+
host: process.env.DB_HOST || 'localhost',
|
|
18
|
+
port: process.env.DB_PORT || 3306,
|
|
19
|
+
name: process.env.DB_NAME || '${answers.projectName}',
|
|
20
|
+
user: process.env.DB_USER || 'root',
|
|
21
|
+
password: process.env.DB_PASSWORD || 'password'
|
|
22
|
+
},
|
|
23
|
+
auth: {
|
|
24
|
+
jwtSecret: process.env.JWT_SECRET || 'your-secret-key',
|
|
25
|
+
jwtExpiresIn: process.env.JWT_EXPIRES_IN || '7d'
|
|
26
|
+
},
|
|
27
|
+
rateLimit: {
|
|
28
|
+
max: parseInt(process.env.RATE_LIMIT_MAX) || 100,
|
|
29
|
+
timeWindow: parseInt(process.env.RATE_LIMIT_TIME_WINDOW) || 60000
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default config;
|
|
34
|
+
`;
|
|
35
|
+
|
|
36
|
+
return configContent;
|
|
37
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate controllers template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function generateControllers(projectPath, answers) {
|
|
6
|
+
const controllersContent = `import { logger } from '../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
export class BaseController {
|
|
9
|
+
static async handleRequest(req, res, next, handler) {
|
|
10
|
+
try {
|
|
11
|
+
const result = await handler(req, res);
|
|
12
|
+
res.json({
|
|
13
|
+
success: true,
|
|
14
|
+
data: result
|
|
15
|
+
});
|
|
16
|
+
} catch (error) {
|
|
17
|
+
logger.error('Controller error:', error);
|
|
18
|
+
next(error);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export class HealthController extends BaseController {
|
|
24
|
+
static async check(req, res) {
|
|
25
|
+
return {
|
|
26
|
+
status: 'OK',
|
|
27
|
+
timestamp: new Date().toISOString(),
|
|
28
|
+
uptime: process.uptime(),
|
|
29
|
+
service: '${answers.projectName}'
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class ApiController extends BaseController {
|
|
35
|
+
static async getInfo(req, res) {
|
|
36
|
+
return {
|
|
37
|
+
message: 'Welcome to ${answers.projectName} API',
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
endpoints: {
|
|
40
|
+
health: '/health',
|
|
41
|
+
api: '/api'
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
return controllersContent;
|
|
49
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generate Docker template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function generateDocker(projectPath, answers) {
|
|
6
|
+
const dockerfile = `# Use Node.js LTS version
|
|
7
|
+
FROM node:18-alpine
|
|
8
|
+
|
|
9
|
+
# Set working directory
|
|
10
|
+
WORKDIR /app
|
|
11
|
+
|
|
12
|
+
# Copy package files
|
|
13
|
+
COPY package*.json ./
|
|
14
|
+
|
|
15
|
+
# Install dependencies
|
|
16
|
+
RUN npm ci --only=production
|
|
17
|
+
|
|
18
|
+
# Copy source code
|
|
19
|
+
COPY . .
|
|
20
|
+
|
|
21
|
+
# Create non-root user
|
|
22
|
+
RUN addgroup -g 1001 -S nodejs
|
|
23
|
+
RUN adduser -S nextjs -u 1001
|
|
24
|
+
|
|
25
|
+
# Change ownership
|
|
26
|
+
RUN chown -R nextjs:nodejs /app
|
|
27
|
+
USER nextjs
|
|
28
|
+
|
|
29
|
+
# Expose port
|
|
30
|
+
EXPOSE 3000
|
|
31
|
+
|
|
32
|
+
# Health check
|
|
33
|
+
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \\
|
|
34
|
+
CMD curl -f http://localhost:3000/health || exit 1
|
|
35
|
+
|
|
36
|
+
# Start the application
|
|
37
|
+
CMD ["npm", "start"]
|
|
38
|
+
`;
|
|
39
|
+
|
|
40
|
+
const dockerCompose = `version: '3.8'
|
|
41
|
+
|
|
42
|
+
services:
|
|
43
|
+
app:
|
|
44
|
+
build: .
|
|
45
|
+
ports:
|
|
46
|
+
- "3000:3000"
|
|
47
|
+
environment:
|
|
48
|
+
- NODE_ENV=production
|
|
49
|
+
- PORT=3000
|
|
50
|
+
depends_on:
|
|
51
|
+
- db
|
|
52
|
+
restart: unless-stopped
|
|
53
|
+
|
|
54
|
+
db:
|
|
55
|
+
image: ${answers.database === 'mongodb' ? 'mongo:latest' : 'postgres:15-alpine'}
|
|
56
|
+
environment:
|
|
57
|
+
${answers.database === 'mongodb'
|
|
58
|
+
? 'MONGO_INITDB_DATABASE=myapp'
|
|
59
|
+
: 'POSTGRES_DB=myapp\n POSTGRES_USER=postgres\n POSTGRES_PASSWORD=password'
|
|
60
|
+
}
|
|
61
|
+
volumes:
|
|
62
|
+
- db_data:/data/db
|
|
63
|
+
ports:
|
|
64
|
+
- "${answers.database === 'mongodb' ? '27017:27017' : '5432:5432'}"
|
|
65
|
+
restart: unless-stopped
|
|
66
|
+
|
|
67
|
+
volumes:
|
|
68
|
+
db_data:
|
|
69
|
+
`;
|
|
70
|
+
|
|
71
|
+
return { dockerfile, dockerCompose };
|
|
72
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling middleware template
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export function generateErrorHandler() {
|
|
6
|
+
return `import { logger } from '../utils/logger.js';
|
|
7
|
+
|
|
8
|
+
export const errorHandler = (err, req, res, next) => {
|
|
9
|
+
let error = { ...err };
|
|
10
|
+
error.message = err.message;
|
|
11
|
+
|
|
12
|
+
// Log error
|
|
13
|
+
logger.error(err);
|
|
14
|
+
|
|
15
|
+
// Mongoose bad ObjectId
|
|
16
|
+
if (err.name === 'CastError') {
|
|
17
|
+
const message = 'Resource not found';
|
|
18
|
+
error = { message, statusCode: 404 };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Mongoose duplicate key
|
|
22
|
+
if (err.code === 11000) {
|
|
23
|
+
const message = 'Duplicate field value entered';
|
|
24
|
+
error = { message, statusCode: 400 };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Mongoose validation error
|
|
28
|
+
if (err.name === 'ValidationError') {
|
|
29
|
+
const message = Object.values(err.errors).map(val => val.message).join(', ');
|
|
30
|
+
error = { message, statusCode: 400 };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
res.status(error.statusCode || 500).json({
|
|
34
|
+
success: false,
|
|
35
|
+
error: error.message || 'Server Error',
|
|
36
|
+
...(process.env.NODE_ENV === 'development' && { stack: err.stack })
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
`;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function generateNotFound() {
|
|
43
|
+
return `export const notFound = (req, res, next) => {
|
|
44
|
+
const error = new Error(\`Not found - \${req.originalUrl}\`);
|
|
45
|
+
res.status(404);
|
|
46
|
+
next(error);
|
|
47
|
+
};
|
|
48
|
+
`;
|
|
49
|
+
}
|