init
This commit is contained in:
Executable
+48
@@ -0,0 +1,48 @@
|
||||
import { ArgumentsHost, Catch, ExceptionFilter, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { FastifyReply } from 'fastify';
|
||||
|
||||
@Catch(HttpException)
|
||||
export class HttpExceptionFilter implements ExceptionFilter {
|
||||
catch(exception: HttpException, host: ArgumentsHost) {
|
||||
// Handle HTTP/REST context
|
||||
try {
|
||||
const ctx = host.switchToHttp();
|
||||
const reply = ctx.getResponse<FastifyReply>();
|
||||
|
||||
// Safety check: ensure reply has the status method
|
||||
if (!reply || typeof reply.status !== 'function') {
|
||||
return;
|
||||
}
|
||||
|
||||
// const request = ctx.getRequest<FastifyRequest>();
|
||||
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,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
// If we can't handle it, let it propagate
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private extractMessage(response: string | Record<string, any>): 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';
|
||||
}
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
// import { CallHandler, ExecutionContext, Injectable, Logger, NestInterceptor } from '@nestjs/common';
|
||||
// import { FastifyRequest } from 'fastify';
|
||||
// import { Observable, map } from 'rxjs';
|
||||
|
||||
// import { IPageFormat } from '../../../../danak_dsc_api/src/common/interfaces/IPagination';
|
||||
|
||||
// @Injectable()
|
||||
// export class PaginationInterceptor implements NestInterceptor {
|
||||
// private readonly logger = new Logger(PaginationInterceptor.name);
|
||||
|
||||
// intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
|
||||
// const request = context.switchToHttp().getRequest<FastifyRequest>();
|
||||
// const query = request.query as { page: string; limit: string };
|
||||
|
||||
// const page = parseInt(query.page, 10) || 1;
|
||||
// const limit = parseInt(query.limit, 10) || 10;
|
||||
|
||||
// const className = context.getClass().name;
|
||||
// const handlerName = context.getHandler().name;
|
||||
|
||||
// return next.handle().pipe(
|
||||
// map(data => {
|
||||
// if (data && (data.paginate || data.count)) {
|
||||
// const { count, paginate, ...response } = data;
|
||||
// this.logger.log(`paginate response from ${className}.${handlerName}`);
|
||||
// const pager = this.formatPage(page, limit, count, request);
|
||||
|
||||
// return {
|
||||
// pager,
|
||||
// ...response,
|
||||
// };
|
||||
// }
|
||||
|
||||
// return data;
|
||||
// }),
|
||||
// );
|
||||
// }
|
||||
|
||||
// private formatPage(page: number, limit: number, totalItems: number, request: FastifyRequest): IPageFormat {
|
||||
// const totalPages = Math.ceil(totalItems / limit);
|
||||
// const prevPage = page === 1 ? false : page - 1;
|
||||
// const nextPage = page >= totalPages ? false : page + 1;
|
||||
|
||||
// const formatLink = (pageNum: number | boolean): string | boolean => {
|
||||
// if (!pageNum) return false;
|
||||
// const protocol = request.protocol;
|
||||
// const hostname = request.hostname;
|
||||
// const originalUrl = request.url;
|
||||
// return `${protocol}://${hostname}${originalUrl.split('?')[0]}?page=${pageNum}&limit=${limit}`;
|
||||
// };
|
||||
|
||||
// return {
|
||||
// page,
|
||||
// limit,
|
||||
// totalItems,
|
||||
// totalPages,
|
||||
// prevPage: formatLink(prevPage),
|
||||
// nextPage: formatLink(nextPage),
|
||||
// };
|
||||
// }
|
||||
// }
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from '@nestjs/common';
|
||||
import { FastifyReply } from 'fastify';
|
||||
import { Observable } from 'rxjs';
|
||||
import { map } from 'rxjs/operators';
|
||||
|
||||
@Injectable()
|
||||
export class ResponseInterceptor implements NestInterceptor {
|
||||
constructor() { }
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler<Record<string, unknown>>): Observable<unknown> {
|
||||
// For REST endpoints, wrap the response
|
||||
try {
|
||||
const ctx = context.switchToHttp().getResponse<FastifyReply>();
|
||||
const statusCode = ctx.statusCode;
|
||||
|
||||
// If statusCode is undefined, skip wrapping
|
||||
if (statusCode === undefined) {
|
||||
return next.handle();
|
||||
}
|
||||
|
||||
return next.handle().pipe(
|
||||
map(data => {
|
||||
// If data is already wrapped or doesn't need wrapping, return as-is
|
||||
if (data && typeof data === 'object' && 'data' in data && 'success' in data) {
|
||||
console.log('data', data)
|
||||
|
||||
return {
|
||||
data: data,
|
||||
...('nextCursor' in (data as any) && (data as any).nextCursor != null
|
||||
? { nextCursor: (data as any).nextCursor }
|
||||
: {}),
|
||||
}
|
||||
}
|
||||
|
||||
// Check if this is a paginated result (has both data and meta properties)
|
||||
if (data && typeof data === 'object' && 'data' in data && 'meta' in data) {
|
||||
return {
|
||||
statusCode,
|
||||
success: true,
|
||||
data: data.data,
|
||||
meta: data.meta,
|
||||
};
|
||||
}
|
||||
|
||||
if (data && 'data' in data) {
|
||||
|
||||
return {
|
||||
statusCode,
|
||||
success: true,
|
||||
data: data.data,
|
||||
...('nextCursor' in data
|
||||
? { nextCursor: (data as any).nextCursor }
|
||||
: {}),
|
||||
};
|
||||
}
|
||||
return {
|
||||
statusCode,
|
||||
success: true,
|
||||
data,
|
||||
};
|
||||
}),
|
||||
);
|
||||
} catch {
|
||||
// If we can't get HTTP context, return data as-is
|
||||
return next.handle();
|
||||
}
|
||||
}
|
||||
}
|
||||
Executable
+26
@@ -0,0 +1,26 @@
|
||||
import { Injectable, Logger, NestMiddleware } from '@nestjs/common';
|
||||
import { FastifyReply, FastifyRequest } from 'fastify';
|
||||
|
||||
@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, rep: FastifyReply['raw'], next: () => void) {
|
||||
const { ip, method, originalUrl } = req;
|
||||
const userAgent = req.headers['user-agent'] || '';
|
||||
const startAt = process.hrtime();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user