chore: refacto NestJS in modules (#308)
* chore: wip refacto in modules * fix: rollback port * fix: jwt guard in wrong folder * chore: rename folder exception-filter in filters * fix: tests are running * fix: excessive stack depth comparing types * fix: auth issue * chore: move createUser in UserService * fix: test * fix: guards * fix: jwt guard don't handle falsy user
This commit is contained in:
105
server/src/guards/create-one-comment-thread.guard.ts
Normal file
105
server/src/guards/create-one-comment-thread.guard.ts
Normal file
@ -0,0 +1,105 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateOneCommentThreadGuard implements CanActivate {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
// TODO: type request
|
||||
const request = gqlContext.getContext().req;
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const targets = args.data?.commentThreadTargets?.createMany?.data;
|
||||
const comments = args.data?.comments?.createMany?.data;
|
||||
const workspace = request.user.workspace;
|
||||
|
||||
if (!targets || targets.length === 0) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Missing commentThreadTargets' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
await targets.map(async (target) => {
|
||||
if (!target.commentableId || !target.commentableType) {
|
||||
throw new HttpException(
|
||||
{
|
||||
reason:
|
||||
'Missing commentThreadTarget.commentableId or commentThreadTarget.commentableType',
|
||||
},
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
if (!['Person', 'Company'].includes(target.commentableType)) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Invalid commentThreadTarget.commentableType' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const targetEntity = await this.prismaService[
|
||||
target.commentableType
|
||||
].findUnique({
|
||||
where: { id: target.commentableId },
|
||||
});
|
||||
|
||||
if (!targetEntity || targetEntity.workspaceId !== workspace.id) {
|
||||
throw new HttpException(
|
||||
{ reason: 'CommentThreadTarget not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
if (!comments) {
|
||||
return true;
|
||||
}
|
||||
|
||||
await comments.map(async (comment) => {
|
||||
if (!comment.authorId) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Missing comment.authorId' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const author = await this.prismaService.user.findUnique({
|
||||
where: { id: comment.authorId },
|
||||
});
|
||||
|
||||
if (!author) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Comment.authorId not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const userWorkspaceMember =
|
||||
await this.prismaService.workspaceMember.findFirst({
|
||||
where: { userId: author.id },
|
||||
});
|
||||
|
||||
if (
|
||||
!userWorkspaceMember ||
|
||||
userWorkspaceMember.workspaceId !== workspace.id
|
||||
) {
|
||||
throw new HttpException(
|
||||
{ reason: 'userWorkspaceMember.workspaceId not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
71
server/src/guards/create-one-comment.guard.ts
Normal file
71
server/src/guards/create-one-comment.guard.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateOneCommentGuard implements CanActivate {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
const authorId = args.data?.author?.connect?.id;
|
||||
const commentThreadId = args.data?.commentThread?.connect?.id;
|
||||
|
||||
if (!authorId || !commentThreadId) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Missing author or commentThread' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const author = await this.prismaService.user.findUnique({
|
||||
where: { id: authorId },
|
||||
});
|
||||
|
||||
const commentThread = await this.prismaService.commentThread.findUnique({
|
||||
where: { id: commentThreadId },
|
||||
});
|
||||
|
||||
if (!author || !commentThread) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Author or commentThread not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const userWorkspaceMember =
|
||||
await this.prismaService.workspaceMember.findFirst({
|
||||
where: { userId: author.id },
|
||||
});
|
||||
|
||||
if (!userWorkspaceMember) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Author or commentThread not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const workspace = request.user.workspace;
|
||||
|
||||
if (
|
||||
userWorkspaceMember.workspaceId !== workspace.id ||
|
||||
commentThread.workspaceId !== workspace.id
|
||||
) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Author or commentThread not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
12
server/src/guards/create-one.guard.ts
Normal file
12
server/src/guards/create-one.guard.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { CanActivate, Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class CreateOneGuard implements CanActivate {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
async canActivate(): Promise<boolean> {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
}
|
||||
12
server/src/guards/delete-many.guard.ts
Normal file
12
server/src/guards/delete-many.guard.ts
Normal file
@ -0,0 +1,12 @@
|
||||
import { CanActivate, Injectable } from '@nestjs/common';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class DeleteManyGuard implements CanActivate {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
async canActivate(): Promise<boolean> {
|
||||
// TODO
|
||||
return true;
|
||||
}
|
||||
}
|
||||
40
server/src/guards/jwt.auth.guard.ts
Normal file
40
server/src/guards/jwt.auth.guard.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import {
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { JsonWebTokenError } from 'jsonwebtoken';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { getRequest } from 'src/utils/extract-request';
|
||||
|
||||
@Injectable()
|
||||
export class JwtAuthGuard extends AuthGuard(['jwt']) {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
getRequest(context: ExecutionContext) {
|
||||
const request = getRequest(context);
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
handleRequest(err: any, user: any, info: any) {
|
||||
assert(user, '', UnauthorizedException);
|
||||
|
||||
if (err) {
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (info && info instanceof Error) {
|
||||
if (info instanceof JsonWebTokenError) {
|
||||
info = String(info);
|
||||
}
|
||||
|
||||
throw new UnauthorizedException(info);
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
49
server/src/guards/update-one.guard.ts
Normal file
49
server/src/guards/update-one.guard.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
HttpException,
|
||||
HttpStatus,
|
||||
Injectable,
|
||||
} from '@nestjs/common';
|
||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
|
||||
@Injectable()
|
||||
export class UpdateOneGuard implements CanActivate {
|
||||
constructor(private prismaService: PrismaService) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const gqlContext = GqlExecutionContext.create(context);
|
||||
const request = gqlContext.getContext().req;
|
||||
const entity = gqlContext.getArgByIndex(3).returnType?.name;
|
||||
const args = gqlContext.getArgs();
|
||||
|
||||
if (!entity || !args.where?.id) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Invalid Request' },
|
||||
HttpStatus.BAD_REQUEST,
|
||||
);
|
||||
}
|
||||
|
||||
const object = await this.prismaService[entity].findUniqueOrThrow({
|
||||
where: { id: args.where.id },
|
||||
});
|
||||
|
||||
if (!object) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Record not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const workspace = request.user.workspace;
|
||||
|
||||
if (object.workspaceId !== workspace.id) {
|
||||
throw new HttpException(
|
||||
{ reason: 'Record not found' },
|
||||
HttpStatus.NOT_FOUND,
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user