create-coreback 1.0.1

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.
Files changed (96) hide show
  1. package/.github/PUBLISH.md +58 -0
  2. package/.github/workflows/publish.yml +78 -0
  3. package/LICENSE +21 -0
  4. package/README.md +62 -0
  5. package/dist/createProject.d.ts +2 -0
  6. package/dist/createProject.d.ts.map +1 -0
  7. package/dist/createProject.js +70 -0
  8. package/dist/createProject.js.map +1 -0
  9. package/dist/generators/cursorRules.d.ts +2 -0
  10. package/dist/generators/cursorRules.d.ts.map +1 -0
  11. package/dist/generators/cursorRules.js +35 -0
  12. package/dist/generators/cursorRules.js.map +1 -0
  13. package/dist/generators/docker.d.ts +3 -0
  14. package/dist/generators/docker.d.ts.map +1 -0
  15. package/dist/generators/docker.js +119 -0
  16. package/dist/generators/docker.js.map +1 -0
  17. package/dist/generators/envExample.d.ts +3 -0
  18. package/dist/generators/envExample.d.ts.map +1 -0
  19. package/dist/generators/envExample.js +39 -0
  20. package/dist/generators/envExample.js.map +1 -0
  21. package/dist/generators/eslint.d.ts +2 -0
  22. package/dist/generators/eslint.d.ts.map +1 -0
  23. package/dist/generators/eslint.js +32 -0
  24. package/dist/generators/eslint.js.map +1 -0
  25. package/dist/generators/githubActions.d.ts +3 -0
  26. package/dist/generators/githubActions.d.ts.map +1 -0
  27. package/dist/generators/githubActions.js +68 -0
  28. package/dist/generators/githubActions.js.map +1 -0
  29. package/dist/generators/gitignore.d.ts +2 -0
  30. package/dist/generators/gitignore.d.ts.map +1 -0
  31. package/dist/generators/gitignore.js +50 -0
  32. package/dist/generators/gitignore.js.map +1 -0
  33. package/dist/generators/index.d.ts +3 -0
  34. package/dist/generators/index.d.ts.map +1 -0
  35. package/dist/generators/index.js +34 -0
  36. package/dist/generators/index.js.map +1 -0
  37. package/dist/generators/jest.d.ts +2 -0
  38. package/dist/generators/jest.d.ts.map +1 -0
  39. package/dist/generators/jest.js +23 -0
  40. package/dist/generators/jest.js.map +1 -0
  41. package/dist/generators/packageJson.d.ts +3 -0
  42. package/dist/generators/packageJson.d.ts.map +1 -0
  43. package/dist/generators/packageJson.js +80 -0
  44. package/dist/generators/packageJson.js.map +1 -0
  45. package/dist/generators/prettier.d.ts +2 -0
  46. package/dist/generators/prettier.d.ts.map +1 -0
  47. package/dist/generators/prettier.js +14 -0
  48. package/dist/generators/prettier.js.map +1 -0
  49. package/dist/generators/prisma.d.ts +3 -0
  50. package/dist/generators/prisma.d.ts.map +1 -0
  51. package/dist/generators/prisma.js +55 -0
  52. package/dist/generators/prisma.js.map +1 -0
  53. package/dist/generators/sourceFiles.d.ts +3 -0
  54. package/dist/generators/sourceFiles.d.ts.map +1 -0
  55. package/dist/generators/sourceFiles.js +767 -0
  56. package/dist/generators/sourceFiles.js.map +1 -0
  57. package/dist/generators/tsconfig.d.ts +2 -0
  58. package/dist/generators/tsconfig.d.ts.map +1 -0
  59. package/dist/generators/tsconfig.js +32 -0
  60. package/dist/generators/tsconfig.js.map +1 -0
  61. package/dist/index.d.ts +3 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +42 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/prompts.d.ts +3 -0
  66. package/dist/prompts.d.ts.map +1 -0
  67. package/dist/prompts.js +56 -0
  68. package/dist/prompts.js.map +1 -0
  69. package/dist/types.d.ts +10 -0
  70. package/dist/types.d.ts.map +1 -0
  71. package/dist/types.js +2 -0
  72. package/dist/types.js.map +1 -0
  73. package/dist/utils/parseArgs.d.ts +2 -0
  74. package/dist/utils/parseArgs.d.ts.map +1 -0
  75. package/dist/utils/parseArgs.js +6 -0
  76. package/dist/utils/parseArgs.js.map +1 -0
  77. package/package.json +46 -0
  78. package/src/createProject.ts +86 -0
  79. package/src/generators/cursorRules.ts +39 -0
  80. package/src/generators/docker.ts +131 -0
  81. package/src/generators/envExample.ts +49 -0
  82. package/src/generators/eslint.ts +38 -0
  83. package/src/generators/githubActions.ts +79 -0
  84. package/src/generators/gitignore.ts +52 -0
  85. package/src/generators/index.ts +45 -0
  86. package/src/generators/jest.ts +29 -0
  87. package/src/generators/packageJson.ts +89 -0
  88. package/src/generators/prettier.ts +22 -0
  89. package/src/generators/prisma.ts +66 -0
  90. package/src/generators/sourceFiles.ts +840 -0
  91. package/src/generators/tsconfig.ts +34 -0
  92. package/src/index.ts +45 -0
  93. package/src/prompts.ts +62 -0
  94. package/src/types.ts +11 -0
  95. package/src/utils/parseArgs.ts +6 -0
  96. package/tsconfig.json +21 -0
