nestjs-prisma-cli 1.0.8 → 1.0.9

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 (28) hide show
  1. package/bin/index.js +45 -32
  2. package/package.json +2 -2
  3. package/template/prisma/schema.prisma +34 -17
  4. package/template/src/app.module.ts +7 -2
  5. package/template/src/common/config/index.ts +4 -0
  6. package/template/src/common/config/multer-config.ts +11 -0
  7. package/template/src/common/config/swagger.config.ts +36 -0
  8. package/template/src/common/decorator/index.ts +4 -0
  9. package/template/src/common/decorator/response-message.decorator.ts +5 -0
  10. package/template/src/common/dto/paginated-response.dto.ts +14 -2
  11. package/template/src/common/dto/pagination.dto.ts +18 -0
  12. package/template/src/common/enums/http-error-type-enum.ts +31 -0
  13. package/template/src/common/enums/index.ts +2 -1
  14. package/template/src/common/enums/message-code.enum.ts +2 -0
  15. package/template/src/common/http-interceptor/http-exception.filter.ts +99 -57
  16. package/template/src/common/http-interceptor/logging.interceptor.ts +55 -10
  17. package/template/src/common/http-interceptor/response.interceptor.ts +68 -28
  18. package/template/src/common/s3/s3.service.ts +9 -24
  19. package/template/src/common/utils/pagination.util.ts +46 -16
  20. package/template/src/main.ts +11 -38
  21. package/template/src/modules/auth/auth.controller.ts +21 -0
  22. package/template/src/modules/auth/auth.service.ts +9 -9
  23. package/template/src/modules/auth/jwt/jwt.guard.ts +1 -1
  24. package/template/src/modules/user/dto/create-user.dto.ts +8 -1
  25. package/template/src/modules/user/user.controller.ts +53 -16
  26. package/template/src/modules/user/user.module.ts +3 -0
  27. package/template/src/modules/user/user.service.ts +132 -98
  28. package/template/src/common/classes/base64.ts +0 -22
package/bin/index.js CHANGED
@@ -56,42 +56,42 @@ model User {
56
56
  name String?
57
57
  email String
58
58
  password String
59
+ profileUrl String?
59
60
  isActive Boolean @default(true)
60
61
  createdAt DateTime @default(now())
61
62
  updatedAt DateTime @updatedAt
62
63
  @@map("tbl_user")
63
64
  }
