backend-scaffold-cli 1.1.0 → 1.3.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/bin/cli.js +258 -153
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
const { program } = require('commander');
|
|
@@ -8,133 +8,156 @@ const fs = require('fs-extra');
|
|
|
8
8
|
const path = require('path');
|
|
9
9
|
const { execSync } = require('child_process');
|
|
10
10
|
|
|
11
|
-
const version= '1.0.0';
|
|
11
|
+
const version = '1.0.0';
|
|
12
12
|
|
|
13
13
|
program
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
const projectPath = path.resolve(process.cwd(), projectName);
|
|
49
|
-
|
|
50
|
-
if (fs.existsSync(projectPath)) {
|
|
51
|
-
console.log(chalk.red(`Error: Directory ${projectName} already exists.`));
|
|
52
|
-
process.exit(1);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
console.log(chalk.blue(`Creating project in ${projectPath}\n`));
|
|
56
|
-
fs.ensureDirSync(projectPath);
|
|
57
|
-
|
|
58
|
-
createFolderStructure(projectPath);
|
|
59
|
-
createConfigFiles(projectPath, answers);
|
|
60
|
-
createMiddlewareFiles(projectPath, answers);
|
|
61
|
-
createRouteFiles(projectPath, answers);
|
|
62
|
-
|
|
63
|
-
console.log(chalk.cyan(' Initializing git repository...'));
|
|
64
|
-
execSync('git init', { cwd: projectPath });
|
|
65
|
-
|
|
66
|
-
if (answers.installDeps) {
|
|
67
|
-
console.log(chalk.cyan(' Installing dependencies...\n'));
|
|
68
|
-
execSync('npm install', { cwd: projectPath, stdio: 'inherit' });
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
console.log(chalk.green.bold('\n Project created successfully!\n'));
|
|
72
|
-
console.log(chalk.cyan('Next steps:'));
|
|
73
|
-
console.log(chalk.white(` cd ${projectName}`));
|
|
74
|
-
console.log(chalk.white(' cp .env.example .env'));
|
|
75
|
-
console.log(chalk.white(' npm start\n'));
|
|
76
|
-
}catch (error) {
|
|
77
|
-
console.error(chalk.red('\n Error creating project:'), error.message);
|
|
78
|
-
process.exit(1);
|
|
14
|
+
.version(version)
|
|
15
|
+
.description('Create a new Express.js backend application')
|
|
16
|
+
.argument('<project-name>', 'Name of the project')
|
|
17
|
+
.action(async (projectName) => {
|
|
18
|
+
try {
|
|
19
|
+
console.log(chalk.blue(`Creating a new Express.js backend application: ${projectName}`));
|
|
20
|
+
|
|
21
|
+
const answers = await inquirer.prompt([
|
|
22
|
+
{
|
|
23
|
+
type: 'confirm',
|
|
24
|
+
name: 'useAuth',
|
|
25
|
+
message: 'Include JWT authentication?',
|
|
26
|
+
default: true
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
type: 'confirm',
|
|
30
|
+
name: 'useValidation',
|
|
31
|
+
message: 'Include input validation?',
|
|
32
|
+
default: true
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: 'confirm',
|
|
36
|
+
name: 'useRateLimit',
|
|
37
|
+
message: 'Include rate limiting?',
|
|
38
|
+
default: true
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
type: 'confirm',
|
|
42
|
+
name: 'installDeps',
|
|
43
|
+
message: 'Install dependencies now?',
|
|
44
|
+
default: true
|
|
79
45
|
}
|
|
80
|
-
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
const projectPath = path.resolve(process.cwd(), projectName);
|
|
55
|
+
|
|
56
|
+
if (fs.existsSync(projectPath)) {
|
|
57
|
+
console.log(chalk.red(`Error: Directory ${projectName} already exists.`));
|
|
58
|
+
process.exit(1);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log(chalk.blue(`Creating project in ${projectPath}\n`));
|
|
62
|
+
fs.ensureDirSync(projectPath);
|
|
63
|
+
|
|
64
|
+
createFolderStructure(projectPath);
|
|
65
|
+
createConfigFiles(projectPath, answers);
|
|
66
|
+
createMiddlewareFiles(projectPath, answers);
|
|
67
|
+
createRouteFiles(projectPath, answers);
|
|
68
|
+
if (answers.useAuth) {
|
|
69
|
+
createUserModel(projectPath);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(chalk.cyan(' Initializing git repository...'));
|
|
73
|
+
execSync('git init', { cwd: projectPath });
|
|
74
|
+
|
|
75
|
+
if (answers.installDeps) {
|
|
76
|
+
console.log(chalk.cyan(' Installing dependencies...\n'));
|
|
77
|
+
execSync('npm install', { cwd: projectPath, stdio: 'inherit' });
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
console.log(chalk.green.bold('\n Project created successfully!\n'));
|
|
81
|
+
console.log(chalk.cyan('Next steps:'));
|
|
82
|
+
console.log(chalk.white(` cd ${projectName}`));
|
|
83
|
+
console.log(chalk.white(' cp .env.example .env'));
|
|
84
|
+
console.log(chalk.white(' npm start\n'));
|
|
85
|
+
} catch (error) {
|
|
86
|
+
console.error(chalk.red('\n Error creating project:'), error.message);
|
|
87
|
+
process.exit(1);
|
|
88
|
+
}
|
|
89
|
+
});
|
|
81
90
|
|
|
82
91
|
program.parse();
|
|
83
92
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
const createFolderStructure = (projectPath) => {
|
|
98
|
+
const folders = [
|
|
99
|
+
'src/config',
|
|
100
|
+
'src/middleware',
|
|
101
|
+
'src/routes',
|
|
102
|
+
'src/controllers',
|
|
103
|
+
'src/models',
|
|
104
|
+
'src/utils',
|
|
105
|
+
'src/validation'
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
folders.forEach(folder => {
|
|
109
|
+
fs.ensureDirSync(path.join(projectPath, folder));
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
console.log(chalk.green(' Folder structure created'));
|
|
100
113
|
|
|
101
114
|
}
|
|
102
115
|
|
|
103
116
|
|
|
104
|
-
const createConfigFiles= (projectPath,answers) =>{
|
|
105
|
-
const packageJson={
|
|
106
|
-
name: path.basename(projectPath),
|
|
107
|
-
version: '1.0.0',
|
|
108
|
-
main: 'src/server.js',
|
|
109
|
-
scripts: {
|
|
110
|
-
start: 'node src/server.js',
|
|
111
|
-
dev: 'nodemon src/server.js',
|
|
112
|
-
test: 'jest'
|
|
113
|
-
},
|
|
114
|
-
keywords: ['express', 'nodejs', 'backend'],
|
|
115
|
-
author: '',
|
|
116
|
-
license: 'MIT',
|
|
117
|
-
dependencies: {
|
|
118
|
-
express: '^4.18.2',
|
|
119
|
-
mongoose: '^7.0.0',
|
|
120
|
-
dotenv: '^16.0.3',
|
|
121
|
-
cors: '^2.8.5',
|
|
122
|
-
'express-rate-limit': '^6.7.0',
|
|
123
|
-
...(answers.useAuth && { jsonwebtoken: '^9.0.0' }),
|
|
124
|
-
...(answers.useValidation && { joi: '^17.9.0' })
|
|
125
|
-
},
|
|
126
|
-
devDependencies: {
|
|
127
|
-
nodemon: '^2.0.22',
|
|
128
|
-
}
|
|
129
117
|
|
|
130
|
-
};
|
|
131
118
|
|
|
132
|
-
fs.writeFileSync(
|
|
133
|
-
path.join(projectPath, 'package.json'),
|
|
134
|
-
JSON.stringify(packageJson, null, 2)
|
|
135
|
-
);
|
|
136
119
|
|
|
137
|
-
|
|
120
|
+
|
|
121
|
+
const createConfigFiles = (projectPath, answers) => {
|
|
122
|
+
const packageJson = {
|
|
123
|
+
name: path.basename(projectPath),
|
|
124
|
+
version: '1.0.0',
|
|
125
|
+
main: 'src/server.js',
|
|
126
|
+
scripts: {
|
|
127
|
+
start: 'node src/server.js',
|
|
128
|
+
dev: 'nodemon src/server.js',
|
|
129
|
+
test: 'jest'
|
|
130
|
+
},
|
|
131
|
+
keywords: ['express', 'nodejs', 'backend'],
|
|
132
|
+
author: '',
|
|
133
|
+
license: 'MIT',
|
|
134
|
+
dependencies: {
|
|
135
|
+
express: '^4.18.2',
|
|
136
|
+
mongoose: '^7.0.0',
|
|
137
|
+
dotenv: '^16.0.3',
|
|
138
|
+
cors: '^2.8.5',
|
|
139
|
+
...(answers.useAuth && { jsonwebtoken: '^9.0.0' , bcryptjs: '^2.4.3'}),
|
|
140
|
+
...(answers.useValidation && { joi: '^17.9.0' }),
|
|
141
|
+
...(answers.useRateLimit && { 'express-rate-limit': '^6.7.0' }),
|
|
142
|
+
|
|
143
|
+
},
|
|
144
|
+
devDependencies: {
|
|
145
|
+
nodemon: '^2.0.22',
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
fs.writeFileSync(
|
|
151
|
+
path.join(projectPath, 'package.json'),
|
|
152
|
+
JSON.stringify(packageJson, null, 2)
|
|
153
|
+
);
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
const env = `# Server Configuration
|
|
138
161
|
PORT=5000
|
|
139
162
|
NODE_ENV=development
|
|
140
163
|
|
|
@@ -158,10 +181,12 @@ CORS_ORIGIN=http://localhost:3000
|
|
|
158
181
|
LOG_LEVEL=debug
|
|
159
182
|
`;
|
|
160
183
|
|
|
161
|
-
|
|
184
|
+
fs.writeFileSync(path.join(projectPath, '.env.example'), env);
|
|
185
|
+
|
|
162
186
|
|
|
163
187
|
|
|
164
|
-
|
|
188
|
+
|
|
189
|
+
const gitignore = `node_modules/
|
|
165
190
|
.env
|
|
166
191
|
.env.local
|
|
167
192
|
.env.*.local
|
|
@@ -175,9 +200,9 @@ yarn-debug.log*
|
|
|
175
200
|
.vscode/
|
|
176
201
|
`;
|
|
177
202
|
|
|
178
|
-
|
|
203
|
+
fs.writeFileSync(path.join(projectPath, '.gitignore'), gitignore);
|
|
179
204
|
|
|
180
|
-
|
|
205
|
+
console.log(chalk.green(' Config files created'));
|
|
181
206
|
|
|
182
207
|
|
|
183
208
|
};
|
|
@@ -185,6 +210,8 @@ yarn-debug.log*
|
|
|
185
210
|
|
|
186
211
|
|
|
187
212
|
|
|
213
|
+
|
|
214
|
+
|
|
188
215
|
function createMiddlewareFiles(projectPath, answers) {
|
|
189
216
|
const errorHandler = `// Middleware for handling errors
|
|
190
217
|
class ErrorHandler extends Error {
|
|
@@ -223,15 +250,16 @@ const errorMiddleware = (err, req, res, next) => {
|
|
|
223
250
|
module.exports = { errorMiddleware, ErrorHandler };
|
|
224
251
|
`;
|
|
225
252
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
253
|
+
fs.writeFileSync(
|
|
254
|
+
path.join(projectPath, 'src/middleware/errorHandler.js'),
|
|
255
|
+
errorHandler
|
|
256
|
+
);
|
|
230
257
|
|
|
231
258
|
|
|
232
259
|
|
|
233
260
|
|
|
234
|
-
|
|
261
|
+
|
|
262
|
+
const logger = `// Simple logging middleware
|
|
235
263
|
const loggerMiddleware = (req, res, next) => {
|
|
236
264
|
const start = Date.now();
|
|
237
265
|
|
|
@@ -248,19 +276,24 @@ const loggerMiddleware = (req, res, next) => {
|
|
|
248
276
|
module.exports = loggerMiddleware;
|
|
249
277
|
`;
|
|
250
278
|
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
279
|
+
fs.writeFileSync(
|
|
280
|
+
path.join(projectPath, 'src/middleware/logger.js'),
|
|
281
|
+
logger
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
|
|
286
|
+
|
|
255
287
|
|
|
256
288
|
|
|
257
289
|
|
|
258
|
-
|
|
290
|
+
if (answers.useRateLimit) {
|
|
291
|
+
const rateLimiter = `const ratelimit= require('express-rate-limit');
|
|
259
292
|
|
|
260
293
|
const limiter=ratelimit({
|
|
261
294
|
windowMs: 15 * 60 * 1000,
|
|
262
295
|
max: 100,
|
|
263
|
-
message: 'Too many requests from this IP, please try again after 15 minutes'
|
|
296
|
+
message: 'Too many requests from this IP, please try again after 15 minutes',
|
|
264
297
|
standardHeaders: true,
|
|
265
298
|
legacyHeaders: false,
|
|
266
299
|
});
|
|
@@ -271,13 +304,19 @@ module.exports=limiter;
|
|
|
271
304
|
`;
|
|
272
305
|
|
|
273
306
|
fs.writeFileSync(
|
|
274
|
-
|
|
275
|
-
|
|
307
|
+
path.join(projectPath, 'src/middleware/rateLimiter.js'),
|
|
308
|
+
rateLimiter
|
|
276
309
|
);
|
|
277
310
|
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
|
|
314
|
+
|
|
278
315
|
|
|
279
|
-
|
|
280
|
-
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
if (answers.useAuth) {
|
|
319
|
+
const auth = `const jwt = require('jsonwebtoken');
|
|
281
320
|
const { ErrorHandler } = require('./errorHandler');
|
|
282
321
|
|
|
283
322
|
const authenticateToken = (req, res, next) => {
|
|
@@ -300,15 +339,16 @@ module.exports = authenticateToken;
|
|
|
300
339
|
`;
|
|
301
340
|
|
|
302
341
|
fs.writeFileSync(
|
|
303
|
-
|
|
304
|
-
|
|
342
|
+
path.join(projectPath, 'src/middleware/auth.js'),
|
|
343
|
+
auth
|
|
305
344
|
);
|
|
306
345
|
}
|
|
307
346
|
|
|
308
347
|
|
|
309
348
|
|
|
310
|
-
|
|
311
|
-
|
|
349
|
+
|
|
350
|
+
if (answers.useValidation) {
|
|
351
|
+
const validation = `const Joi = require('joi');
|
|
312
352
|
|
|
313
353
|
const validateRequest = (schema) => {
|
|
314
354
|
return (req, res, next) => {
|
|
@@ -334,8 +374,8 @@ module.exports = validateRequest;
|
|
|
334
374
|
`;
|
|
335
375
|
|
|
336
376
|
fs.writeFileSync(
|
|
337
|
-
|
|
338
|
-
|
|
377
|
+
path.join(projectPath, 'src/middleware/validation.js'),
|
|
378
|
+
validation
|
|
339
379
|
);
|
|
340
380
|
}
|
|
341
381
|
|
|
@@ -365,14 +405,16 @@ router.get('/protected', authenticateToken, (req, res) => {
|
|
|
365
405
|
|
|
366
406
|
module.exports = router;
|
|
367
407
|
`;
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
408
|
+
fs.writeFileSync(
|
|
409
|
+
path.join(projectPath, 'src/routes/index.js'),
|
|
410
|
+
routes
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
|
|
372
414
|
|
|
373
|
-
|
|
374
415
|
|
|
375
|
-
|
|
416
|
+
|
|
417
|
+
const dbConfig = `const mongoose = require('mongoose');
|
|
376
418
|
const dns = require('dns');
|
|
377
419
|
|
|
378
420
|
// Set DNS servers
|
|
@@ -406,15 +448,16 @@ const connectDB = async () => {
|
|
|
406
448
|
module.exports = connectDB;
|
|
407
449
|
`;
|
|
408
450
|
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
451
|
+
fs.writeFileSync(
|
|
452
|
+
path.join(projectPath, 'src/config/db.js'),
|
|
453
|
+
dbConfig
|
|
454
|
+
);
|
|
455
|
+
|
|
413
456
|
|
|
414
457
|
|
|
415
458
|
|
|
416
459
|
|
|
417
|
-
const server = `require('dotenv').config();
|
|
460
|
+
const server = `require('dotenv').config();
|
|
418
461
|
const express = require('express');
|
|
419
462
|
const cors = require('cors');
|
|
420
463
|
const connectDB = require('./config/db');
|
|
@@ -423,7 +466,7 @@ const loggerMiddleware = require('./middleware/logger');
|
|
|
423
466
|
const { errorMiddleware } = require('./middleware/errorHandler');
|
|
424
467
|
|
|
425
468
|
const app = express();
|
|
426
|
-
const limiter = require('./middleware/rateLimiter');
|
|
469
|
+
${answers.useRateLimit ? "const limiter = require('./middleware/rateLimiter');" : ''}
|
|
427
470
|
const PORT = process.env.PORT || 5000;
|
|
428
471
|
|
|
429
472
|
// Connect to MongoDB
|
|
@@ -437,7 +480,7 @@ app.use(cors({
|
|
|
437
480
|
app.use(express.json());
|
|
438
481
|
app.use(express.urlencoded({ extended: true }));
|
|
439
482
|
app.use(loggerMiddleware);
|
|
440
|
-
app.use(limiter);
|
|
483
|
+
${answers.useRateLimit ? 'app.use(limiter);' : ''}
|
|
441
484
|
|
|
442
485
|
|
|
443
486
|
// Routes
|
|
@@ -473,3 +516,65 @@ process.on('unhandledRejection', (err) => {
|
|
|
473
516
|
|
|
474
517
|
console.log(chalk.green('Route and config files created'));
|
|
475
518
|
}
|
|
519
|
+
|
|
520
|
+
|
|
521
|
+
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
function createUserModel(projectPath) {
|
|
525
|
+
const userModel = `const mongoose = require('mongoose');
|
|
526
|
+
const bcrypt = require('bcryptjs');
|
|
527
|
+
|
|
528
|
+
const userSchema = new mongoose.Schema(
|
|
529
|
+
{
|
|
530
|
+
name:{
|
|
531
|
+
type: String,
|
|
532
|
+
required: [true, 'Name is required'],
|
|
533
|
+
trim: true,
|
|
534
|
+
},
|
|
535
|
+
email: {
|
|
536
|
+
type: String,
|
|
537
|
+
required: [true, 'Email is required'],
|
|
538
|
+
unique: true,
|
|
539
|
+
lowercase: true,
|
|
540
|
+
trim: true,
|
|
541
|
+
match: [/.+@.+\\..+/, 'Please fill a valid email address'],
|
|
542
|
+
},
|
|
543
|
+
password: {
|
|
544
|
+
type: String,
|
|
545
|
+
required: [true, 'Password is required'],
|
|
546
|
+
minlength: [6, 'Password must be at least 6 characters long'],
|
|
547
|
+
select: false
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
{ timestamps: true }
|
|
551
|
+
|
|
552
|
+
);
|
|
553
|
+
|
|
554
|
+
|
|
555
|
+
//hash password before saving
|
|
556
|
+
userSchema.pre('save', async function (next) {
|
|
557
|
+
if (!this.isModified('password')) {
|
|
558
|
+
return next();
|
|
559
|
+
}
|
|
560
|
+
this.password = await bcrypt.hash(this.password,10);
|
|
561
|
+
next();
|
|
562
|
+
});
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
//compare password method
|
|
566
|
+
userSchema.methods.comparePassword = async function(candidatePassword) {
|
|
567
|
+
return await bcrypt.compare(candidatePassword, this.password);
|
|
568
|
+
};
|
|
569
|
+
|
|
570
|
+
module.exports = mongoose.model('User', userSchema);
|
|
571
|
+
|
|
572
|
+
`;
|
|
573
|
+
|
|
574
|
+
fs.writeFileSync(
|
|
575
|
+
path.join(projectPath, 'src/models/User.js'),
|
|
576
|
+
userModel
|
|
577
|
+
)
|
|
578
|
+
|
|
579
|
+
console.log(chalk.green('User model created'));
|
|
580
|
+
}
|