diff --git a/src/app.module.ts b/src/app.module.ts index 66b9a5c..9410167 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,10 +1,11 @@ import { CacheModule } from "@nestjs/cache-manager"; -import { Module } from "@nestjs/common"; +import { MiddlewareConsumer, Module, NestModule } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; import { TypeOrmModule } from "@nestjs/typeorm"; import { cacheConfig } from "./configs/cache.config"; import { DatabaseConfigs } from "./configs/typeorm.config"; +import { HTTPLogger } from "./core/middlewares/logger.middleware"; import { TicketsModule } from "./modules/tickets/tickets.module"; import { UsersModule } from "./modules/users/users.module"; @@ -19,4 +20,8 @@ import { UsersModule } from "./modules/users/users.module"; controllers: [], providers: [], }) -export class AppModule {} +export class AppModule implements NestModule { + configure(consumer: MiddlewareConsumer) { + consumer.apply(HTTPLogger).forRoutes("*"); + } +} diff --git a/src/core/exceptions/http.exceptions.ts b/src/core/exceptions/http.exceptions.ts new file mode 100644 index 0000000..2b7db3e --- /dev/null +++ b/src/core/exceptions/http.exceptions.ts @@ -0,0 +1,37 @@ +import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from "@nestjs/common"; +import { FastifyReply, FastifyRequest } from "fastify"; + +@Catch(HttpException) +export class HttpExceptionFilter implements ExceptionFilter { + catch(exception: HttpException, host: ArgumentsHost) { + const ctx = host.switchToHttp(); + const reply = ctx.getResponse(); + const request = ctx.getRequest(); + const status = exception.getStatus() || HttpStatus.INTERNAL_SERVER_ERROR; + + const exceptionResponse = exception.getResponse(); + const message = this.extractMessage(exceptionResponse); + + reply.status(status).send({ + statusCode: status, + success: false, + error: { + message, + // timestamp: new Date().toISOString(), + path: request.url, + }, + }); + } + + private extractMessage(response: string | Record): string | string[] { + if (typeof response === "string") { + return [response]; + } + + if (typeof response === "object" && "message" in response) { + return Array.isArray(response.message) ? response.message : [response.message]; + } + + return "something wrong"; + } +} diff --git a/src/core/interceptors/response.interceptor.ts b/src/core/interceptors/response.interceptor.ts index 81ff800..a9ad8c8 100644 --- a/src/core/interceptors/response.interceptor.ts +++ b/src/core/interceptors/response.interceptor.ts @@ -1,5 +1,5 @@ import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from "@nestjs/common"; -import { Response } from "express"; +import { FastifyReply } from "fastify"; import { Observable } from "rxjs"; import { map } from "rxjs/operators"; @@ -7,7 +7,7 @@ import { map } from "rxjs/operators"; export class ResponseInterceptor implements NestInterceptor { // intercept(context: ExecutionContext, next: CallHandler>): Observable { - const ctx = context.switchToHttp().getResponse(); + const ctx = context.switchToHttp().getResponse(); const statusCode = ctx.statusCode; return next.handle().pipe( diff --git a/src/core/middlewares/logger.middleware.ts b/src/core/middlewares/logger.middleware.ts index 13b6c62..7234e54 100644 --- a/src/core/middlewares/logger.middleware.ts +++ b/src/core/middlewares/logger.middleware.ts @@ -1,24 +1,26 @@ -// import { Injectable, Logger, NestMiddleware } from "@nestjs/common"; -// import { FastifyReply, FastifyRequest } from "fastify"; +import { Injectable, Logger, NestMiddleware } from "@nestjs/common"; +import { FastifyReply, FastifyRequest } from "fastify"; -// @Injectable() -// export class HTTPLoggerMiddleware implements NestMiddleware { -// private readonly logger = new Logger(HTTPLoggerMiddleware.name); -// // use(req: any, res: any, next: (error?: Error | any) => void) {} +@Injectable() +export class HTTPLogger implements NestMiddleware { + private readonly logger = new Logger(HTTPLogger.name); + // use(req: any, res: any, next: (error?: Error | any) => void) {} -// use(req: FastifyRequest, res: FastifyReply, next: NextFunction) { -// const { ip, method, baseUrl } = req; -// const userAgent = req.get("user-agent") || ""; -// const startAt = process.hrtime(); + use(req: FastifyRequest, rep: FastifyReply["raw"], next: () => void) { + const { ip, method, originalUrl } = req; + const userAgent = req.headers["user-agent"] || ""; + const startAt = process.hrtime(); -// res.on("finish", () => { -// const { statusCode } = res; -// const contentLength = res.get("content-length") || 0; -// const dif = process.hrtime(startAt); -// const responseTime = dif[0] * 1e3 + dif[1] * 1e-6; -// this.logger.log(`${method} - ${baseUrl} - ${statusCode} - ${contentLength} - ${userAgent} - ${ip} - ${responseTime.toFixed(2)}ms`); -// }); + rep.on("finish", () => { + const { statusCode } = rep; + const contentLength = rep.getHeader("content-length") || 0; + const dif = process.hrtime(startAt); + const responseTime = dif[0] * 1e3 + dif[1] * 1e-6; + this.logger.log( + `${method} - ${originalUrl} - ${statusCode} - ${contentLength} - ${userAgent} - ${ip} - ${responseTime.toFixed(2)}ms`, + ); + }); -// next(); -// } -// } + next(); + } +} diff --git a/src/main.ts b/src/main.ts index 9e362ed..573f1cc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -5,12 +5,17 @@ import { FastifyAdapter, NestFastifyApplication } from "@nestjs/platform-fastify import { AppModule } from "./app.module"; import { getSwaggerDocument } from "./configs/swagger.config"; +import { HttpExceptionFilter } from "./core/exceptions/http.exceptions"; +import { PaginationInterceptor } from "./core/interceptors/pagination.interceptor"; +import { ResponseInterceptor } from "./core/interceptors/response.interceptor"; async function bootstrap() { const logger = new Logger("APP"); const app = await NestFactory.create(AppModule, new FastifyAdapter()); app.useGlobalPipes(new ValidationPipe({ transform: true, whitelist: true })); + app.useGlobalInterceptors(new ResponseInterceptor(), new PaginationInterceptor()); + app.useGlobalFilters(new HttpExceptionFilter()); app.enableCors({ origin: true,