65
+
66
+ model Log {
67
+ id String @id @default(auto()) @map("_id") @db.ObjectId
68
+ method String
69
+ path String
70
+ statusCode Int
71
+ messageCode String?
72
+ message String?
73
+ headers Json
74
+ body Json?
75
+ query Json?
76
+ duration Int
77
+ createdAt DateTime @default(now())
78
+
79
+ @@map("tbl_log")
80
+ }
64
81
  `;
65
82
  }
66
83
 
67
84
  try {
68
85
  let prismaContent = await fs.readFile(templatePrismaPath, "utf-8");
69
- return prismaContent.replace(
70
- /datasource\s+db\s*{[^}]*provider\s*=\s*".*"/,
71
- `datasource db {\n provider = "${selectedProvider}"`
72
- );
73
- } catch {
74
- return `generator client {
75
- provider = "prisma-client-js"
76
- }
77
86
 
78
- datasource db {
79
- provider = "${selectedProvider}"
80
- url = env("DATABASE_URL")
81
- }
87
+ prismaContent = prismaContent.replace(
88
+ /(datasource\s+db\s*{[^}]*provider\s*=\s*")\w+(")/,
89
+ `$1${selectedProvider}$2`
90
+ );
82
91
 
83
- model User {
84
- id Int @id @default(autoincrement())
85
- userId String @unique
86
- name String?
87
- email String
88
- password String
89
- isActive Boolean @default(true)
90
- createdAt DateTime @default(now())
91
- updatedAt DateTime @updatedAt
92
- @@map("tbl_user")
93
- }
94
- `;
92
+ return prismaContent;
93
+ } catch (err) {
94
+ throw new Error(`Failed to read Prisma template: ${err.message}`);
95
95
  }
96
96
  }
97
97
 
@@ -141,12 +141,27 @@ async function main() {
141
141
  "@aws-sdk/s3-request-presigner",
142
142
  "moment",
143
143
  ];
144
- await execa(pkgManager, ["install", ...coreDeps], { cwd: projectPath, stdio: "inherit" });
145
144
 
146
- await execa(pkgManager, ["install", "@prisma/client"], { cwd: projectPath, stdio: "inherit" });
147
- await execa(pkgManager, ["install", "-D", "prisma"], { cwd: projectPath, stdio: "inherit" });
145
+ const devDeps = [
146
+ "prisma",
147
+ "@types/multer",
148
+ ];
149
+
148
150
 
149
- console.log(chalk.green(" Dependencies installed!"));
151
+ await execa(pkgManager, ["install", ...coreDeps], {
152
+ cwd: projectPath,
153
+ stdio: "inherit",
154
+ });
155
+
156
+ await execa(pkgManager, ["install", "@prisma/client"], {
157
+ cwd: projectPath,
158
+ stdio: "inherit",
159
+ });
160
+
161
+ await execa(pkgManager, ["install", "-D", ...devDeps], {
162
+ cwd: projectPath,
163
+ stdio: "inherit",
164
+ });
150
165
 
151
166
  const templatePath = path.resolve(__dirname, "../template");
152
167
  const projectPrismaPath = path.join(projectPath, "prisma/schema.prisma");
@@ -154,7 +169,6 @@ async function main() {
154
169
 
155
170
  if (await fs.pathExists(templatePath)) {
156
171
  await fs.copy(templatePath, projectPath, { overwrite: true });
157
- console.log(chalk.green("✅ Template files copied!"));
158
172
  }
159
173
 
160
174
  const providerMap = {
@@ -199,10 +213,9 @@ PORT=3000
199
213
  await fs.outputFile(path.join(projectPath, ".env"), envContent);
200
214
 
201
215
  console.log(chalk.yellow("🎉 Project ready!"));
202
- console.log(chalk.green("✅ .env created! Please update DATABASE_URL if necessary before running Prisma commands."));
203
- console.log(chalk.cyan(`cd ${projectName}`));
216
+ console.log(chalk.green("✅ Congratulations! Your project has been created successfully."));
217
+ console.log(chalk.cyan(`👉 Next steps: cd ${projectName}`));
204
218
 
205
- console.log(chalk.yellow("🔧 Next steps (run manually):"));
206
219
  console.log(chalk.cyan(`1. Generate Prisma Client:`));
207
220
  console.log(chalk.cyan(` npx prisma generate`));
208
221
  if (selectedProvider === "mongodb") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nestjs-prisma-cli",
3
- "version": "1.0.8",
3
+ "version": "1.0.9",
4
4
  "description": "A CLI to generate NestJS + Prisma project boilerplate with Swagger, Auth, and AWS S3 setup",
5
5
  "type": "module",
6
6
  "main": "bin/index.js",
@@ -28,7 +28,7 @@
28
28
  ],
29
29
  "author": "Kyaw Soe",
30
30
  "license": "MIT",
31
- "homepage": "https://github.com/kyawsoe-dev/nestjs-generator-cli/blob/main/README.md",
31
+ "homepage": "https://github.com/kyawsoe-dev/nestjs-generator-cli#readme",
32
32
  "dependencies": {
33
33
  "chalk": "^4.1.2",
34
34
  "execa": "^9.6.0",
@@ -1,20 +1,37 @@
1
1
  generator client {
2
- provider = "prisma-client-js"
3
- }
2
+ provider = "prisma-client-js"
3
+ }
4
4
 
5
- datasource db {
6
- provider = "mysql"
7
- url = env("DATABASE_URL")
8
- }
5
+ datasource db {
6
+ provider = "mysql"
7
+ url = env("DATABASE_URL")
8
+ }
9
9
 
10
- model User {
11
- id Int @id @default(autoincrement())
12
- userId String @unique
13
- name String?
14
- email String
15
- password String
16
- createdAt DateTime @default(now())
17
- updatedAt DateTime @updatedAt
18
- @@map("tbl_user")
19
- }
20
-
10
+ model User {
11
+ id Int @id @default(autoincrement())
12
+ userId String @unique
13
+ name String?
14
+ email String
15
+ password String
16
+ profileUrl String?
17
+ createdAt DateTime @default(now())
18
+ updatedAt DateTime @updatedAt
19
+
20
+ @@map("tbl_user")
21
+ }
22
+
23
+ model Log {
24
+ id Int @id @default(autoincrement())
25
+ method String
26
+ path String
27
+ statusCode Int
28
+ messageCode String?
29
+ message String?
30
+ headers Json
31
+ body Json?
32
+ query Json?
33
+ duration Int
34
+ createdAt DateTime @default(now())
35
+
36
+ @@map("tbl_log")
37
+ }
@@ -1,5 +1,5 @@
1
1
  import { Module } from '@nestjs/common';
2
- import { APP_INTERCEPTOR } from '@nestjs/core';
2
+ import { APP_FILTER, APP_INTERCEPTOR } from '@nestjs/core';
3
3
  import { APP_GUARD } from '@nestjs/core';
4
4
  import { AppController } from './app.controller';
5
5
  import { AppService } from './app.service';
@@ -7,6 +7,7 @@ import { PrismaModule } from './prisma/prisma.module';
7
7
  import {
8
8
  HttpResponseInterceptor,
9
9
  LoggingInterceptor,
10
+ HttpExceptionFilter,
10
11
  } from './common/http-interceptor/index';
11
12
  import { JwtAuthGuard } from './modules/auth/jwt/jwt.guard';
12
13
  import { ConfigModule } from '@nestjs/config';
@@ -20,7 +21,7 @@ import { AuthModule } from './modules/auth/auth.module';
20
21
  }),
21
22
  PrismaModule,
22
23
  UserModule,
23
- AuthModule
24
+ AuthModule,
24
25
  ],
25
26
  controllers: [AppController],
26
27
  providers: [
@@ -28,6 +29,10 @@ import { AuthModule } from './modules/auth/auth.module';
28
29
  provide: APP_INTERCEPTOR,
29
30
  useClass: LoggingInterceptor,
30
31
  },
32
+ {
33
+ provide: APP_FILTER,
34
+ useClass: HttpExceptionFilter,
35
+ },
31
36
  {
32
37
  provide: APP_INTERCEPTOR,
33
38
  useClass: HttpResponseInterceptor,
@@ -0,0 +1,4 @@
1
+ import { MulterConfig } from './multer-config';
2
+ import { SwaggerConfig } from './swagger.config';
3
+
4
+ export { MulterConfig, SwaggerConfig };
@@ -0,0 +1,11 @@
1
+ import { MulterOptions } from '@nestjs/platform-express/multer/interfaces/multer-options.interface';
2
+
3
+ export const MulterConfig: MulterOptions = {
4
+ limits: { fileSize: 10 * 1024 * 1024 },
5
+ fileFilter: (req, file, cb) => {
6
+ if (!file.mimetype.match(/^image\/(jpg|jpeg|png|gif)$/)) {
7
+ return cb(new Error('Only image files are allowed!'), false);
8
+ }
9
+ cb(null, true);
10
+ },
11
+ };
@@ -0,0 +1,36 @@
1
+ import { writeFileSync } from 'fs';
2
+ import { join } from 'path';
3
+ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
4
+ import { INestApplication } from '@nestjs/common';
5
+
6
+ const title = 'API';
7
+ const description = 'API Swagger documentation';
8
+
9
+ export const SwaggerConfig = (app: INestApplication) => {
10
+ const options = new DocumentBuilder()
11
+ .setTitle(title)
12
+ .setDescription(description)
13
+ .setVersion('1.0')
14
+ .addBearerAuth(
15
+ {
16
+ type: 'http',
17
+ scheme: 'bearer',
18
+ bearerFormat: 'JWT',
19
+ },
20
+ 'bearerAuth',
21
+ )
22
+ .addSecurityRequirements('bearerAuth')
23
+ .build();
24
+
25
+ const document = SwaggerModule.createDocument(app, options);
26
+
27
+ const outputPath = join(process.cwd(), 'swagger-openapi-v1.json');
28
+ writeFileSync(outputPath, JSON.stringify(document, null, 2));
29
+ console.log(`Swagger JSON saved to ${outputPath}`);
30
+
31
+ SwaggerModule.setup('api', app, document, {
32
+ swaggerOptions: {
33
+ persistAuthorization: true,
34
+ },
35
+ });
36
+ };
@@ -0,0 +1,4 @@
1
+ import { Public, IS_PUBLIC_KEY } from './public.decorator';
2
+ import { ResponseMessage } from './response-message.decorator';
3
+
4
+ export { Public, IS_PUBLIC_KEY, ResponseMessage };
@@ -0,0 +1,5 @@
1
+
2
+ import { SetMetadata } from '@nestjs/common';
3
+ export const RESPONSE_MESSAGE_METADATA = 'responseMessage';
4
+ export const ResponseMessage = (message: string) =>
5
+ SetMetadata(RESPONSE_MESSAGE_METADATA, message);
@@ -5,10 +5,22 @@ export class PaginatedResponseDto<T> {
5
5
  total: number;
6
6
 
7
7
  @ApiProperty()
8
- page: number;
8
+ limit: number;
9
9
 
10
10
  @ApiProperty()
11
- limit: number;
11
+ currentPage: number;
12
+
13
+ @ApiProperty()
14
+ firstPage: number;
15
+
16
+ @ApiProperty()
17
+ lastPage: number;
18
+
19
+ @ApiProperty()
20
+ nextPage: number | null;
21
+
22
+ @ApiProperty()
23
+ previousPage: number | null;
12
24
 
13
25
  @ApiProperty({ isArray: true })
14
26
  data: T[];
@@ -1,10 +1,12 @@
1
1
  import { ApiPropertyOptional } from '@nestjs/swagger';
2
+ import { Transform } from 'class-transformer';
2
3
  import { Type } from 'class-transformer';
3
4
  import {
4
5
  IsInt,
5
6
  IsOptional,
6
7
  IsString,
7
8
  Min,
9
+ IsIn,
8
10
  IsBooleanString,
9
11
  } from 'class-validator';
10
12
 
@@ -23,6 +25,22 @@ export class PaginationDto {
23
25
  @Min(1)
24
26
  limit?: number = 20;
25
27
 
28
+ @ApiPropertyOptional({ example: 'id', description: 'Field to sort by' })
29
+ @IsOptional()
30
+ @IsString()
31
+ sortBy?: string;
32
+
33
+ @ApiPropertyOptional({
34
+ example: 'desc',
35
+ description: 'Sort order: asc or desc',
36
+ })
37
+ @IsOptional()
38
+ @IsIn(['asc', 'desc'])
39
+ @Transform(({ value }) =>
40
+ typeof value === 'string' ? value.toLowerCase() : value,
41
+ )
42
+ sortOrder?: 'asc' | 'desc';
43
+
26
44
  @ApiPropertyOptional({ example: '', description: 'Search term' })
27
45
  @IsOptional()
28
46
  @IsString()
@@ -0,0 +1,31 @@
1
+ export enum HttpErrorType {
2
+ BAD_REQUEST = 400,
3
+ UNAUTHORIZED = 401,
4
+ PAYMENT_REQUIRED = 402,
5
+ FORBIDDEN = 403,
6
+ NOT_FOUND = 404,
7
+ METHOD_NOT_ALLOWED = 405,
8
+ NOT_ACCEPTABLE = 406,
9
+ PROXY_AUTHENTICATION_REQUIRED = 407,
10
+ REQUEST_TIMEOUT = 408,
11
+ CONFLICT = 409,
12
+ GONE = 410,
13
+ LENGTH_REQUIRED = 411,
14
+ PRECONDITION_FAILED = 412,
15
+ PAYLOAD_TOO_LARGE = 413,
16
+ URI_TOO_LONG = 414,
17
+ UNSUPPORTED_MEDIA_TYPE = 415,
18
+ REQUESTED_RANGE_NOT_SATISFIABLE = 416,
19
+ EXPECTATION_FAILED = 417,
20
+ I_AM_A_TEAPOT = 418,
21
+ MISDIRECTED = 421,
22
+ UNPROCESSABLE_ENTITY = 422,
23
+ FAILED_DEPENDENCY = 424,
24
+ TOO_MANY_REQUESTS = 429,
25
+ INTERNAL_SERVER_ERROR = 500,
26
+ NOT_IMPLEMENTED = 501,
27
+ BAD_GATEWAY = 502,
28
+ SERVICE_UNAVAILABLE = 503,
29
+ GATEWAY_TIMEOUT = 504,
30
+ HTTP_VERSION_NOT_SUPPORTED = 505,
31
+ }
@@ -1,4 +1,5 @@
1
1
  import { DBErrorCode } from './db-error-code.enum';
2
+ import { HttpErrorType } from './http-error-type-enum';
2
3
  import { MESSAGE_CODE } from './message-code.enum';
3
4
 
4
- export { DBErrorCode, MESSAGE_CODE };
5
+ export { DBErrorCode, MESSAGE_CODE, HttpErrorType };
@@ -5,6 +5,7 @@ export enum MESSAGE_CODE {
5
5
  PASSWORD_SUCCESS = 204,
6
6
  DATA_NOT_FOUND = 205,
7
7
  USER_VERIFY = 206,
8
+
8
9
  WRONG_USER_PW = 401,
9
10
  INVALID_USER_ID = 402,
10
11
  MISSED_TOKEN = 403,
@@ -16,5 +17,6 @@ export enum MESSAGE_CODE {
16
17
  ROUTE_NOT_FOUND = 410,
17
18
  REQUEST_FIELD_REQUIRED = 411,
18
19
  DELETE_USER = 412,
20
+
19
21
  SERVER_ERROR = 501,
20
22
  }
@@ -1,94 +1,136 @@
1
+ import { PrismaClientKnownRequestError } from "@prisma/client/runtime/library";
1
2
  import {
2
- ExceptionFilter,
3
- Catch,
4
3
  ArgumentsHost,
4
+ Catch,
5
+ ExceptionFilter,
5
6
  HttpException,
6
7
  HttpStatus,
7
- } from '@nestjs/common';
8
- import { Request, Response } from 'express';
9
- import { DBErrorCode, MESSAGE_CODE } from '../enums';
10
- import { AppLogger } from '../logger/winston.logger';
8
+ } from "@nestjs/common";
9
+ import { Request, Response } from "express";
10
+ import * as moment from "moment";
11
+ import { PrismaService } from "src/prisma/prisma.service";
12
+ import { AppLogger } from "../logger/winston.logger";
13
+ import { DBErrorCode, MESSAGE_CODE } from "../enums";
11
14
 
12
15
  @Catch()
13
16
  export class HttpExceptionFilter implements ExceptionFilter {
14
- catch(exception: unknown, host: ArgumentsHost) {
15
- console.log(exception, 'exception');
17
+ constructor(private readonly prisma: PrismaService) {}
18
+
19
+ async catch(exception: unknown, host: ArgumentsHost) {
20
+ console.log(exception, "exception");
16
21
  const ctx = host.switchToHttp();
17
22
  const request = ctx.getRequest<Request>();
18
23
  const response = ctx.getResponse<Response>();
24
+ const { method, originalUrl, headers, body, query } = request;
19
25
 
20
- let status = HttpStatus.INTERNAL_SERVER_ERROR;
21
- let message = 'An unexpected error occurred';
22
- let messageCode: string | number = status;
26
+ let statusCode = HttpStatus.INTERNAL_SERVER_ERROR;
27
+ let message = "Internal Server Error";
28
+ let messageCode: string | number = statusCode;
29
+ let validationErrors: Record<string, string[]> | null = null;
23
30
 
24
31
  if (exception instanceof HttpException) {
25
- status = exception.getStatus();
26
- const exceptionResponse = exception.getResponse();
27
-
28
- if (typeof exceptionResponse === 'string') {
29
- message = capitalizeFirst(exceptionResponse);
30
- messageCode = MESSAGE_CODE.INVALID;
31
- } else if (
32
- typeof exceptionResponse === 'object' &&
33
- exceptionResponse !== null
32
+ statusCode = exception.getStatus();
33
+ const exRes = exception.getResponse();
34
+ if (
35
+ statusCode === HttpStatus.BAD_REQUEST &&
36
+ typeof exRes === "object" &&
37
+ Array.isArray((exRes as any).message)
34
38
  ) {
35
- const res: any = exceptionResponse;
39
+ const errors = (exRes as any).message;
40
+ validationErrors = {};
41
+ errors.forEach((msg: string) => {
42
+ const field = msg.split(" ")[0];
43
+ if (!validationErrors![field]) validationErrors![field] = [];
44
+ validationErrors![field].push(msg);
45
+ });
46
+ message = "Validation failed with invalid inputs.";
47
+ messageCode = MESSAGE_CODE.INVALID;
48
+ } else if (typeof exRes === "string") {
49
+ message = capitalizeFirst(exRes);
50
+ messageCode = MESSAGE_CODE.INVALID;
51
+ } else if (typeof exRes === "object" && exRes !== null) {
52
+ const res: any = exRes;
36
53
  const rawMessage = Array.isArray(res.message)
37
54
  ? res.message[0]
38
- : (res.message ?? message);
39
-
55
+ : res.message ?? message;
40
56
  message = capitalizeFirst(rawMessage);
41
57
  messageCode =
42
58
  res.messageCode ??
43
- (status === HttpStatus.BAD_REQUEST ? MESSAGE_CODE.INVALID : status);
59
+ (statusCode === HttpStatus.BAD_REQUEST
60
+ ? MESSAGE_CODE.INVALID
61
+ : statusCode);
44
62
  } else {
45
63
  message = capitalizeFirst(exception.message);
46
- messageCode = status;
64
+ messageCode = statusCode;
47
65
  }
48
- } else if (typeof exception === 'object' && exception !== null) {
49
- const ex: any = exception;
50
- if (ex.code) {
51
- switch (ex.code) {
52
- case DBErrorCode.PgUniqueConstraintViolation:
53
- status = HttpStatus.CONFLICT;
54
- message = 'Unique constraint violated';
55
- messageCode = MESSAGE_CODE.INVALID;
56
- break;
57
- case DBErrorCode.PgForeignKeyConstraintViolation:
58
- status = HttpStatus.CONFLICT;
59
- message = 'Foreign key constraint violated';
60
- messageCode = MESSAGE_CODE.INVALID;
61
- break;
62
- case DBErrorCode.PgNotNullConstraintViolation:
63
- status = HttpStatus.BAD_REQUEST;
64
- message = 'Not null constraint violated';
65
- messageCode = MESSAGE_CODE.INVALID;
66
- break;
67
- default:
68
- message = capitalizeFirst(ex.message || 'Database exception');
69
- messageCode = status;
70
- }
66
+ } else if (exception instanceof PrismaClientKnownRequestError) {
67
+ switch (exception.code) {
68
+ case "P2002":
69
+ statusCode = HttpStatus.CONFLICT;
70
+ const target = (exception.meta?.target as string) || "field";
71
+ message = `Duplicate entry: ${target
72
+ .replace("tbl_", "")
73
+ .replace("_key", "")} already exists.`;
74
+ messageCode = MESSAGE_CODE.INVALID;
75
+ break;
76
+ case DBErrorCode.PgForeignKeyConstraintViolation:
77
+ statusCode = HttpStatus.CONFLICT;
78
+ message = "Foreign key constraint violated";
79
+ messageCode = MESSAGE_CODE.INVALID;
80
+ break;
81
+ case DBErrorCode.PgNotNullConstraintViolation:
82
+ statusCode = HttpStatus.BAD_REQUEST;
83
+ message = "Not null constraint violated";
84
+ messageCode = MESSAGE_CODE.INVALID;
85
+ break;
86
+ default:
87
+ message = capitalizeFirst(exception.message || "Database exception");
88
+ messageCode = statusCode;
71
89
  }
90
+ } else if (exception instanceof Error) {
91
+ message = capitalizeFirst(exception.message) || message;
92
+ messageCode = statusCode;
72
93
  }
73
94
 
74
- const duration = Date.now() - (request as any).__startTime || 0;
75
- const logMessage = `${request.method} ${request.originalUrl} ${status} - ${duration}ms | Message Code: ${messageCode} | Message: ${message} | Headers: ${JSON.stringify(request.headers)}`;
95
+ const duration = Date.now() - ((request as any).__startTime || Date.now());
96
+ const logMessage = `${method} ${originalUrl} ${statusCode} - ${duration}ms | MessageCode: ${messageCode} | Message: ${message} | Headers: ${JSON.stringify(
97
+ headers
98
+ )}`;
99
+
100
+ if (statusCode >= 500) AppLogger.error(logMessage);
101
+ else AppLogger.warn(logMessage);
76
102
 
77
- if (status >= 500) {
78
- AppLogger.error(logMessage);
79
- } else {
80
- AppLogger.warn(logMessage);
103
+ try {
104
+ await this.prisma.log.create({
105
+ data: {
106
+ method,
107
+ path: originalUrl,
108
+ statusCode,
109
+ messageCode: messageCode.toString(),
110
+ message,
111
+ headers: headers as any,
112
+ body: body as any,
113
+ query: query as any,
114
+ duration,
115
+ },
116
+ });
117
+ } catch (err) {
118
+ AppLogger.error("Failed to save log to DB: " + err.message);
81
119
  }
82
120
 
83
- response.status(status).json({
84
- statusCode: status,
121
+ return response.status(statusCode).json({
122
+ status: false,
123
+ statusCode,
85
124
  messageCode,
125
+ path: originalUrl,
86
126
  message,
127
+ validationErrors,
128
+ timestamp: moment().format("YYYY-MM-DD HH:mm:ss"),
87
129
  });
88
130
  }
89
131
  }
90
132
 
91
133
  function capitalizeFirst(text: string): string {
92
- if (!text || typeof text !== 'string') return text;
134
+ if (!text || typeof text !== "string") return text;
93
135
  return text.charAt(0).toUpperCase() + text.slice(1);
94
136
  }