create-flex-stack 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,745 @@
1
+ export function getLayeredFiles(options) {
2
+ const files = {};
3
+ const userIdExpr = options.orm === 'prisma' ? 'user.id' : 'user._id.toString()';
4
+ // 1. src/config/env.ts
5
+ if (options.validation === 'zod') {
6
+ files['src/config/env.ts'] = `import dotenv from 'dotenv';
7
+ import { z } from 'zod';
8
+
9
+ dotenv.config();
10
+
11
+ const envSchema = z.object({
12
+ PORT: z.coerce.number().default(3000),
13
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
14
+ DATABASE_URL: z.string().min(1, 'DATABASE_URL is required'),
15
+ ${options.auth ? `JWT_SECRET: z.string().min(8, 'JWT_SECRET must be at least 8 characters'),
16
+ JWT_REFRESH_SECRET: z.string().min(8, 'JWT_REFRESH_SECRET must be at least 8 characters'),
17
+ JWT_EXPIRES_IN: z.string().default('15m'),
18
+ JWT_REFRESH_EXPIRES_IN: z.string().default('7d'),` : ''}
19
+ ${options.caching ? `REDIS_URL: z.string().min(1, 'REDIS_URL is required'),` : ''}
20
+ });
21
+
22
+ const parsed = envSchema.safeParse(process.env);
23
+
24
+ if (!parsed.success) {
25
+ console.error('❌ Invalid environment variables:', parsed.error.format());
26
+ process.exit(1);
27
+ }
28
+
29
+ export const env = parsed.data;
30
+ `;
31
+ }
32
+ else {
33
+ // Joi
34
+ files['src/config/env.ts'] = `import dotenv from 'dotenv';
35
+ import Joi from 'joi';
36
+
37
+ dotenv.config();
38
+
39
+ const envSchema = Joi.object({
40
+ PORT: Joi.number().default(3000),
41
+ NODE_ENV: Joi.string().valid('development', 'production', 'test').default('development'),
42
+ DATABASE_URL: Joi.string().required(),
43
+ ${options.auth ? `JWT_SECRET: Joi.string().min(8).required(),
44
+ JWT_REFRESH_SECRET: Joi.string().min(8).required(),
45
+ JWT_EXPIRES_IN: Joi.string().default('15m'),
46
+ JWT_REFRESH_EXPIRES_IN: Joi.string().default('7d'),` : ''}
47
+ ${options.caching ? `REDIS_URL: Joi.string().required(),` : ''}
48
+ }).unknown().required();
49
+
50
+ const { error, value } = envSchema.validate(process.env);
51
+
52
+ if (error) {
53
+ console.error('❌ Invalid environment variables:', error.message);
54
+ process.exit(1);
55
+ }
56
+
57
+ export const env = {
58
+ PORT: Number(value.PORT),
59
+ NODE_ENV: value.NODE_ENV,
60
+ DATABASE_URL: value.DATABASE_URL,
61
+ ${options.auth ? `JWT_SECRET: value.JWT_SECRET,
62
+ JWT_REFRESH_SECRET: value.JWT_REFRESH_SECRET,
63
+ JWT_EXPIRES_IN: value.JWT_EXPIRES_IN,
64
+ JWT_REFRESH_EXPIRES_IN: value.JWT_REFRESH_EXPIRES_IN,` : ''}
65
+ ${options.caching ? `REDIS_URL: value.REDIS_URL,` : ''}
66
+ };
67
+ `;
68
+ }
69
+ // 2. src/middlewares/errorHandler.ts
70
+ files['src/middlewares/errorHandler.ts'] = `import { Request, Response, NextFunction } from 'express';
71
+
72
+ export class AppError extends Error {
73
+ constructor(
74
+ public statusCode: number,
75
+ message: string,
76
+ public isOperational = true
77
+ ) {
78
+ super(message);
79
+ Object.setPrototypeOf(this, new.target.prototype);
80
+ Error.captureStackTrace(this, this.constructor);
81
+ }
82
+ }
83
+
84
+ export const errorHandler = (
85
+ err: Error | AppError,
86
+ req: Request,
87
+ res: Response,
88
+ next: NextFunction
89
+ ) => {
90
+ const statusCode = err instanceof AppError ? err.statusCode : 500;
91
+ const message = err.message || 'Internal Server Error';
92
+
93
+ res.status(statusCode).json({
94
+ status: 'error',
95
+ statusCode,
96
+ message,
97
+ ...(process.env.NODE_ENV === 'development' && { stack: err.stack }),
98
+ });
99
+ };
100
+ `;
101
+ // 3. Auth and RBAC Middlewares (conditional)
102
+ if (options.auth) {
103
+ files['src/middlewares/auth.ts'] = `import { Request, Response, NextFunction } from 'express';
104
+ import jwt from 'jsonwebtoken';
105
+ import { env } from '../config/env.js';
106
+ import { AppError } from './errorHandler.js';
107
+
108
+ export interface AuthenticatedRequest extends Request {
109
+ user?: {
110
+ id: string;
111
+ email: string;
112
+ role: string;
113
+ };
114
+ }
115
+
116
+ export const requireAuth = (
117
+ req: AuthenticatedRequest,
118
+ res: Response,
119
+ next: NextFunction
120
+ ) => {
121
+ const authHeader = req.headers.authorization;
122
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
123
+ return next(new AppError(401, 'Authentication token missing or invalid'));
124
+ }
125
+
126
+ const token = authHeader.split(' ')[1];
127
+
128
+ try {
129
+ const decoded = jwt.verify(token, env.JWT_SECRET) as {
130
+ id: string;
131
+ email: string;
132
+ role: string;
133
+ };
134
+
135
+ req.user = decoded;
136
+ next();
137
+ } catch (error) {
138
+ next(new AppError(401, 'Authentication token expired or corrupt'));
139
+ }
140
+ };
141
+ `;
142
+ if (options.rbac) {
143
+ files['src/middlewares/rbac.ts'] = `import { Response, NextFunction } from 'express';
144
+ import { AuthenticatedRequest } from './auth.js';
145
+ import { AppError } from './errorHandler.js';
146
+
147
+ export const authorize = (...roles: string[]) => {
148
+ return (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
149
+ if (!req.user) {
150
+ return next(new AppError(401, 'User not authenticated'));
151
+ }
152
+
153
+ if (!roles.includes(req.user.role)) {
154
+ return next(new AppError(403, 'Permission denied: insufficient privileges'));
155
+ }
156
+
157
+ next();
158
+ };
159
+ };
160
+ `;
161
+ }
162
+ }
163
+ // 4. Rate Limiting Middleware (conditional)
164
+ if (options.rateLimiting) {
165
+ files['src/middlewares/rateLimiter.ts'] = `import rateLimit from 'express-rate-limit';
166
+
167
+ export const apiLimiter = rateLimit({
168
+ windowMs: 15 * 60 * 1000, // 15 minutes
169
+ max: 100, // Limit each IP to 100 requests per windowMs
170
+ standardHeaders: true, // Return rate limit info in the \`RateLimit-*\` headers
171
+ legacyHeaders: false, // Disable the \`X-RateLimit-*\` headers
172
+ message: {
173
+ status: 'error',
174
+ statusCode: 429,
175
+ message: 'Too many requests from this IP, please try again after 15 minutes',
176
+ },
177
+ });
178
+ `;
179
+ }
180
+ // 5. Schemas (Zod or Joi)
181
+ if (options.validation === 'zod') {
182
+ files['src/schemas/user.schema.ts'] = `import { z } from 'zod';
183
+
184
+ export const registerSchema = z.object({
185
+ body: z.object({
186
+ email: z.string().email('Invalid email address'),
187
+ name: z.string().min(2, 'Name must be at least 2 characters').optional(),
188
+ password: z.string().min(6, 'Password must be at least 6 characters'),
189
+ role: z.enum(['user', 'admin']).optional(),
190
+ }),
191
+ });
192
+
193
+ export const loginSchema = z.object({
194
+ body: z.object({
195
+ email: z.string().email('Invalid email address'),
196
+ password: z.string().min(1, 'Password is required'),
197
+ }),
198
+ });
199
+
200
+ export const refreshSchema = z.object({
201
+ body: z.object({
202
+ refreshToken: z.string().min(1, 'Refresh token is required'),
203
+ }),
204
+ });
205
+ `;
206
+ }
207
+ else {
208
+ // Joi
209
+ files['src/schemas/user.schema.ts'] = `import Joi from 'joi';
210
+
211
+ export const registerSchema = Joi.object({
212
+ body: Joi.object({
213
+ email: Joi.string().email().required(),
214
+ name: Joi.string().min(2).optional(),
215
+ password: Joi.string().min(6).required(),
216
+ role: Joi.string().valid('user', 'admin').default('user'),
217
+ }).required(),
218
+ }).unknown(true);
219
+
220
+ export const loginSchema = Joi.object({
221
+ body: Joi.object({
222
+ email: Joi.string().email().required(),
223
+ password: Joi.string().required(),
224
+ }).required(),
225
+ }).unknown(true);
226
+
227
+ export const refreshSchema = Joi.object({
228
+ body: Joi.object({
229
+ refreshToken: Joi.string().required(),
230
+ }).required(),
231
+ }).unknown(true);
232
+ `;
233
+ }
234
+ // Helper validation middleware
235
+ files['src/middlewares/validate.ts'] = options.validation === 'zod'
236
+ ? `import { Request, Response, NextFunction } from 'express';
237
+ import { AnyZodObject, ZodError } from 'zod';
238
+
239
+ export const validate = (schema: AnyZodObject) => {
240
+ return async (req: Request, res: Response, next: NextFunction) => {
241
+ try {
242
+ const parsed = await schema.parseAsync({
243
+ body: req.body,
244
+ query: req.query,
245
+ params: req.params,
246
+ });
247
+ req.body = parsed.body;
248
+ req.query = parsed.query;
249
+ req.params = parsed.params;
250
+ next();
251
+ } catch (error) {
252
+ if (error instanceof ZodError) {
253
+ res.status(400).json({
254
+ status: 'error',
255
+ statusCode: 400,
256
+ message: 'Validation failed',
257
+ errors: error.errors.map(err => ({
258
+ field: err.path.join('.'),
259
+ message: err.message,
260
+ })),
261
+ });
262
+ } else {
263
+ next(error);
264
+ }
265
+ }
266
+ };
267
+ };
268
+ `
269
+ : `import { Request, Response, NextFunction } from 'express';
270
+ import { Schema } from 'joi';
271
+
272
+ export const validate = (schema: Schema) => {
273
+ return async (req: Request, res: Response, next: NextFunction) => {
274
+ try {
275
+ const { error, value } = schema.validate({
276
+ body: req.body,
277
+ query: req.query,
278
+ params: req.params,
279
+ }, { abortEarly: false });
280
+
281
+ if (error) {
282
+ return res.status(400).json({
283
+ status: 'error',
284
+ statusCode: 400,
285
+ message: 'Validation failed',
286
+ errors: error.details.map(err => ({
287
+ field: err.path.join('.'),
288
+ message: err.message,
289
+ })),
290
+ });
291
+ }
292
+ req.body = value.body;
293
+ req.query = value.query;
294
+ req.params = value.params;
295
+ next();
296
+ } catch (err) {
297
+ next(err);
298
+ }
299
+ };
300
+ };
301
+ `;
302
+ // 6. DB Client / User Model
303
+ if (options.orm === 'mongoose') {
304
+ files['src/models/user.model.ts'] = `import mongoose, { Schema } from 'mongoose';
305
+
306
+ const UserSchema = new Schema({
307
+ email: { type: String, required: true, unique: true },
308
+ name: { type: String },
309
+ password: { type: String, required: true },
310
+ role: { type: String, default: 'user', enum: ['user', 'admin'] },
311
+ refreshToken: { type: String },
312
+ }, { timestamps: true });
313
+
314
+ export const UserModel = mongoose.model('User', UserSchema);
315
+ `;
316
+ }
317
+ // 7. Repositories
318
+ if (options.orm === 'prisma') {
319
+ files['src/repositories/user.repository.ts'] = `import { PrismaClient } from '@prisma/client';
320
+
321
+ const prisma = new PrismaClient();
322
+
323
+ export class UserRepository {
324
+ async findByEmail(email: string) {
325
+ return prisma.user.findUnique({ where: { email } });
326
+ }
327
+
328
+ async findById(id: string) {
329
+ return prisma.user.findUnique({ where: { id } });
330
+ }
331
+
332
+ async create(data: any) {
333
+ return prisma.user.create({ data });
334
+ }
335
+
336
+ async update(id: string, data: any) {
337
+ return prisma.user.update({
338
+ where: { id },
339
+ data,
340
+ });
341
+ }
342
+
343
+ async findAll() {
344
+ return prisma.user.findMany({
345
+ select: {
346
+ id: true,
347
+ email: true,
348
+ name: true,
349
+ role: true,
350
+ createdAt: true,
351
+ }
352
+ });
353
+ }
354
+ }
355
+ `;
356
+ }
357
+ else {
358
+ // Mongoose Repository
359
+ files['src/repositories/user.repository.ts'] = `import { UserModel } from '../models/user.model.js';
360
+
361
+ export class UserRepository {
362
+ async findByEmail(email: string) {
363
+ return UserModel.findOne({ email });
364
+ }
365
+
366
+ async findById(id: string) {
367
+ return UserModel.findById(id);
368
+ }
369
+
370
+ async create(data: any) {
371
+ return UserModel.create(data);
372
+ }
373
+
374
+ async update(id: string, data: any) {
375
+ return UserModel.findByIdAndUpdate(id, data, { new: true });
376
+ }
377
+
378
+ async findAll() {
379
+ return UserModel.find({}, 'id email name role createdAt');
380
+ }
381
+ }
382
+ `;
383
+ }
384
+ // 8. Services
385
+ files['src/services/user.service.ts'] = `import bcrypt from 'bcryptjs';
386
+ import jwt from 'jsonwebtoken';
387
+ import { UserRepository } from '../repositories/user.repository.js';
388
+ import { env } from '../config/env.js';
389
+ import { AppError } from '../middlewares/errorHandler.js';
390
+
391
+ const userRepository = new UserRepository();
392
+
393
+ export class UserService {
394
+ private generateTokens(payload: { id: string; email: string; role: string }) {
395
+ const accessToken = jwt.sign(payload, env.JWT_SECRET, {
396
+ expiresIn: env.JWT_EXPIRES_IN as any,
397
+ });
398
+ const refreshToken = jwt.sign(payload, env.JWT_REFRESH_SECRET, {
399
+ expiresIn: env.JWT_REFRESH_EXPIRES_IN as any,
400
+ });
401
+ return { accessToken, refreshToken };
402
+ }
403
+
404
+ async register(data: any) {
405
+ const existing = await userRepository.findByEmail(data.email);
406
+ if (existing) {
407
+ throw new AppError(400, 'Email already registered');
408
+ }
409
+
410
+ const hashedPassword = await bcrypt.hash(data.password, 10);
411
+ const user = await userRepository.create({
412
+ ...data,
413
+ password: hashedPassword,
414
+ });
415
+
416
+ const tokens = this.generateTokens({
417
+ id: ${userIdExpr},
418
+ email: user.email,
419
+ role: user.role,
420
+ });
421
+
422
+ await userRepository.update(${userIdExpr}, {
423
+ refreshToken: tokens.refreshToken,
424
+ });
425
+
426
+ return {
427
+ user: {
428
+ id: ${userIdExpr},
429
+ email: user.email,
430
+ name: user.name,
431
+ role: user.role,
432
+ },
433
+ ...tokens,
434
+ };
435
+ }
436
+
437
+ async login(data: any) {
438
+ const user = await userRepository.findByEmail(data.email);
439
+ if (!user) {
440
+ throw new AppError(401, 'Invalid email or password');
441
+ }
442
+
443
+ const isMatch = await bcrypt.compare(data.password, user.password);
444
+ if (!isMatch) {
445
+ throw new AppError(401, 'Invalid email or password');
446
+ }
447
+
448
+ const tokens = this.generateTokens({
449
+ id: ${userIdExpr},
450
+ email: user.email,
451
+ role: user.role,
452
+ });
453
+
454
+ await userRepository.update(${userIdExpr}, {
455
+ refreshToken: tokens.refreshToken,
456
+ });
457
+
458
+ return {
459
+ user: {
460
+ id: ${userIdExpr},
461
+ email: user.email,
462
+ name: user.name,
463
+ role: user.role,
464
+ },
465
+ ...tokens,
466
+ };
467
+ }
468
+
469
+ async refresh(token: string) {
470
+ try {
471
+ const decoded = jwt.verify(token, env.JWT_REFRESH_SECRET) as any;
472
+ const user = await userRepository.findById(decoded.id);
473
+
474
+ if (!user || user.refreshToken !== token) {
475
+ throw new AppError(401, 'Invalid or revoked refresh token');
476
+ }
477
+
478
+ const tokens = this.generateTokens({
479
+ id: ${userIdExpr},
480
+ email: user.email,
481
+ role: user.role,
482
+ });
483
+
484
+ await userRepository.update(${userIdExpr}, {
485
+ refreshToken: tokens.refreshToken,
486
+ });
487
+
488
+ return tokens;
489
+ } catch (err) {
490
+ throw new AppError(401, 'Invalid or expired refresh token');
491
+ }
492
+ }
493
+
494
+ async getProfile(id: string) {
495
+ const user = await userRepository.findById(id);
496
+ if (!user) {
497
+ throw new AppError(404, 'User not found');
498
+ }
499
+ return {
500
+ id: ${userIdExpr},
501
+ email: user.email,
502
+ name: user.name,
503
+ role: user.role,
504
+ createdAt: user.createdAt,
505
+ };
506
+ }
507
+
508
+ async getAllUsers() {
509
+ return userRepository.findAll();
510
+ }
511
+ }
512
+ `;
513
+ // 9. Controllers
514
+ files['src/controllers/user.controller.ts'] = `import { Request, Response, NextFunction } from 'express';
515
+ import { UserService } from '../services/user.service.js';
516
+ import { AuthenticatedRequest } from '../middlewares/auth.js';
517
+
518
+ const userService = new UserService();
519
+
520
+ export class UserController {
521
+ async register(req: Request, res: Response, next: NextFunction) {
522
+ try {
523
+ const result = await userService.register(req.body);
524
+ res.status(201).json({
525
+ status: 'success',
526
+ data: result,
527
+ });
528
+ } catch (err) {
529
+ next(err);
530
+ }
531
+ }
532
+
533
+ async login(req: Request, res: Response, next: NextFunction) {
534
+ try {
535
+ const result = await userService.login(req.body);
536
+ res.status(200).json({
537
+ status: 'success',
538
+ data: result,
539
+ });
540
+ } catch (err) {
541
+ next(err);
542
+ }
543
+ }
544
+
545
+ async refresh(req: Request, res: Response, next: NextFunction) {
546
+ try {
547
+ const result = await userService.refresh(req.body.refreshToken);
548
+ res.status(200).json({
549
+ status: 'success',
550
+ data: result,
551
+ });
552
+ } catch (err) {
553
+ next(err);
554
+ }
555
+ }
556
+
557
+ async profile(req: AuthenticatedRequest, res: Response, next: NextFunction) {
558
+ try {
559
+ const user = await userService.getProfile(req.user!.id);
560
+ res.status(200).json({
561
+ status: 'success',
562
+ data: { user },
563
+ });
564
+ } catch (err) {
565
+ next(err);
566
+ }
567
+ }
568
+
569
+ async list(req: Request, res: Response, next: NextFunction) {
570
+ try {
571
+ const users = await userService.getAllUsers();
572
+ res.status(200).json({
573
+ status: 'success',
574
+ data: { users },
575
+ });
576
+ } catch (err) {
577
+ next(err);
578
+ }
579
+ }
580
+ }
581
+ `;
582
+ // 10. Routes
583
+ const rbacImport = options.rbac ? ", authorize" : "";
584
+ files['src/routes/user.routes.ts'] = `import { Router } from 'express';
585
+ import { UserController } from '../controllers/user.controller.js';
586
+ import { requireAuth } from '../middlewares/auth.js';
587
+ ${options.rbac ? `import { authorize } from '../middlewares/rbac.js';` : ''}
588
+ import { validate } from '../middlewares/validate.js';
589
+ import { registerSchema, loginSchema, refreshSchema } from '../schemas/user.schema.js';
590
+
591
+ const router = Router();
592
+ const controller = new UserController();
593
+
594
+ router.post('/register', validate(registerSchema), controller.register);
595
+ router.post('/login', validate(loginSchema), controller.login);
596
+ router.post('/refresh', validate(refreshSchema), controller.refresh);
597
+ router.get('/profile', requireAuth, controller.profile);
598
+ router.get('/', requireAuth${options.rbac ? ', authorize(\'admin\')' : ''}, controller.list);
599
+
600
+ export default router;
601
+ `;
602
+ files['src/routes/index.ts'] = `import { Router } from 'express';
603
+ import userRoutes from './user.routes.js';
604
+
605
+ const router = Router();
606
+
607
+ router.use('/users', userRoutes);
608
+
609
+ export default router;
610
+ `;
611
+ // 11. Express App / Server
612
+ let appMiddlewaresImport = '';
613
+ let appMiddlewares = '';
614
+ if (options.rateLimiting) {
615
+ appMiddlewaresImport += `import { apiLimiter } from './middlewares/rateLimiter.js';\n`;
616
+ appMiddlewares += `app.use('/api', apiLimiter);\n`;
617
+ }
618
+ if (options.swagger) {
619
+ appMiddlewaresImport += `import swaggerUi from 'swagger-ui-express';\n`;
620
+ appMiddlewares += `
621
+ // Basic Swagger specs
622
+ const swaggerDocument = {
623
+ openapi: "3.0.0",
624
+ info: {
625
+ title: "${options.projectName} API",
626
+ version: "1.0.0",
627
+ description: "API Documentation"
628
+ },
629
+ servers: [{ url: "http://localhost:3000/api" }],
630
+ paths: {
631
+ "/users/register": {
632
+ post: {
633
+ summary: "Register a user",
634
+ requestBody: {
635
+ required: true,
636
+ content: {
637
+ "application/json": {
638
+ schema: {
639
+ type: "object",
640
+ properties: {
641
+ email: { type: "string" },
642
+ password: { type: "string" },
643
+ name: { type: "string" }
644
+ },
645
+ required: ["email", "password"]
646
+ }
647
+ }
648
+ }
649
+ },
650
+ responses: { "201": { description: "User registered" } }
651
+ }
652
+ },
653
+ "/users/login": {
654
+ post: {
655
+ summary: "Login",
656
+ requestBody: {
657
+ required: true,
658
+ content: {
659
+ "application/json": {
660
+ schema: {
661
+ type: "object",
662
+ properties: {
663
+ email: { type: "string" },
664
+ password: { type: "string" }
665
+ },
666
+ required: ["email", "password"]
667
+ }
668
+ }
669
+ }
670
+ },
671
+ responses: { "200": { description: "Successful login" } }
672
+ }
673
+ }
674
+ }
675
+ };
676
+ app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
677
+ `;
678
+ }
679
+ files['src/app.ts'] = `import express from 'express';
680
+ import cors from 'cors';
681
+ import helmet from 'helmet';
682
+ import routes from './routes/index.js';
683
+ import { errorHandler } from './middlewares/errorHandler.js';
684
+ ${appMiddlewaresImport}
685
+ const app = express();
686
+
687
+ app.use(helmet());
688
+ app.use(cors());
689
+ app.use(express.json());
690
+
691
+ ${appMiddlewares}
692
+ app.use('/api', routes);
693
+
694
+ app.use(errorHandler);
695
+
696
+ export default app;
697
+ `;
698
+ let dbConnectionCode = '';
699
+ if (options.orm === 'mongoose') {
700
+ dbConnectionCode = `import mongoose from 'mongoose';
701
+
702
+ async function connectDB() {
703
+ try {
704
+ await mongoose.connect(env.DATABASE_URL);
705
+ console.log('🔌 Connected to MongoDB database successfully.');
706
+ } catch (err) {
707
+ console.error('❌ Failed to connect to MongoDB:', err);
708
+ process.exit(1);
709
+ }
710
+ }`;
711
+ }
712
+ else {
713
+ // Prisma
714
+ dbConnectionCode = `import { PrismaClient } from '@prisma/client';
715
+ const prisma = new PrismaClient();
716
+
717
+ async function connectDB() {
718
+ try {
719
+ await prisma.$connect();
720
+ console.log('🔌 Connected to Database successfully (Prisma).');
721
+ } catch (err) {
722
+ console.error('❌ Failed to connect to Database via Prisma:', err);
723
+ process.exit(1);
724
+ }
725
+ }`;
726
+ }
727
+ files['src/server.ts'] = `import app from './app.js';
728
+ import { env } from './config/env.js';
729
+ ${dbConnectionCode}
730
+
731
+ const PORT = env.PORT || 3000;
732
+
733
+ async function start() {
734
+ await connectDB();
735
+ app.listen(PORT, () => {
736
+ console.log(\`🚀 Server running in \${env.NODE_ENV} mode on port \${PORT}\`);
737
+ console.log(\`🔗 Healthcheck URL: http://localhost:\${PORT}/api/users/profile (needs auth)\`);
738
+ ${options.swagger ? `console.log(\`📄 Swagger Docs: http://localhost:\${PORT}/docs\`);` : ''}
739
+ });
740
+ }
741
+
742
+ start();
743
+ `;
744
+ return files;
745
+ }