91 lines
2.6 KiB
TypeScript
91 lines
2.6 KiB
TypeScript
import {
|
|
ArgumentsHost,
|
|
Catch,
|
|
ExceptionFilter,
|
|
ForbiddenException,
|
|
Logger,
|
|
MethodNotAllowedException,
|
|
NotFoundException,
|
|
UnauthorizedException,
|
|
} from '@nestjs/common';
|
|
import { FastifyReply, FastifyRequest } from 'fastify';
|
|
import { ApiErrorResponse } from 'picsur-shared/dist/dto/api/api.dto';
|
|
import {
|
|
Fail,
|
|
Failure,
|
|
FT,
|
|
IsFailure,
|
|
} from 'picsur-shared/dist/types/failable';
|
|
|
|
// This will catch any exception that is made in any request
|
|
// (As long as its within nest, the earlier fastify stages are not handled here)
|
|
// It neatly wraps the error for easier handling on the client
|
|
|
|
@Catch()
|
|
export class MainExceptionFilter implements ExceptionFilter {
|
|
private static readonly logger = new Logger(MainExceptionFilter.name);
|
|
|
|
catch(exception: Failure, host: ArgumentsHost) {
|
|
const ctx = host.switchToHttp();
|
|
const response = ctx.getResponse<FastifyReply>();
|
|
const request = ctx.getRequest<FastifyRequest>();
|
|
|
|
const traceString = `(${request.ip} -> ${request.method} ${request.url})`;
|
|
|
|
if (!IsFailure(exception)) {
|
|
exception = this.transformKnownExceptions(exception);
|
|
}
|
|
|
|
const status = exception.getCode();
|
|
const type = exception.getType();
|
|
|
|
const message = exception.getReason();
|
|
const logmessage =
|
|
message +
|
|
(exception.getDebugMessage() ? ' - ' + exception.getDebugMessage() : '');
|
|
|
|
if (exception.isImportant()) {
|
|
MainExceptionFilter.logger.error(
|
|
`${traceString} ${exception.getName()}: ${logmessage}`,
|
|
);
|
|
if (exception.getStack()) {
|
|
MainExceptionFilter.logger.debug(exception.getStack());
|
|
}
|
|
} else {
|
|
MainExceptionFilter.logger.warn(
|
|
`${traceString} ${exception.getName()}: ${logmessage}`,
|
|
);
|
|
}
|
|
|
|
const toSend: ApiErrorResponse = {
|
|
success: false,
|
|
statusCode: status,
|
|
timestamp: new Date().toISOString(),
|
|
timeMs: Math.round(response.getResponseTime()),
|
|
|
|
data: {
|
|
type,
|
|
message,
|
|
},
|
|
};
|
|
|
|
response.status(status).send(toSend);
|
|
}
|
|
|
|
private transformKnownExceptions(exception: any): Failure {
|
|
if (exception instanceof UnauthorizedException) {
|
|
return Fail(FT.Permission, exception);
|
|
} else if (exception instanceof ForbiddenException) {
|
|
return Fail(FT.Permission, exception);
|
|
} else if (exception instanceof NotFoundException) {
|
|
return Fail(FT.RouteNotFound, exception);
|
|
} else if (exception instanceof MethodNotAllowedException) {
|
|
return Fail(FT.RouteNotFound, exception);
|
|
} else if (exception instanceof Error) {
|
|
return Fail(FT.Internal, exception);
|
|
} else {
|
|
return Fail(FT.Unknown, exception);
|
|
}
|
|
}
|
|
}
|