@@ -0,0 +1,767 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ export async function generateSourceFiles(projectPath, config) {
4
+ const srcDir = path.join(projectPath, 'src');
5
+ const configDir = path.join(srcDir, 'config');
6
+ const controllersDir = path.join(srcDir, 'controllers');
7
+ const servicesDir = path.join(srcDir, 'services');
8
+ const repositoriesDir = path.join(srcDir, 'repositories');
9
+ const middlewaresDir = path.join(srcDir, 'middlewares');
10
+ const routesDir = path.join(srcDir, 'routes');
11
+ const typesDir = path.join(srcDir, 'types');
12
+ const utilsDir = path.join(srcDir, 'utils');
13
+ const validatorsDir = path.join(srcDir, 'validators');
14
+ const testsDir = path.join(projectPath, 'tests');
15
+ const unitTestsDir = path.join(testsDir, 'unit');
16
+ const integrationTestsDir = path.join(testsDir, 'integration');
17
+ // Create directories
18
+ await Promise.all([
19
+ fs.ensureDir(configDir),
20
+ fs.ensureDir(controllersDir),
21
+ fs.ensureDir(servicesDir),
22
+ fs.ensureDir(repositoriesDir),
23
+ fs.ensureDir(middlewaresDir),
24
+ fs.ensureDir(routesDir),
25
+ fs.ensureDir(typesDir),
26
+ fs.ensureDir(utilsDir),
27
+ fs.ensureDir(validatorsDir),
28
+ fs.ensureDir(unitTestsDir),
29
+ fs.ensureDir(integrationTestsDir),
30
+ ]);
31
+ // Generate files
32
+ await generateIndex(srcDir, config);
33
+ await generateConfigFiles(configDir, config);
34
+ await generateMiddlewares(middlewaresDir, config);
35
+ await generateUtils(utilsDir);
36
+ await generateRoutes(routesDir, config);
37
+ await generateTypes(typesDir);
38
+ await generateValidators(validatorsDir, config);
39
+ await generateControllers(controllersDir, config);
40
+ await generateServices(servicesDir, config);
41
+ await generateRepositories(repositoriesDir, config);
42
+ await generateTests(unitTestsDir, integrationTestsDir, config);
43
+ }
44
+ async function generateIndex(srcDir, config) {
45
+ const indexContent = `import express from 'express';
46
+ import { config } from './config/env.js';
47
+ import { setupMiddlewares } from './config/middlewares.js';
48
+ import { setupRoutes } from './routes/index.js';
49
+ import { setupSwagger } from './config/swagger.js';
50
+ import { logger } from './utils/logger.js';
51
+ import { errorHandler } from './middlewares/errorHandler.js';
52
+
53
+ const app = express();
54
+
55
+ setupMiddlewares(app);
56
+ setupSwagger(app);
57
+ setupRoutes(app);
58
+ app.use(errorHandler);
59
+
60
+ const server = app.listen(config.PORT, () => {
61
+ logger.info(\`🚀 Server: http://localhost:\${config.PORT}\`);
62
+ logger.info(\`📚 Docs: http://localhost:\${config.PORT}/api-docs\`);
63
+ });
64
+
65
+ process.on('SIGTERM', () => {
66
+ server.close();
67
+ process.exit(0);
68
+ });
69
+ `;
70
+ await fs.writeFile(path.join(srcDir, 'index.ts'), indexContent);
71
+ }
72
+ async function generateConfigFiles(configDir, config) {
73
+ // env.ts
74
+ const envContent = `import { z } from 'zod';
75
+ import dotenv from 'dotenv';
76
+
77
+ dotenv.config();
78
+
79
+ const envSchema = z.object({
80
+ NODE_ENV: z.enum(['development', 'production', 'test']).default('development'),
81
+ PORT: z.coerce.number().default(3000),
82
+ DATABASE_URL: z.string(),
83
+ ${config.includeAuth ? ` JWT_SECRET: z.string(),
84
+ JWT_EXPIRES_IN: z.string().default('7d'),` : ''}
85
+ RATE_LIMIT_WINDOW_MS: z.coerce.number().default(900000),
86
+ RATE_LIMIT_MAX: z.coerce.number().default(100),
87
+ });
88
+
89
+ export const config = envSchema.parse(process.env);
90
+ `;
91
+ await fs.writeFile(path.join(configDir, 'env.ts'), envContent);
92
+ // database.ts
93
+ const databaseContent = `import { PrismaClient } from '@prisma/client';
94
+ import { logger } from '../utils/logger.js';
95
+
96
+ const prisma = new PrismaClient({
97
+ log: [
98
+ { level: 'query', emit: 'event' },
99
+ { level: 'error', emit: 'stdout' },
100
+ { level: 'warn', emit: 'stdout' },
101
+ ],
102
+ });
103
+
104
+ prisma.$on('query', (e) => {
105
+ logger.debug('Query: ' + e.query);
106
+ logger.debug('Params: ' + e.params);
107
+ logger.debug('Duration: ' + e.duration + 'ms');
108
+ });
109
+
110
+ export { prisma };
111
+ `;
112
+ await fs.writeFile(path.join(configDir, 'database.ts'), databaseContent);
113
+ // middlewares.ts
114
+ const middlewaresContent = `import express, { Express } from 'express';
115
+ import cors from 'cors';
116
+ import helmet from 'helmet';
117
+ import compression from 'compression';
118
+ import rateLimit from 'express-rate-limit';
119
+ import { config } from './env.js';
120
+
121
+ export function setupMiddlewares(app: Express): void {
122
+ app.use(helmet());
123
+ app.use(cors());
124
+ app.use(compression());
125
+ app.use(express.json());
126
+ app.use(express.urlencoded({ extended: true }));
127
+
128
+ const limiter = rateLimit({
129
+ windowMs: config.RATE_LIMIT_WINDOW_MS,
130
+ max: config.RATE_LIMIT_MAX,
131
+ message: 'Too many requests from this IP, please try again later.',
132
+ });
133
+
134
+ app.use('/api/', limiter);
135
+ }
136
+ `;
137
+ await fs.writeFile(path.join(configDir, 'middlewares.ts'), middlewaresContent);
138
+ // swagger.ts
139
+ const swaggerContent = `import { Express } from 'express';
140
+ import swaggerJsdoc from 'swagger-jsdoc';
141
+ import swaggerUi from 'swagger-ui-express';
142
+ import { config } from './env.js';
143
+
144
+ const options: swaggerJsdoc.Options = {
145
+ definition: {
146
+ openapi: '3.0.0',
147
+ info: {
148
+ title: 'CoreBack API',
149
+ version: '1.0.0',
150
+ description: 'Production-ready backend API',
151
+ },
152
+ servers: [
153
+ {
154
+ url: \`http://localhost:\${config.PORT}\`,
155
+ description: 'Development server',
156
+ },
157
+ ],
158
+ },
159
+ apis: ['./src/routes/**/*.ts', './src/controllers/**/*.ts'],
160
+ };
161
+
162
+ const swaggerSpec = swaggerJsdoc(options);
163
+
164
+ export function setupSwagger(app: Express): void {
165
+ app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec));
166
+ }
167
+ `;
168
+ await fs.writeFile(path.join(configDir, 'swagger.ts'), swaggerContent);
169
+ }
170
+ async function generateMiddlewares(middlewaresDir, config) {
171
+ // errorHandler.ts
172
+ const errorHandlerContent = `import { Request, Response, NextFunction } from 'express';
173
+ import { ZodError } from 'zod';
174
+ import { logger } from '../utils/logger.js';
175
+
176
+ export class AppError extends Error {
177
+ constructor(
178
+ public statusCode: number,
179
+ message: string,
180
+ public isOperational = true
181
+ ) {
182
+ super(message);
183
+ Object.setPrototypeOf(this, AppError.prototype);
184
+ }
185
+ }
186
+
187
+ export const errorHandler = (
188
+ err: Error,
189
+ req: Request,
190
+ res: Response,
191
+ next: NextFunction
192
+ ) => {
193
+ logger.error(err);
194
+
195
+ if (err instanceof ZodError) {
196
+ return res.status(400).json({
197
+ status: 'error',
198
+ message: 'Validation error',
199
+ errors: err.errors,
200
+ });
201
+ }
202
+
203
+ if (err instanceof AppError) {
204
+ return res.status(err.statusCode).json({
205
+ status: 'error',
206
+ message: err.message,
207
+ });
208
+ }
209
+
210
+ res.status(500).json({
211
+ status: 'error',
212
+ message: 'Internal server error',
213
+ });
214
+ };
215
+ `;
216
+ await fs.writeFile(path.join(middlewaresDir, 'errorHandler.ts'), errorHandlerContent);
217
+ // validator.ts
218
+ const validatorContent = `import { Request, Response, NextFunction } from 'express';
219
+ import { ZodSchema, ZodError } from 'zod';
220
+
221
+ export const validate = (schema: ZodSchema) => {
222
+ return (req: Request, res: Response, next: NextFunction) => {
223
+ try {
224
+ schema.parse({
225
+ body: req.body,
226
+ query: req.query,
227
+ params: req.params,
228
+ });
229
+ next();
230
+ } catch (error) {
231
+ if (error instanceof ZodError) {
232
+ return res.status(400).json({
233
+ status: 'error',
234
+ message: 'Validation error',
235
+ errors: error.errors,
236
+ });
237
+ }
238
+ next(error);
239
+ }
240
+ };
241
+ };
242
+ `;
243
+ await fs.writeFile(path.join(middlewaresDir, 'validator.ts'), validatorContent);
244
+ if (config.includeAuth) {
245
+ // auth.ts
246
+ const authContent = `import { Request, Response, NextFunction } from 'express';
247
+ import jwt from 'jsonwebtoken';
248
+ import { config } from '../config/env.js';
249
+ import { AppError } from './errorHandler.js';
250
+
251
+ export interface AuthRequest extends Request {
252
+ user?: {
253
+ id: string;
254
+ email: string;
255
+ };
256
+ }
257
+
258
+ export const authenticate = (
259
+ req: AuthRequest,
260
+ res: Response,
261
+ next: NextFunction
262
+ ) => {
263
+ try {
264
+ const authHeader = req.headers.authorization;
265
+
266
+ if (!authHeader || !authHeader.startsWith('Bearer ')) {
267
+ throw new AppError(401, 'Authentication required');
268
+ }
269
+
270
+ const token = authHeader.substring(7);
271
+ const decoded = jwt.verify(token, config.JWT_SECRET) as {
272
+ id: string;
273
+ email: string;
274
+ };
275
+
276
+ req.user = decoded;
277
+ next();
278
+ } catch (error) {
279
+ if (error instanceof jwt.JsonWebTokenError) {
280
+ next(new AppError(401, 'Invalid token'));
281
+ } else {
282
+ next(error);
283
+ }
284
+ }
285
+ };
286
+ `;
287
+ await fs.writeFile(path.join(middlewaresDir, 'auth.ts'), authContent);
288
+ }
289
+ }
290
+ async function generateUtils(utilsDir) {
291
+ const loggerContent = `import winston from 'winston';
292
+ import { config } from '../config/env.js';
293
+ import fs from 'fs-extra';
294
+ import path from 'path';
295
+
296
+ // Ensure logs directory exists
297
+ const logsDir = path.join(process.cwd(), 'logs');
298
+ fs.ensureDirSync(logsDir);
299
+
300
+ const logFormat = winston.format.combine(
301
+ winston.format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }),
302
+ winston.format.errors({ stack: true }),
303
+ winston.format.splat(),
304
+ winston.format.json()
305
+ );
306
+
307
+ export const logger = winston.createLogger({
308
+ level: config.NODE_ENV === 'production' ? 'info' : 'debug',
309
+ format: logFormat,
310
+ defaultMeta: { service: 'coreback-api' },
311
+ transports: [
312
+ new winston.transports.File({ filename: path.join(logsDir, 'error.log'), level: 'error' }),
313
+ new winston.transports.File({ filename: path.join(logsDir, 'combined.log') }),
314
+ ],
315
+ });
316
+
317
+ if (config.NODE_ENV !== 'production') {
318
+ logger.add(
319
+ new winston.transports.Console({
320
+ format: winston.format.combine(
321
+ winston.format.colorize(),
322
+ winston.format.simple()
323
+ ),
324
+ })
325
+ );
326
+ }
327
+ `;
328
+ await fs.writeFile(path.join(utilsDir, 'logger.ts'), loggerContent);
329
+ }
330
+ async function generateRoutes(routesDir, config) {
331
+ // index.ts
332
+ let indexContent = `import { Express } from 'express';
333
+ import { healthRoutes } from './health.routes.js';
334
+ `;
335
+ if (config.includeAuth) {
336
+ indexContent += `import { authRoutes } from './auth.routes.js';
337
+ `;
338
+ }
339
+ indexContent += `
340
+ export function setupRoutes(app: Express): void {
341
+ app.use('/api/health', healthRoutes);
342
+ `;
343
+ if (config.includeAuth) {
344
+ indexContent += ` app.use('/api/auth', authRoutes);
345
+ `;
346
+ }
347
+ indexContent += `}
348
+ `;
349
+ await fs.writeFile(path.join(routesDir, 'index.ts'), indexContent);
350
+ // health.routes.ts
351
+ const healthRoutesContent = `import { Router } from 'express';
352
+ import { healthController } from '../controllers/health.controller.js';
353
+
354
+ /**
355
+ * @swagger
356
+ * tags:
357
+ * name: Health
358
+ * description: Health check endpoints
359
+ */
360
+
361
+ const router = Router();
362
+
363
+ /**
364
+ * @swagger
365
+ * /api/health:
366
+ * get:
367
+ * summary: Health check endpoint
368
+ * tags: [Health]
369
+ * responses:
370
+ * 200:
371
+ * description: Service is healthy
372
+ * content:
373
+ * application/json:
374
+ * schema:
375
+ * type: object
376
+ * properties:
377
+ * status:
378
+ * type: string
379
+ * example: ok
380
+ * timestamp:
381
+ * type: string
382
+ * example: 2024-01-01T00:00:00.000Z
383
+ */
384
+ router.get('/', healthController.check);
385
+
386
+ export { router as healthRoutes };
387
+ `;
388
+ await fs.writeFile(path.join(routesDir, 'health.routes.ts'), healthRoutesContent);
389
+ if (config.includeAuth) {
390
+ // auth.routes.ts
391
+ const authRoutesContent = `import { Router } from 'express';
392
+ import { authController } from '../controllers/auth.controller.js';
393
+ import { validate } from '../middlewares/validator.js';
394
+ import { registerSchema, loginSchema } from '../validators/auth.validator.js';
395
+ import { authenticate } from '../middlewares/auth.js';
396
+
397
+ /**
398
+ * @swagger
399
+ * tags:
400
+ * name: Auth
401
+ * description: Authentication endpoints
402
+ */
403
+
404
+ const router = Router();
405
+
406
+ /**
407
+ * @swagger
408
+ * /api/auth/register:
409
+ * post:
410
+ * summary: Register a new user
411
+ * tags: [Auth]
412
+ * requestBody:
413
+ * required: true
414
+ * content:
415
+ * application/json:
416
+ * schema:
417
+ * type: object
418
+ * required:
419
+ * - email
420
+ * - password
421
+ * properties:
422
+ * email:
423
+ * type: string
424
+ * format: email
425
+ * password:
426
+ * type: string
427
+ * minLength: 8
428
+ * name:
429
+ * type: string
430
+ * responses:
431
+ * 201:
432
+ * description: User created successfully
433
+ * 400:
434
+ * description: Validation error
435
+ */
436
+ router.post('/register', validate(registerSchema), authController.register);
437
+
438
+ /**
439
+ * @swagger
440
+ * /api/auth/login:
441
+ * post:
442
+ * summary: Login user
443
+ * tags: [Auth]
444
+ * requestBody:
445
+ * required: true
446
+ * content:
447
+ * application/json:
448
+ * schema:
449
+ * type: object
450
+ * required:
451
+ * - email
452
+ * - password
453
+ * properties:
454
+ * email:
455
+ * type: string
456
+ * format: email
457
+ * password:
458
+ * type: string
459
+ * responses:
460
+ * 200:
461
+ * description: Login successful
462
+ * 401:
463
+ * description: Invalid credentials
464
+ */
465
+ router.post('/login', validate(loginSchema), authController.login);
466
+
467
+ /**
468
+ * @swagger
469
+ * /api/auth/me:
470
+ * get:
471
+ * summary: Get current user
472
+ * tags: [Auth]
473
+ * security:
474
+ * - bearerAuth: []
475
+ * responses:
476
+ * 200:
477
+ * description: Current user information
478
+ * 401:
479
+ * description: Unauthorized
480
+ */
481
+ router.get('/me', authenticate, authController.me);
482
+
483
+ export { router as authRoutes };
484
+ `;
485
+ await fs.writeFile(path.join(routesDir, 'auth.routes.ts'), authRoutesContent);
486
+ }
487
+ }
488
+ async function generateTypes(typesDir) {
489
+ const indexContent = `export interface ApiResponse<T = unknown> {
490
+ status: 'success' | 'error';
491
+ data?: T;
492
+ message?: string;
493
+ errors?: unknown[];
494
+ }
495
+ `;
496
+ await fs.writeFile(path.join(typesDir, 'index.ts'), indexContent);
497
+ }
498
+ async function generateValidators(validatorsDir, config) {
499
+ if (config.includeAuth) {
500
+ const authValidatorContent = `import { z } from 'zod';
501
+
502
+ export const registerSchema = z.object({
503
+ body: z.object({
504
+ email: z.string().email('Invalid email format'),
505
+ password: z.string().min(8, 'Password must be at least 8 characters'),
506
+ name: z.string().optional(),
507
+ }),
508
+ });
509
+
510
+ export const loginSchema = z.object({
511
+ body: z.object({
512
+ email: z.string().email('Invalid email format'),
513
+ password: z.string().min(1, 'Password is required'),
514
+ }),
515
+ });
516
+ `;
517
+ await fs.writeFile(path.join(validatorsDir, 'auth.validator.ts'), authValidatorContent);
518
+ }
519
+ }
520
+ async function generateControllers(controllersDir, config) {
521
+ // health.controller.ts
522
+ const healthControllerContent = `import { Request, Response } from 'express';
523
+
524
+ export const healthController = {
525
+ check: (_req: Request, res: Response) => {
526
+ res.json({
527
+ status: 'ok',
528
+ timestamp: new Date().toISOString(),
529
+ });
530
+ },
531
+ };
532
+ `;
533
+ await fs.writeFile(path.join(controllersDir, 'health.controller.ts'), healthControllerContent);
534
+ if (config.includeAuth) {
535
+ // auth.controller.ts
536
+ const authControllerContent = `import { Response } from 'express';
537
+ import { AuthRequest } from '../middlewares/auth.js';
538
+ import { authService } from '../services/auth.service.js';
539
+ import { AppError } from '../middlewares/errorHandler.js';
540
+
541
+ export const authController = {
542
+ register: async (req: AuthRequest, res: Response) => {
543
+ try {
544
+ const { email, password, name } = req.body;
545
+ const user = await authService.register(email, password, name);
546
+ res.status(201).json({
547
+ status: 'success',
548
+ data: user,
549
+ });
550
+ } catch (error) {
551
+ if (error instanceof AppError) {
552
+ throw error;
553
+ }
554
+ throw new AppError(500, 'Failed to register user');
555
+ }
556
+ },
557
+
558
+ login: async (req: AuthRequest, res: Response) => {
559
+ try {
560
+ const { email, password } = req.body;
561
+ const result = await authService.login(email, password);
562
+ res.json({
563
+ status: 'success',
564
+ data: result,
565
+ });
566
+ } catch (error) {
567
+ if (error instanceof AppError) {
568
+ throw error;
569
+ }
570
+ throw new AppError(500, 'Failed to login');
571
+ }
572
+ },
573
+
574
+ me: async (req: AuthRequest, res: Response) => {
575
+ try {
576
+ const user = await authService.getUserById(req.user!.id);
577
+ res.json({
578
+ status: 'success',
579
+ data: user,
580
+ });
581
+ } catch (error) {
582
+ if (error instanceof AppError) {
583
+ throw error;
584
+ }
585
+ throw new AppError(500, 'Failed to get user');
586
+ }
587
+ },
588
+ };
589
+ `;
590
+ await fs.writeFile(path.join(controllersDir, 'auth.controller.ts'), authControllerContent);
591
+ }
592
+ }
593
+ async function generateServices(servicesDir, config) {
594
+ if (config.includeAuth) {
595
+ const authServiceContent = `import bcrypt from 'bcrypt';
596
+ import jwt from 'jsonwebtoken';
597
+ import { config } from '../config/env.js';
598
+ import { userRepository } from '../repositories/user.repository.js';
599
+ import { AppError } from '../middlewares/errorHandler.js';
600
+
601
+ export const authService = {
602
+ async register(email: string, password: string, name?: string) {
603
+ const existingUser = await userRepository.findByEmail(email);
604
+
605
+ if (existingUser) {
606
+ throw new AppError(400, 'User already exists');
607
+ }
608
+
609
+ const hashedPassword = await bcrypt.hash(password, 10);
610
+ const user = await userRepository.create({
611
+ email,
612
+ password: hashedPassword,
613
+ name,
614
+ });
615
+
616
+ const token = jwt.sign(
617
+ { id: user.id, email: user.email },
618
+ config.JWT_SECRET,
619
+ { expiresIn: config.JWT_EXPIRES_IN }
620
+ );
621
+
622
+ return {
623
+ user: {
624
+ id: user.id,
625
+ email: user.email,
626
+ name: user.name,
627
+ },
628
+ token,
629
+ };
630
+ },
631
+
632
+ async login(email: string, password: string) {
633
+ const user = await userRepository.findByEmail(email);
634
+
635
+ if (!user) {
636
+ throw new AppError(401, 'Invalid credentials');
637
+ }
638
+
639
+ const isValidPassword = await bcrypt.compare(password, user.password);
640
+
641
+ if (!isValidPassword) {
642
+ throw new AppError(401, 'Invalid credentials');
643
+ }
644
+
645
+ const token = jwt.sign(
646
+ { id: user.id, email: user.email },
647
+ config.JWT_SECRET,
648
+ { expiresIn: config.JWT_EXPIRES_IN }
649
+ );
650
+
651
+ return {
652
+ user: {
653
+ id: user.id,
654
+ email: user.email,
655
+ name: user.name,
656
+ },
657
+ token,
658
+ };
659
+ },
660
+
661
+ async getUserById(id: string) {
662
+ const user = await userRepository.findById(id);
663
+
664
+ if (!user) {
665
+ throw new AppError(404, 'User not found');
666
+ }
667
+
668
+ return {
669
+ id: user.id,
670
+ email: user.email,
671
+ name: user.name,
672
+ createdAt: user.createdAt,
673
+ updatedAt: user.updatedAt,
674
+ };
675
+ },
676
+ };
677
+ `;
678
+ await fs.writeFile(path.join(servicesDir, 'auth.service.ts'), authServiceContent);
679
+ }
680
+ }
681
+ async function generateRepositories(repositoriesDir, config) {
682
+ if (config.includeAuth) {
683
+ const userRepositoryContent = `import { prisma } from '../config/database.js';
684
+
685
+ export const userRepository = {
686
+ async findByEmail(email: string) {
687
+ return prisma.user.findUnique({
688
+ where: { email },
689
+ });
690
+ },
691
+
692
+ async findById(id: string) {
693
+ return prisma.user.findUnique({
694
+ where: { id },
695
+ select: {
696
+ id: true,
697
+ email: true,
698
+ name: true,
699
+ createdAt: true,
700
+ updatedAt: true,
701
+ },
702
+ });
703
+ },
704
+
705
+ async create(data: { email: string; password: string; name?: string }) {
706
+ return prisma.user.create({
707
+ data,
708
+ select: {
709
+ id: true,
710
+ email: true,
711
+ name: true,
712
+ createdAt: true,
713
+ updatedAt: true,
714
+ },
715
+ });
716
+ },
717
+ };
718
+ `;
719
+ await fs.writeFile(path.join(repositoriesDir, 'user.repository.ts'), userRepositoryContent);
720
+ }
721
+ }
722
+ async function generateTests(unitTestsDir, integrationTestsDir, config) {
723
+ // Unit test example
724
+ const unitTestContent = `import { healthController } from '../../src/controllers/health.controller.js';
725
+
726
+ describe('Health Controller', () => {
727
+ it('should return ok status', () => {
728
+ const mockReq = {} as any;
729
+ const mockRes = {
730
+ json: jest.fn(),
731
+ } as any;
732
+
733
+ healthController.check(mockReq, mockRes);
734
+
735
+ expect(mockRes.json).toHaveBeenCalledWith({
736
+ status: 'ok',
737
+ timestamp: expect.any(String),
738
+ });
739
+ });
740
+ });
741
+ `;
742
+ await fs.writeFile(path.join(unitTestsDir, 'health.controller.test.ts'), unitTestContent);
743
+ // Integration test example
744
+ const integrationTestContent = `import request from 'supertest';
745
+ import express from 'express';
746
+ import { setupRoutes } from '../../src/routes/index.js';
747
+ import { setupMiddlewares } from '../../src/config/middlewares.js';
748
+ import { errorHandler } from '../../src/middlewares/errorHandler.js';
749
+
750
+ const app = express();
751
+ setupMiddlewares(app);
752
+ setupRoutes(app);
753
+ app.use(errorHandler);
754
+
755
+ describe('Health API', () => {
756
+ it('GET /api/health should return 200', async () => {
757
+ const response = await request(app).get('/api/health');
758
+
759
+ expect(response.status).toBe(200);
760
+ expect(response.body).toHaveProperty('status', 'ok');
761
+ expect(response.body).toHaveProperty('timestamp');
762
+ });
763
+ });
764
+ `;
765
+ await fs.writeFile(path.join(integrationTestsDir, 'health.api.test.ts'), integrationTestContent);
766
+ }
767
+ //# sourceMappingURL=sourceFiles.js.map