diff --git a/backend/src/auth/jwt.guard.ts b/backend/src/auth/jwt.guard.ts index c33f292d..077eddb6 100644 --- a/backend/src/auth/jwt.guard.ts +++ b/backend/src/auth/jwt.guard.ts @@ -1,7 +1,13 @@ -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 { AuthorizedUser } from "src/utils/types/req.type"; @Injectable() export class JwtAuthGuard extends AuthGuard("jwt") { @@ -19,4 +25,17 @@ export class JwtAuthGuard extends AuthGuard("jwt") { } return super.canActivate(context); } + + 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 err || new ForbiddenException("Access denied"); + } + } + return user; + } } diff --git a/backend/src/documents/documents.service.ts b/backend/src/documents/documents.service.ts index 31d30300..dec88286 100644 --- a/backend/src/documents/documents.service.ts +++ b/backend/src/documents/documents.service.ts @@ -43,7 +43,7 @@ export class DocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("Document not found"); } return { diff --git a/backend/src/files/files.service.ts b/backend/src/files/files.service.ts index c147bc65..6c378d59 100644 --- a/backend/src/files/files.service.ts +++ b/backend/src/files/files.service.ts @@ -46,11 +46,11 @@ export class FilesService { }, }); } catch (e) { - throw new UnauthorizedException(); + throw new UnauthorizedException("Client unauthorized."); } if (contentLength > 10_000_000) { - throw new UnprocessableEntityException(); + throw new UnprocessableEntityException("Content length too long."); } const fileKey = `${workspace.slug}-${generateRandomKey()}.${contentType.split("/")[1]}`; @@ -75,7 +75,7 @@ export class FilesService { }); return getSignedUrl(this.s3Client, command, { expiresIn: 3600 }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException("File not found."); } } diff --git a/backend/src/intelligence/intelligence.service.ts b/backend/src/intelligence/intelligence.service.ts index 44101862..ceb1bfcc 100644 --- a/backend/src/intelligence/intelligence.service.ts +++ b/backend/src/intelligence/intelligence.service.ts @@ -25,7 +25,7 @@ export class IntelligenceService { }; const selectedPrompt = promptTemplates[feature]; - if (!selectedPrompt) throw new NotFoundException(); + 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 c44a7369..36f7f034 100644 --- a/backend/src/users/users.service.ts +++ b/backend/src/users/users.service.ts @@ -73,7 +73,7 @@ export class UsersService { const { conflict } = await this.checkService.checkNameConflict(nickname); if (conflict) { - throw new ConflictException(); + 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 eed5f9a2..444b3333 100644 --- a/backend/src/workspace-documents/workspace-documents.service.ts +++ b/backend/src/workspace-documents/workspace-documents.service.ts @@ -26,7 +26,9 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException( + "The workspace does not exist, or the user lacks the appropriate permissions." + ); } return this.prismaService.document.create({ @@ -52,7 +54,9 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException( + "The workspace does not exist, or the user lacks the appropriate permissions." + ); } const additionalOptions: Prisma.DocumentFindManyArgs = {}; @@ -111,14 +115,16 @@ 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(); + throw new NotFoundException( + "The workspace or document does not exist, or the user lacks the appropriate permissions." + ); } } @@ -146,7 +152,9 @@ export class WorkspaceDocumentsService { }, }); } catch (e) { - throw new NotFoundException(); + 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 5723aba1..dd2ff0bc 100644 --- a/backend/src/workspace-users/workspace-users.service.ts +++ b/backend/src/workspace-users/workspace-users.service.ts @@ -21,7 +21,9 @@ export class WorkspaceUsersService { }, }); } catch (e) { - throw new NotFoundException(); + 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 df4c9035..6d9a6cce 100644 --- a/backend/src/workspaces/workspaces.service.ts +++ b/backend/src/workspaces/workspaces.service.ts @@ -24,7 +24,7 @@ export class WorkspacesService { const { conflict } = await this.checkService.checkNameConflict(title); if (conflict) { - throw new ConflictException(); + throw new ConflictException("Workspace title is already in use."); } const workspace = await this.prismaService.workspace.create({ @@ -62,7 +62,9 @@ export class WorkspacesService { return foundWorkspace; } catch (e) { - throw new NotFoundException(); + throw new NotFoundException( + "Workspace not found, or the user lacks the appropriate permissions." + ); } } @@ -113,7 +115,9 @@ export class WorkspacesService { }, }); } catch (e) { - throw new NotFoundException(); + throw new NotFoundException( + "Worksapce does not exist, or the user lacks the appropriate permissions." + ); } const token = generateRandomKey(); diff --git a/frontend/src/hooks/useErrorHandler.ts b/frontend/src/hooks/useErrorHandler.ts index eb6b553f..768820d7 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.message || error.message || "Something went wrong...", + { variant: "error" } + ); }, [enqueueSnackbar] );