From 2ebe1d1fd7bcf95c9394e7dd0b934f80c28e4f67 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 2 Aug 2024 18:13:12 +0900 Subject: [PATCH 1/8] Add detail error descriptions --- backend/src/documents/documents.service.ts | 10 +++++-- backend/src/files/files.service.ts | 20 +++++++++++--- .../src/intelligence/intelligence.service.ts | 6 ++++- backend/src/users/users.service.ts | 5 +++- .../workspace-documents.service.ts | 24 ++++++++++++++--- .../workspace-users.service.ts | 6 ++++- backend/src/workspaces/workspaces.service.ts | 26 +++++++++++++++---- 7 files changed, 79 insertions(+), 18 deletions(-) diff --git a/backend/src/documents/documents.service.ts b/backend/src/documents/documents.service.ts index 31d30300..2b251cbc 100644 --- a/backend/src/documents/documents.service.ts +++ b/backend/src/documents/documents.service.ts @@ -31,7 +31,10 @@ export class DocumentsService { throw new Error(); } } catch (e) { - throw new UnauthorizedException("Invalid sharing token"); + throw new UnauthorizedException("Unauthorized", { + cause: new Error(), + description: "Invalid sharing token", + }); } let document: Document; @@ -43,7 +46,10 @@ export class DocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: "Document not found", + }); } return { diff --git a/backend/src/files/files.service.ts b/backend/src/files/files.service.ts index c147bc65..01c0678b 100644 --- a/backend/src/files/files.service.ts +++ b/backend/src/files/files.service.ts @@ -46,11 +46,17 @@ export class FilesService { }, }); } catch (e) { - throw new UnauthorizedException(); + throw new UnauthorizedException("Unauthorized", { + cause: new Error(), + description: "Client unauthorized.", + }); } if (contentLength > 10_000_000) { - throw new UnprocessableEntityException(); + throw new UnprocessableEntityException("Unprocessable Entity", { + cause: new Error(), + description: "Content length too long.", + }); } const fileKey = `${workspace.slug}-${generateRandomKey()}.${contentType.split("/")[1]}`; @@ -75,7 +81,10 @@ export class FilesService { }); return getSignedUrl(this.s3Client, command, { expiresIn: 3600 }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: "File not found.", + }); } } @@ -92,7 +101,10 @@ export class FilesService { case "pdf": return this.exportToPdf(content, fileName); default: - throw new BadRequestException("Invalid export type"); + throw new BadRequestException("Bad request", { + cause: new Error(), + description: "Invalid export type", + }); } } diff --git a/backend/src/intelligence/intelligence.service.ts b/backend/src/intelligence/intelligence.service.ts index 44101862..ac12db17 100644 --- a/backend/src/intelligence/intelligence.service.ts +++ b/backend/src/intelligence/intelligence.service.ts @@ -25,7 +25,11 @@ export class IntelligenceService { }; const selectedPrompt = promptTemplates[feature]; - if (!selectedPrompt) throw new NotFoundException(); + if (!selectedPrompt) + throw new NotFoundException("Not found", { + cause: new Error(), + description: "Feature not found", + }); return selectedPrompt; } diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts index 04de8452..2fdae458 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -74,7 +74,10 @@ export class UsersService { const { conflict } = await this.checkService.checkNameConflict(nickname); if (conflict) { - throw new ConflictException(); + throw new ConflictException("Conflict", { + cause: new Error(), + description: "The nickname conflicts.", + }); } await this.prismaService.user.update({ diff --git a/backend/src/workspace-documents/workspace-documents.service.ts b/backend/src/workspace-documents/workspace-documents.service.ts index eed5f9a2..a586f805 100644 --- a/backend/src/workspace-documents/workspace-documents.service.ts +++ b/backend/src/workspace-documents/workspace-documents.service.ts @@ -26,7 +26,11 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: + "The workspace does not exist, or the user lacks the appropriate permissions.", + }); } return this.prismaService.document.create({ @@ -52,7 +56,11 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: + "The workspace does not exist, or the user lacks the appropriate permissions.", + }); } const additionalOptions: Prisma.DocumentFindManyArgs = {}; @@ -118,7 +126,11 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: + "The workspace or document does not exist, or the user lacks the appropriate permissions.", + }); } } @@ -146,7 +158,11 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: + "The workspace or document does not exist, or the user lacks the appropriate permissions.", + }); } const token = generateRandomKey(); diff --git a/backend/src/workspace-users/workspace-users.service.ts b/backend/src/workspace-users/workspace-users.service.ts index 5723aba1..bdffe763 100644 --- a/backend/src/workspace-users/workspace-users.service.ts +++ b/backend/src/workspace-users/workspace-users.service.ts @@ -21,7 +21,11 @@ export class WorkspaceUsersService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: + "The workspace does not exist, or the user lacks the appropriate permissions.", + }); } const additionalOptions: Prisma.UserFindManyArgs = {}; diff --git a/backend/src/workspaces/workspaces.service.ts b/backend/src/workspaces/workspaces.service.ts index 978f66d0..14eddd51 100644 --- a/backend/src/workspaces/workspaces.service.ts +++ b/backend/src/workspaces/workspaces.service.ts @@ -25,7 +25,10 @@ export class WorkspacesService { const { conflict } = await this.checkService.checkNameConflict(title); if (conflict) { - throw new ConflictException(); + throw new ConflictException("Conflict", { + cause: new Error(), + description: "Workspace title is already in use.", + }); } const workspace = await this.prismaService.workspace.create({ @@ -63,7 +66,10 @@ export class WorkspacesService { return foundWorkspace; } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: "Workspace not found, or the user lacks the appropriate permissions.", + }); } } @@ -114,7 +120,11 @@ export class WorkspacesService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Not found", { + cause: new Error(), + description: + "Worksapce does not exist, or the user lacks the appropriate permissions.", + }); } const token = generateRandomKey(); @@ -152,7 +162,10 @@ export class WorkspacesService { throw new Error(); } } catch (err) { - throw new UnauthorizedException("Invitation token is invalid or expired."); + throw new UnauthorizedException("Unauthorized", { + cause: new Error(), + description: "Invitation token is invalid or expired.", + }); } try { @@ -162,7 +175,10 @@ export class WorkspacesService { }, }); } catch (e) { - throw new NotFoundException("The workspace is deleted."); + throw new NotFoundException("Not found", { + cause: new Error(), + description: "The workspace is deleted.", + }); } const userWorkspace = await this.prismaService.userWorkspace.findFirst({ From 8273ca2fe084fda8380e093cf2df7f47507345de Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 2 Aug 2024 18:17:12 +0900 Subject: [PATCH 2/8] Fix exception occurrence --- .../src/workspace-documents/workspace-documents.service.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/workspace-documents/workspace-documents.service.ts b/backend/src/workspace-documents/workspace-documents.service.ts index a586f805..82b3b391 100644 --- a/backend/src/workspace-documents/workspace-documents.service.ts +++ b/backend/src/workspace-documents/workspace-documents.service.ts @@ -119,12 +119,12 @@ export class WorkspaceDocumentsService { workspaceId, }, }); - - return this.prismaService.document.findUniqueOrThrow({ + const document = await this.prismaService.document.findUniqueOrThrow({ where: { id: documentId, }, }); + return document; } catch (e) { throw new NotFoundException("Not found", { cause: new Error(), From 49aeb5917c99f3dce923cd38bd2e992ade4d858b Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 2 Aug 2024 18:20:50 +0900 Subject: [PATCH 3/8] Update JwtAuthGuard to handle token expiration and invalid token errors --- backend/src/auth/jwt.guard.ts | 48 ++++++++++++++++++++++++++++++++--- 1 file changed, 45 insertions(+), 3 deletions(-) diff --git a/backend/src/auth/jwt.guard.ts b/backend/src/auth/jwt.guard.ts index c33f292d..8d20980e 100644 --- a/backend/src/auth/jwt.guard.ts +++ b/backend/src/auth/jwt.guard.ts @@ -1,11 +1,21 @@ -import { ExecutionContext, Injectable } from "@nestjs/common"; +import { + ExecutionContext, + ForbiddenException, + Injectable, + UnauthorizedException, +} from "@nestjs/common"; import { Reflector } from "@nestjs/core"; import { AuthGuard } from "@nestjs/passport"; import { IS_PUBLIC_PATH } from "src/utils/decorators/auth.decorator"; +import * as jwt from "jsonwebtoken"; +import { ConfigService } from "@nestjs/config"; @Injectable() export class JwtAuthGuard extends AuthGuard("jwt") { - constructor(private reflector: Reflector) { + constructor( + private reflector: Reflector, + private configService: ConfigService + ) { super(); } @@ -17,6 +27,38 @@ export class JwtAuthGuard extends AuthGuard("jwt") { if (isPublic) { return true; } - return super.canActivate(context); + + try { + const request = context.switchToHttp().getRequest(); + const token = request.headers.authorization?.split(" ")[1]; + if (!token) { + throw new UnauthorizedException("Unauthorized", { + cause: new Error(), + description: "Token not found", + }); + } + + const secretKey = this.configService.get("JWT_AUTH_SECRET"); + const decoded = jwt.verify(token, secretKey); + request.user = decoded; + return super.canActivate(context); + } catch (e) { + if (e.name === "TokenExpiredError") { + throw new UnauthorizedException("Unauthorized", { + cause: new Error(), + description: "Token has expired.", + }); + } else if (e.name === "JsonWebTokenError") { + throw new UnauthorizedException("Unauthorized", { + cause: new Error(), + description: "Invalid token", + }); + } else { + throw new ForbiddenException("Forbidden", { + cause: new Error(), + description: "Access denied", + }); + } + } } } From 304f21beda6e99850e78d743b3d113c5deeaa524 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Fri, 2 Aug 2024 18:21:29 +0900 Subject: [PATCH 4/8] Update useErrorHandler to handle custom error responses --- frontend/src/hooks/useErrorHandler.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/frontend/src/hooks/useErrorHandler.ts b/frontend/src/hooks/useErrorHandler.ts index eb6b553f..33b1dd8b 100644 --- a/frontend/src/hooks/useErrorHandler.ts +++ b/frontend/src/hooks/useErrorHandler.ts @@ -1,11 +1,24 @@ import { useSnackbar } from "notistack"; import { useCallback } from "react"; +interface CustomError extends Error { + response?: { + data: { + error?: string; + message: string; + statusCode: number; + }; + }; +} + export function useErrorHandler() { const { enqueueSnackbar } = useSnackbar(); const handleError = useCallback( - (error: Error) => { - enqueueSnackbar(error.message || "Something went wrong...", { variant: "error" }); + (error: CustomError) => { + enqueueSnackbar( + error.response?.data.error || error.message || "Something went wrong...", + { variant: "error" } + ); }, [enqueueSnackbar] ); From 1e42017f2ff48ba778390ae6647001104cd745bb Mon Sep 17 00:00:00 2001 From: JunSeong Date: Sat, 3 Aug 2024 12:16:56 +0900 Subject: [PATCH 5/8] Update error handling and exception messages --- backend/src/documents/documents.service.ts | 10 ++---- backend/src/files/files.service.ts | 20 +++--------- .../src/intelligence/intelligence.service.ts | 6 +--- backend/src/users/users.service.ts | 5 +-- .../workspace-documents.service.ts | 32 +++++++------------ .../workspace-users.service.ts | 8 ++--- backend/src/workspaces/workspaces.service.ts | 30 ++++++----------- 7 files changed, 32 insertions(+), 79 deletions(-) diff --git a/backend/src/documents/documents.service.ts b/backend/src/documents/documents.service.ts index 2b251cbc..dec88286 100644 --- a/backend/src/documents/documents.service.ts +++ b/backend/src/documents/documents.service.ts @@ -31,10 +31,7 @@ export class DocumentsService { throw new Error(); } } catch (e) { - throw new UnauthorizedException("Unauthorized", { - cause: new Error(), - description: "Invalid sharing token", - }); + throw new UnauthorizedException("Invalid sharing token"); } let document: Document; @@ -46,10 +43,7 @@ export class DocumentsService { }, }); } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: "Document not found", - }); + throw new NotFoundException("Document not found"); } return { diff --git a/backend/src/files/files.service.ts b/backend/src/files/files.service.ts index 01c0678b..6c378d59 100644 --- a/backend/src/files/files.service.ts +++ b/backend/src/files/files.service.ts @@ -46,17 +46,11 @@ export class FilesService { }, }); } catch (e) { - throw new UnauthorizedException("Unauthorized", { - cause: new Error(), - description: "Client unauthorized.", - }); + throw new UnauthorizedException("Client unauthorized."); } if (contentLength > 10_000_000) { - throw new UnprocessableEntityException("Unprocessable Entity", { - cause: new Error(), - description: "Content length too long.", - }); + throw new UnprocessableEntityException("Content length too long."); } const fileKey = `${workspace.slug}-${generateRandomKey()}.${contentType.split("/")[1]}`; @@ -81,10 +75,7 @@ export class FilesService { }); return getSignedUrl(this.s3Client, command, { expiresIn: 3600 }); } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: "File not found.", - }); + throw new NotFoundException("File not found."); } } @@ -101,10 +92,7 @@ export class FilesService { case "pdf": return this.exportToPdf(content, fileName); default: - throw new BadRequestException("Bad request", { - cause: new Error(), - description: "Invalid export type", - }); + throw new BadRequestException("Invalid export type"); } } diff --git a/backend/src/intelligence/intelligence.service.ts b/backend/src/intelligence/intelligence.service.ts index ac12db17..ceb1bfcc 100644 --- a/backend/src/intelligence/intelligence.service.ts +++ b/backend/src/intelligence/intelligence.service.ts @@ -25,11 +25,7 @@ export class IntelligenceService { }; const selectedPrompt = promptTemplates[feature]; - if (!selectedPrompt) - throw new NotFoundException("Not found", { - cause: new Error(), - description: "Feature not found", - }); + if (!selectedPrompt) throw new NotFoundException("Feature not found"); return selectedPrompt; } diff --git a/backend/src/users/users.service.ts b/backend/src/users/users.service.ts index 2fdae458..edc1fce6 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -74,10 +74,7 @@ export class UsersService { const { conflict } = await this.checkService.checkNameConflict(nickname); if (conflict) { - throw new ConflictException("Conflict", { - cause: new Error(), - description: "The nickname conflicts.", - }); + throw new ConflictException("The nickname conflicts."); } await this.prismaService.user.update({ diff --git a/backend/src/workspace-documents/workspace-documents.service.ts b/backend/src/workspace-documents/workspace-documents.service.ts index 82b3b391..444b3333 100644 --- a/backend/src/workspace-documents/workspace-documents.service.ts +++ b/backend/src/workspace-documents/workspace-documents.service.ts @@ -26,11 +26,9 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: - "The workspace does not exist, or the user lacks the appropriate permissions.", - }); + throw new NotFoundException( + "The workspace does not exist, or the user lacks the appropriate permissions." + ); } return this.prismaService.document.create({ @@ -56,11 +54,9 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: - "The workspace does not exist, or the user lacks the appropriate permissions.", - }); + throw new NotFoundException( + "The workspace does not exist, or the user lacks the appropriate permissions." + ); } const additionalOptions: Prisma.DocumentFindManyArgs = {}; @@ -126,11 +122,9 @@ export class WorkspaceDocumentsService { }); return document; } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: - "The workspace or document does not exist, or the user lacks the appropriate permissions.", - }); + throw new NotFoundException( + "The workspace or document does not exist, or the user lacks the appropriate permissions." + ); } } @@ -158,11 +152,9 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: - "The workspace or document does not exist, or the user lacks the appropriate permissions.", - }); + throw new NotFoundException( + "The workspace or document does not exist, or the user lacks the appropriate permissions." + ); } const token = generateRandomKey(); diff --git a/backend/src/workspace-users/workspace-users.service.ts b/backend/src/workspace-users/workspace-users.service.ts index bdffe763..dd2ff0bc 100644 --- a/backend/src/workspace-users/workspace-users.service.ts +++ b/backend/src/workspace-users/workspace-users.service.ts @@ -21,11 +21,9 @@ export class WorkspaceUsersService { }, }); } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: - "The workspace does not exist, or the user lacks the appropriate permissions.", - }); + throw new NotFoundException( + "The workspace does not exist, or the user lacks the appropriate permissions." + ); } const additionalOptions: Prisma.UserFindManyArgs = {}; diff --git a/backend/src/workspaces/workspaces.service.ts b/backend/src/workspaces/workspaces.service.ts index 14eddd51..8c3f39cf 100644 --- a/backend/src/workspaces/workspaces.service.ts +++ b/backend/src/workspaces/workspaces.service.ts @@ -25,10 +25,7 @@ export class WorkspacesService { const { conflict } = await this.checkService.checkNameConflict(title); if (conflict) { - throw new ConflictException("Conflict", { - cause: new Error(), - description: "Workspace title is already in use.", - }); + throw new ConflictException("Workspace title is already in use."); } const workspace = await this.prismaService.workspace.create({ @@ -66,10 +63,9 @@ export class WorkspacesService { return foundWorkspace; } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: "Workspace not found, or the user lacks the appropriate permissions.", - }); + throw new NotFoundException( + "Workspace not found, or the user lacks the appropriate permissions." + ); } } @@ -120,11 +116,9 @@ export class WorkspacesService { }, }); } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: - "Worksapce does not exist, or the user lacks the appropriate permissions.", - }); + throw new NotFoundException( + "Worksapce does not exist, or the user lacks the appropriate permissions." + ); } const token = generateRandomKey(); @@ -162,10 +156,7 @@ export class WorkspacesService { throw new Error(); } } catch (err) { - throw new UnauthorizedException("Unauthorized", { - cause: new Error(), - description: "Invitation token is invalid or expired.", - }); + throw new UnauthorizedException("Invitation token is invalid or expired."); } try { @@ -175,10 +166,7 @@ export class WorkspacesService { }, }); } catch (e) { - throw new NotFoundException("Not found", { - cause: new Error(), - description: "The workspace is deleted.", - }); + throw new NotFoundException("The workspace is deleted."); } const userWorkspace = await this.prismaService.userWorkspace.findFirst({ From 1e2907a92ae03ae85cdf0d09b210cc0f4ff82d5c Mon Sep 17 00:00:00 2001 From: JunSeong Date: Sat, 3 Aug 2024 12:18:15 +0900 Subject: [PATCH 6/8] Update error handler to handle custom error response --- frontend/src/hooks/useErrorHandler.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/frontend/src/hooks/useErrorHandler.ts b/frontend/src/hooks/useErrorHandler.ts index 33b1dd8b..82549f2d 100644 --- a/frontend/src/hooks/useErrorHandler.ts +++ b/frontend/src/hooks/useErrorHandler.ts @@ -4,8 +4,8 @@ import { useCallback } from "react"; interface CustomError extends Error { response?: { data: { - error?: string; - message: string; + error: string; + message?: string; statusCode: number; }; }; @@ -15,8 +15,9 @@ export function useErrorHandler() { const { enqueueSnackbar } = useSnackbar(); const handleError = useCallback( (error: CustomError) => { + console.error(error); enqueueSnackbar( - error.response?.data.error || error.message || "Something went wrong...", + error.response?.data.message || error.message || "Something went wrong...", { variant: "error" } ); }, From a66bd78812c1d0622dca50daaffd48468566ac87 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Sat, 3 Aug 2024 12:19:55 +0900 Subject: [PATCH 7/8] Update JwtAuthGuard to handle exception with handleRequest --- backend/src/auth/jwt.guard.ts | 47 +++++++++-------------------------- 1 file changed, 12 insertions(+), 35 deletions(-) diff --git a/backend/src/auth/jwt.guard.ts b/backend/src/auth/jwt.guard.ts index 8d20980e..077eddb6 100644 --- a/backend/src/auth/jwt.guard.ts +++ b/backend/src/auth/jwt.guard.ts @@ -7,15 +7,11 @@ import { import { Reflector } from "@nestjs/core"; import { AuthGuard } from "@nestjs/passport"; import { IS_PUBLIC_PATH } from "src/utils/decorators/auth.decorator"; -import * as jwt from "jsonwebtoken"; -import { ConfigService } from "@nestjs/config"; +import { AuthorizedUser } from "src/utils/types/req.type"; @Injectable() export class JwtAuthGuard extends AuthGuard("jwt") { - constructor( - private reflector: Reflector, - private configService: ConfigService - ) { + constructor(private reflector: Reflector) { super(); } @@ -27,38 +23,19 @@ export class JwtAuthGuard extends AuthGuard("jwt") { if (isPublic) { return true; } + return super.canActivate(context); + } - try { - const request = context.switchToHttp().getRequest(); - const token = request.headers.authorization?.split(" ")[1]; - if (!token) { - throw new UnauthorizedException("Unauthorized", { - cause: new Error(), - description: "Token not found", - }); - } - - const secretKey = this.configService.get("JWT_AUTH_SECRET"); - const decoded = jwt.verify(token, secretKey); - request.user = decoded; - return super.canActivate(context); - } catch (e) { - if (e.name === "TokenExpiredError") { - throw new UnauthorizedException("Unauthorized", { - cause: new Error(), - description: "Token has expired.", - }); - } else if (e.name === "JsonWebTokenError") { - throw new UnauthorizedException("Unauthorized", { - cause: new Error(), - description: "Invalid token", - }); + handleRequest(err: Error, user: User, info: Error): User { + if (err || !user) { + if (info?.name === "TokenExpiredError") { + throw new UnauthorizedException("Token has expired."); + } else if (info?.name === "JsonWebTokenError") { + throw new UnauthorizedException("Invalid token"); } else { - throw new ForbiddenException("Forbidden", { - cause: new Error(), - description: "Access denied", - }); + throw err || new ForbiddenException("Access denied"); } } + return user; } } From b0d8ebe573d7e59ffaffced1c40762d7cea515c3 Mon Sep 17 00:00:00 2001 From: JunSeong Date: Sat, 3 Aug 2024 15:25:22 +0900 Subject: [PATCH 8/8] Remove console.error in useErrorHandler --- frontend/src/hooks/useErrorHandler.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/frontend/src/hooks/useErrorHandler.ts b/frontend/src/hooks/useErrorHandler.ts index 82549f2d..768820d7 100644 --- a/frontend/src/hooks/useErrorHandler.ts +++ b/frontend/src/hooks/useErrorHandler.ts @@ -15,7 +15,6 @@ export function useErrorHandler() { const { enqueueSnackbar } = useSnackbar(); const handleError = useCallback( (error: CustomError) => { - console.error(error); enqueueSnackbar( error.response?.data.message || error.message || "Something went wrong...", { variant: "error" }