create-xpress-backend 1.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.
@@ -0,0 +1,519 @@
1
+ 'use strict';
2
+
3
+ const { buildSharedFiles, buildReadme } = require('./shared');
4
+
5
+ function buildJsTemplates({ projectName, port, includeFileUpload, includeAuth }) {
6
+ const files = {};
7
+
8
+ // ─── package.json ─────────────────────────────────────────────────────────
9
+ const deps = {
10
+ '@prisma/adapter-pg': '^7.3.0',
11
+ '@prisma/client': '^7.3.0',
12
+ '@prisma/config': '^7.3.0',
13
+ bcryptjs: '^3.0.2',
14
+ cors: '^2.8.5',
15
+ dotenv: '^17.2.3',
16
+ express: '^4.21.2',
17
+ helmet: '^8.1.0',
18
+ joi: '^18.0.1',
19
+ jsonwebtoken: '^9.0.2',
20
+ morgan: '^1.10.1',
21
+ pg: '^8.18.0',
22
+ };
23
+ if (includeFileUpload) deps['multer'] = '^2.0.2';
24
+
25
+ files['package.json'] = JSON.stringify({
26
+ name: projectName,
27
+ version: '1.0.0',
28
+ description: '',
29
+ main: 'src/server.js',
30
+ scripts: {
31
+ dev: 'nodemon src/server.js',
32
+ start: 'node src/server.js',
33
+ 'db:generate': 'prisma generate',
34
+ 'db:push': 'prisma db push',
35
+ 'db:reset': 'prisma db push --force-reset',
36
+ 'db:seed': 'node src/database/seed.js',
37
+ 'db:studio': 'prisma studio',
38
+ 'db:dev': 'prisma db push && prisma generate',
39
+ },
40
+ keywords: [],
41
+ author: {
42
+ name: 'ayushsolanki29',
43
+ url: 'https://github.com/ayushsolanki29',
44
+ },
45
+ license: 'MIT',
46
+ type: 'commonjs',
47
+ homepage: 'https://github.com/ayushsolanki29/create-xpress-backend#readme',
48
+ repository: {
49
+ type: 'git',
50
+ url: 'https://github.com/ayushsolanki29/create-xpress-backend',
51
+ },
52
+ bugs: {
53
+ url: 'https://github.com/ayushsolanki29/create-xpress-backend/issues',
54
+ },
55
+ _createdWith: 'create-xpress-backend',
56
+ _scaffold: 'https://github.com/ayushsolanki29/create-xpress-backend',
57
+ dependencies: deps,
58
+ devDependencies: {
59
+ nodemon: '^3.1.10',
60
+ prisma: '^7.3.0',
61
+ },
62
+ }, null, 2);
63
+
64
+ // ─── shared files (env, gitignore, prisma schema, prisma.config, seed) ────
65
+ Object.assign(files, buildSharedFiles({ projectName, port, includeFileUpload, includeAuth, language: 'js' }));
66
+
67
+ // ─── src/database/prisma.js ───────────────────────────────────────────────
68
+ files['src/database/prisma.js'] = `// src/database/prisma.js
69
+ require('dotenv').config();
70
+ const { PrismaClient } = require('@prisma/client');
71
+ const { PrismaPg } = require('@prisma/adapter-pg');
72
+ const { Pool } = require('pg');
73
+
74
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
75
+ const adapter = new PrismaPg(pool);
76
+
77
+ const prisma = new PrismaClient({ adapter, log: ['error', 'warn'] });
78
+
79
+ module.exports = prisma;
80
+ `;
81
+
82
+ // ─── src/middleware/validate.js ───────────────────────────────────────────
83
+ files['src/middleware/validate.js'] = `// src/middleware/validate.js
84
+ const validate = (schema) => (req, res, next) => {
85
+ const { error } = schema.validate(req.body);
86
+ if (error) {
87
+ return res.status(400).json({ success: false, message: error.details[0].message });
88
+ }
89
+ next();
90
+ };
91
+
92
+ module.exports = validate;
93
+ `;
94
+
95
+ // ─── src/middleware/upload.js (optional) ──────────────────────────────────
96
+ if (includeFileUpload) {
97
+ files['src/middleware/upload.js'] = `// src/middleware/upload.js
98
+ const multer = require('multer');
99
+ const path = require('path');
100
+ const fs = require('fs');
101
+
102
+ const uploadDir = path.join(process.cwd(), 'uploads');
103
+ if (!fs.existsSync(uploadDir)) fs.mkdirSync(uploadDir, { recursive: true });
104
+
105
+ const storage = multer.diskStorage({
106
+ destination: (_req, _file, cb) => cb(null, uploadDir),
107
+ filename: (_req, file, cb) => {
108
+ const unique = Date.now() + '-' + Math.round(Math.random() * 1e9);
109
+ cb(null, file.fieldname + '-' + unique + path.extname(file.originalname));
110
+ },
111
+ });
112
+
113
+ const fileFilter = (_req, file, cb) => {
114
+ const allowed = [
115
+ 'image/jpeg', 'image/png', 'image/gif',
116
+ 'application/pdf', 'text/plain',
117
+ 'application/msword',
118
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
119
+ ];
120
+ allowed.includes(file.mimetype) ? cb(null, true) : cb(new Error('Invalid file type'), false);
121
+ };
122
+
123
+ const upload = multer({
124
+ storage,
125
+ limits: { fileSize: (parseInt(process.env.MAX_FILE_SIZE) || 10) * 1024 * 1024 },
126
+ fileFilter,
127
+ });
128
+
129
+ module.exports = upload;
130
+ `;
131
+ }
132
+
133
+ // ─── src/middleware/auth.js (optional) ────────────────────────────────────
134
+ if (includeAuth) {
135
+ files['src/middleware/auth.js'] = `// src/middleware/auth.js
136
+ const jwt = require('jsonwebtoken');
137
+
138
+ const authMiddleware = (req, res, next) => {
139
+ const header = req.headers.authorization;
140
+ if (!header || !header.startsWith('Bearer ')) {
141
+ return res.status(401).json({ success: false, message: 'No token provided' });
142
+ }
143
+ const token = header.split(' ')[1];
144
+ try {
145
+ req.user = jwt.verify(token, process.env.JWT_SECRET);
146
+ next();
147
+ } catch {
148
+ res.status(401).json({ success: false, message: 'Invalid or expired token' });
149
+ }
150
+ };
151
+
152
+ module.exports = authMiddleware;
153
+ `;
154
+ }
155
+
156
+ // ─── src/app.js ───────────────────────────────────────────────────────────
157
+ files['src/app.js'] = `const express = require('express');
158
+ const cors = require('cors');
159
+ const helmet = require('helmet');
160
+ const morgan = require('morgan');
161
+ const path = require('path');
162
+
163
+ const app = express();
164
+
165
+ const CLIENT_URL = process.env.CLIENT_URL || '*';
166
+
167
+ app.set('trust proxy', 1);
168
+ app.use(helmet());
169
+ app.use(cors({ origin: CLIENT_URL, credentials: true }));
170
+ app.use(morgan('dev'));
171
+ app.use(express.json({ limit: '10mb' }));
172
+ app.use(express.urlencoded({ extended: true }));
173
+ ${includeFileUpload ? `\napp.use('/uploads', express.static(path.join(__dirname, '../uploads')));\n` : ''}
174
+ app.use('/api', require('./modules'));
175
+
176
+ app.get('/health', (_req, res) => {
177
+ res.status(200).json({
178
+ status: 'OK',
179
+ message: 'API Running ✅',
180
+ timestamp: new Date().toISOString(),
181
+ uptime: process.uptime(),
182
+ });
183
+ });
184
+
185
+ app.use((_req, res) => {
186
+ res.status(404).json({ success: false, message: 'Route not found' });
187
+ });
188
+
189
+ app.use((error, _req, res, _next) => {
190
+ console.error('🔥 Error:', error);
191
+ res.status(error.status || 500).json({
192
+ success: false,
193
+ message: error.message || 'Internal server error',
194
+ });
195
+ });
196
+
197
+ module.exports = app;
198
+ `;
199
+
200
+ // ─── src/server.js ────────────────────────────────────────────────────────
201
+ files['src/server.js'] = `require('dotenv').config();
202
+ const app = require('./app');
203
+ const http = require('http');
204
+
205
+ const PORT = process.env.PORT || ${port};
206
+ const CLIENT_URL = process.env.CLIENT_URL || 'http://localhost:3000';
207
+ const SERVER_URL = process.env.SERVER_URL || \`http://localhost:\${PORT}\`;
208
+
209
+ const server = http.createServer(app);
210
+
211
+ server.listen(PORT, () => {
212
+ console.log('\\n========================================');
213
+ console.log(' ${projectName} backend online');
214
+ console.log(' Server :', SERVER_URL);
215
+ console.log(' Client :', CLIENT_URL);
216
+ console.log(' Health :', \`\${SERVER_URL}/health\`);
217
+ console.log(' Started :', new Date().toLocaleString());
218
+ console.log('========================================\\n');
219
+ });
220
+
221
+ const shutdown = () => {
222
+ console.log('\\n⚠️ Shutting down...');
223
+ server.close(() => { console.log('✅ Server closed.'); process.exit(0); });
224
+ setTimeout(() => { console.log('⏳ Forced exit.'); process.exit(1); }, 5000);
225
+ };
226
+
227
+ process.on('SIGINT', shutdown);
228
+ process.on('SIGTERM', shutdown);
229
+
230
+ module.exports = server;
231
+ `;
232
+
233
+ // ─── src/modules/index.js ─────────────────────────────────────────────────
234
+ files['src/modules/index.js'] = `const express = require('express');
235
+ const router = express.Router();
236
+
237
+ const demoRoutes = require('./demo/demo.routes');
238
+ ${includeAuth ? `const authRoutes = require('./auth/auth.routes');\n` : ''}
239
+ router.use('/demo', demoRoutes);
240
+ ${includeAuth ? `router.use('/auth', authRoutes);\n` : ''}
241
+ module.exports = router;
242
+ `;
243
+
244
+ // ─── demo module ──────────────────────────────────────────────────────────
245
+ files['src/modules/demo/demo.validation.js'] = `// src/modules/demo/demo.validation.js
246
+ const Joi = require('joi');
247
+
248
+ const createDemoValidation = Joi.object({
249
+ name: Joi.string().min(3).max(100).required(),
250
+ email: Joi.string().email().required(),
251
+ description: Joi.string().max(500).optional(),
252
+ });
253
+
254
+ const updateDemoValidation = Joi.object({
255
+ name: Joi.string().min(3).max(100).optional(),
256
+ email: Joi.string().email().optional(),
257
+ description: Joi.string().max(500).optional(),
258
+ });
259
+
260
+ module.exports = { createDemoValidation, updateDemoValidation };
261
+ `;
262
+
263
+ files['src/modules/demo/demo.service.js'] = `// src/modules/demo/demo.service.js
264
+ const prisma = require('../../database/prisma');
265
+ ${includeFileUpload ? `const fs = require('fs');\n` : ''}
266
+ class DemoService {
267
+ async createDemo(data) {
268
+ return prisma.user.create({ data: { email: data.email, name: data.name } });
269
+ }
270
+
271
+ async getAllDemos() {
272
+ return prisma.user.findMany({
273
+ ${includeFileUpload ? `include: { files: true },` : ''}
274
+ orderBy: { createdAt: 'desc' },
275
+ });
276
+ }
277
+
278
+ async getDemoById(id) {
279
+ const demo = await prisma.user.findUnique({
280
+ where: { id },
281
+ ${includeFileUpload ? `include: { files: true },` : ''}
282
+ });
283
+ if (!demo) throw new Error('Demo not found');
284
+ return demo;
285
+ }
286
+
287
+ async updateDemo(id, data) {
288
+ return prisma.user.update({ where: { id }, data: { name: data.name, email: data.email } });
289
+ }
290
+
291
+ async deleteDemo(id) {
292
+ await prisma.user.delete({ where: { id } });
293
+ return { message: 'Demo deleted successfully' };
294
+ }
295
+ ${includeFileUpload ? `
296
+ async uploadFile(userId, file) {
297
+ try {
298
+ return await prisma.file.create({
299
+ data: {
300
+ filename: file.filename,
301
+ originalName: file.originalname,
302
+ path: file.path,
303
+ size: file.size,
304
+ mimetype: file.mimetype,
305
+ userId,
306
+ },
307
+ });
308
+ } catch (error) {
309
+ if (file.path && fs.existsSync(file.path)) fs.unlinkSync(file.path);
310
+ throw new Error(\`Failed to upload file: \${error.message}\`);
311
+ }
312
+ }
313
+
314
+ async getUserFiles(userId) {
315
+ return prisma.file.findMany({ where: { userId }, orderBy: { createdAt: 'desc' } });
316
+ }
317
+
318
+ async deleteFile(fileId, userId) {
319
+ const file = await prisma.file.findFirst({ where: { id: fileId, userId } });
320
+ if (!file) throw new Error('File not found');
321
+ if (fs.existsSync(file.path)) fs.unlinkSync(file.path);
322
+ await prisma.file.delete({ where: { id: fileId } });
323
+ return { message: 'File deleted successfully' };
324
+ }
325
+ ` : ''}
326
+ }
327
+
328
+ module.exports = new DemoService();
329
+ `;
330
+
331
+ files['src/modules/demo/demo.controller.js'] = `// src/modules/demo/demo.controller.js
332
+ const demoService = require('./demo.service');
333
+
334
+ class DemoController {
335
+ async createDemo(req, res, next) {
336
+ try {
337
+ const result = await demoService.createDemo(req.body);
338
+ res.status(201).json({ success: true, message: 'Demo created successfully', data: result });
339
+ } catch (error) { next(error); }
340
+ }
341
+
342
+ async getAllDemos(req, res, next) {
343
+ try {
344
+ const result = await demoService.getAllDemos();
345
+ res.status(200).json({ success: true, data: result });
346
+ } catch (error) { next(error); }
347
+ }
348
+
349
+ async getDemoById(req, res, next) {
350
+ try {
351
+ const result = await demoService.getDemoById(req.params.id);
352
+ res.status(200).json({ success: true, data: result });
353
+ } catch (error) { next(error); }
354
+ }
355
+
356
+ async updateDemo(req, res, next) {
357
+ try {
358
+ const result = await demoService.updateDemo(req.params.id, req.body);
359
+ res.status(200).json({ success: true, message: 'Demo updated successfully', data: result });
360
+ } catch (error) { next(error); }
361
+ }
362
+
363
+ async deleteDemo(req, res, next) {
364
+ try {
365
+ const result = await demoService.deleteDemo(req.params.id);
366
+ res.status(200).json({ success: true, message: result.message });
367
+ } catch (error) { next(error); }
368
+ }
369
+ ${includeFileUpload ? `
370
+ async uploadFile(req, res, next) {
371
+ try {
372
+ if (!req.file) return res.status(400).json({ success: false, message: 'No file uploaded' });
373
+ const userId = req.user?.id || 'demo-user-id';
374
+ const result = await demoService.uploadFile(userId, req.file);
375
+ res.status(201).json({ success: true, message: 'File uploaded successfully', data: result });
376
+ } catch (error) { next(error); }
377
+ }
378
+
379
+ async getUserFiles(req, res, next) {
380
+ try {
381
+ const userId = req.user?.id || 'demo-user-id';
382
+ const result = await demoService.getUserFiles(userId);
383
+ res.status(200).json({ success: true, data: result });
384
+ } catch (error) { next(error); }
385
+ }
386
+
387
+ async deleteFile(req, res, next) {
388
+ try {
389
+ const userId = req.user?.id || 'demo-user-id';
390
+ const result = await demoService.deleteFile(req.params.fileId, userId);
391
+ res.status(200).json({ success: true, message: result.message });
392
+ } catch (error) { next(error); }
393
+ }
394
+ ` : ''}
395
+ }
396
+
397
+ module.exports = new DemoController();
398
+ `;
399
+
400
+ files['src/modules/demo/demo.routes.js'] = `// src/modules/demo/demo.routes.js
401
+ const express = require('express');
402
+ const router = express.Router();
403
+ const demoController = require('./demo.controller');
404
+ const demoValidation = require('./demo.validation');
405
+ const validate = require('../../middleware/validate');
406
+ ${includeFileUpload ? `const upload = require('../../middleware/upload');\n` : ''}
407
+ router.post('/', validate(demoValidation.createDemoValidation), demoController.createDemo);
408
+ router.get('/', demoController.getAllDemos);
409
+ router.get('/:id', demoController.getDemoById);
410
+ router.put('/:id', validate(demoValidation.updateDemoValidation), demoController.updateDemo);
411
+ router.delete('/:id', demoController.deleteDemo);
412
+ ${includeFileUpload ? `
413
+ router.post('/upload', upload.single('file'), demoController.uploadFile);
414
+ router.get('/files/all', demoController.getUserFiles);
415
+ router.delete('/files/:fileId', demoController.deleteFile);
416
+ ` : ''}
417
+ module.exports = router;
418
+ `;
419
+
420
+ // ─── auth module (optional) ───────────────────────────────────────────────
421
+ if (includeAuth) {
422
+ files['src/modules/auth/auth.validation.js'] = `// src/modules/auth/auth.validation.js
423
+ const Joi = require('joi');
424
+
425
+ const registerValidation = Joi.object({
426
+ name: Joi.string().min(2).max(100).required(),
427
+ email: Joi.string().email().required(),
428
+ password: Joi.string().min(6).required(),
429
+ });
430
+
431
+ const loginValidation = Joi.object({
432
+ email: Joi.string().email().required(),
433
+ password: Joi.string().required(),
434
+ });
435
+
436
+ module.exports = { registerValidation, loginValidation };
437
+ `;
438
+
439
+ files['src/modules/auth/auth.service.js'] = `// src/modules/auth/auth.service.js
440
+ const bcrypt = require('bcryptjs');
441
+ const jwt = require('jsonwebtoken');
442
+ const prisma = require('../../database/prisma');
443
+
444
+ class AuthService {
445
+ async register(data) {
446
+ const existing = await prisma.user.findUnique({ where: { email: data.email } });
447
+ if (existing) throw Object.assign(new Error('Email already registered'), { status: 409 });
448
+
449
+ const hashed = await bcrypt.hash(data.password, 10);
450
+ const user = await prisma.user.create({
451
+ data: { name: data.name, email: data.email, password: hashed },
452
+ });
453
+ const { password: _, ...safe } = user;
454
+ return safe;
455
+ }
456
+
457
+ async login(data) {
458
+ const user = await prisma.user.findUnique({ where: { email: data.email } });
459
+ if (!user) throw Object.assign(new Error('Invalid credentials'), { status: 401 });
460
+
461
+ const valid = await bcrypt.compare(data.password, user.password);
462
+ if (!valid) throw Object.assign(new Error('Invalid credentials'), { status: 401 });
463
+
464
+ const token = jwt.sign(
465
+ { id: user.id, email: user.email },
466
+ process.env.JWT_SECRET,
467
+ { expiresIn: process.env.JWT_EXPIRES_IN || '7d' }
468
+ );
469
+ const { password: _, ...safe } = user;
470
+ return { user: safe, token };
471
+ }
472
+ }
473
+
474
+ module.exports = new AuthService();
475
+ `;
476
+
477
+ files['src/modules/auth/auth.controller.js'] = `// src/modules/auth/auth.controller.js
478
+ const authService = require('./auth.service');
479
+
480
+ class AuthController {
481
+ async register(req, res, next) {
482
+ try {
483
+ const result = await authService.register(req.body);
484
+ res.status(201).json({ success: true, message: 'Registered successfully', data: result });
485
+ } catch (error) { next(error); }
486
+ }
487
+
488
+ async login(req, res, next) {
489
+ try {
490
+ const result = await authService.login(req.body);
491
+ res.status(200).json({ success: true, message: 'Login successful', data: result });
492
+ } catch (error) { next(error); }
493
+ }
494
+ }
495
+
496
+ module.exports = new AuthController();
497
+ `;
498
+
499
+ files['src/modules/auth/auth.routes.js'] = `// src/modules/auth/auth.routes.js
500
+ const express = require('express');
501
+ const router = express.Router();
502
+ const authController = require('./auth.controller');
503
+ const authValidation = require('./auth.validation');
504
+ const validate = require('../../middleware/validate');
505
+
506
+ router.post('/register', validate(authValidation.registerValidation), authController.register);
507
+ router.post('/login', validate(authValidation.loginValidation), authController.login);
508
+
509
+ module.exports = router;
510
+ `;
511
+ }
512
+
513
+ // ─── README ───────────────────────────────────────────────────────────────
514
+ files['README.md'] = buildReadme({ projectName, port, includeFileUpload, includeAuth, language: 'js' });
515
+
516
+ return files;
517
+ }
518
+
519
+ module.exports = { buildJsTemplates };