mirror of
https://github.com/twentyhq/twenty.git
synced 2024-11-23 14:03:35 +03:00
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:
parent
5921c7f11d
commit
2cd081234f
1
server/.nvmrc
Normal file
1
server/.nvmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
18.10.0
|
18
server/jest.config.ts
Normal file
18
server/jest.config.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
module.exports = {
|
||||||
|
clearMocks: true,
|
||||||
|
preset: 'ts-jest',
|
||||||
|
testEnvironment: 'node',
|
||||||
|
setupFilesAfterEnv: ['<rootDir>/src/prisma-mock/jest-prisma-singleton.ts'],
|
||||||
|
|
||||||
|
moduleFileExtensions: ['js', 'json', 'ts'],
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^src/(.*)': '<rootDir>/src/$1',
|
||||||
|
},
|
||||||
|
rootDir: './',
|
||||||
|
testRegex: '.*\\.spec\\.ts$',
|
||||||
|
transform: {
|
||||||
|
'^.+\\.(t|j)s$': 'ts-jest',
|
||||||
|
},
|
||||||
|
collectCoverageFrom: ['**/*.(t|j)s'],
|
||||||
|
coverageDirectory: '../coverage',
|
||||||
|
};
|
@ -43,6 +43,7 @@
|
|||||||
"graphql": "^16.6.0",
|
"graphql": "^16.6.0",
|
||||||
"graphql-type-json": "^0.3.2",
|
"graphql-type-json": "^0.3.2",
|
||||||
"jest-mock-extended": "^3.0.4",
|
"jest-mock-extended": "^3.0.4",
|
||||||
|
"jsonwebtoken": "^9.0.0",
|
||||||
"passport": "^0.6.0",
|
"passport": "^0.6.0",
|
||||||
"passport-google-oauth20": "^2.0.0",
|
"passport-google-oauth20": "^2.0.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
@ -60,7 +61,9 @@
|
|||||||
"@types/jest": "28.1.8",
|
"@types/jest": "28.1.8",
|
||||||
"@types/node": "^16.0.0",
|
"@types/node": "^16.0.0",
|
||||||
"@types/passport-google-oauth20": "^2.0.11",
|
"@types/passport-google-oauth20": "^2.0.11",
|
||||||
|
"@types/passport-jwt": "^3.0.8",
|
||||||
"@types/supertest": "^2.0.11",
|
"@types/supertest": "^2.0.11",
|
||||||
|
"@types/uuid": "^9.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||||
"@typescript-eslint/parser": "^5.0.0",
|
"@typescript-eslint/parser": "^5.0.0",
|
||||||
"eslint": "^8.0.1",
|
"eslint": "^8.0.1",
|
||||||
@ -78,23 +81,6 @@
|
|||||||
"tsconfig-paths": "4.1.0",
|
"tsconfig-paths": "4.1.0",
|
||||||
"typescript": "^4.9.4"
|
"typescript": "^4.9.4"
|
||||||
},
|
},
|
||||||
"jest": {
|
|
||||||
"moduleFileExtensions": [
|
|
||||||
"js",
|
|
||||||
"json",
|
|
||||||
"ts"
|
|
||||||
],
|
|
||||||
"rootDir": "src",
|
|
||||||
"testRegex": ".*\\.spec\\.ts$",
|
|
||||||
"transform": {
|
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
|
||||||
},
|
|
||||||
"collectCoverageFrom": [
|
|
||||||
"**/*.(t|j)s"
|
|
||||||
],
|
|
||||||
"coverageDirectory": "../coverage",
|
|
||||||
"testEnvironment": "node"
|
|
||||||
},
|
|
||||||
"prisma": {
|
"prisma": {
|
||||||
"schema": "src/database/schema.prisma",
|
"schema": "src/database/schema.prisma",
|
||||||
"seed": "ts-node src/database/seeds/index.ts"
|
"seed": "ts-node src/database/seeds/index.ts"
|
||||||
|
@ -1,68 +0,0 @@
|
|||||||
import { Module } from '@nestjs/common';
|
|
||||||
import { GraphQLModule } from '@nestjs/graphql';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
|
||||||
|
|
||||||
import { AuthModule } from 'src/auth/auth.module';
|
|
||||||
import { PrismaModule } from 'src/database/prisma.module';
|
|
||||||
import { ArgsService } from './resolvers/services/args.service';
|
|
||||||
|
|
||||||
import { CompanyResolver } from './resolvers/company.resolver';
|
|
||||||
import { UserResolver } from './resolvers/user.resolver';
|
|
||||||
import { PersonResolver } from './resolvers/person.resolver';
|
|
||||||
import { CommentResolver } from './resolvers/comment.resolver';
|
|
||||||
import { CommentThreadResolver } from './resolvers/comment-thread.resolver';
|
|
||||||
import { PipelineResolver } from './resolvers/pipeline.resolver';
|
|
||||||
import { PipelineStageResolver } from './resolvers/pipeline-stage.resolver';
|
|
||||||
|
|
||||||
import { PersonRelationsResolver } from './resolvers/relations/person-relations.resolver';
|
|
||||||
import { UserRelationsResolver } from './resolvers/relations/user-relations.resolver';
|
|
||||||
import { WorkspaceMemberRelationsResolver } from './resolvers/relations/workspace-member-relations.resolver';
|
|
||||||
import { CompanyRelationsResolver } from './resolvers/relations/company-relations.resolver';
|
|
||||||
import { CommentThreadRelationsResolver } from './resolvers/relations/comment-thread-relations.resolver';
|
|
||||||
import { PipelineRelationsResolver } from './resolvers/relations/pipeline-relations.resolver';
|
|
||||||
import { GraphQLError } from 'graphql';
|
|
||||||
import { CommentRelationsResolver } from './resolvers/relations/comment-relations.resolver';
|
|
||||||
import { PipelineProgressResolver } from './resolvers/pipeline-progress.resolver';
|
|
||||||
import { PipelineStageRelationsResolver } from './resolvers/relations/pipeline-stage-relations.resolver';
|
|
||||||
import { PipelineProgressRelationsResolver } from './resolvers/relations/pipeline-progress-relations.resolver';
|
|
||||||
|
|
||||||
@Module({
|
|
||||||
imports: [
|
|
||||||
GraphQLModule.forRoot<ApolloDriverConfig>({
|
|
||||||
context: ({ req }) => ({ req }),
|
|
||||||
driver: ApolloDriver,
|
|
||||||
autoSchemaFile: true,
|
|
||||||
formatError: (error: GraphQLError) => {
|
|
||||||
error.extensions.stacktrace = undefined;
|
|
||||||
return error;
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
AuthModule,
|
|
||||||
PrismaModule,
|
|
||||||
],
|
|
||||||
providers: [
|
|
||||||
ConfigService,
|
|
||||||
ArgsService,
|
|
||||||
|
|
||||||
CompanyResolver,
|
|
||||||
PersonResolver,
|
|
||||||
UserResolver,
|
|
||||||
CommentResolver,
|
|
||||||
CommentThreadResolver,
|
|
||||||
PipelineResolver,
|
|
||||||
PipelineStageResolver,
|
|
||||||
PipelineProgressResolver,
|
|
||||||
|
|
||||||
CompanyRelationsResolver,
|
|
||||||
CommentRelationsResolver,
|
|
||||||
PersonRelationsResolver,
|
|
||||||
UserRelationsResolver,
|
|
||||||
WorkspaceMemberRelationsResolver,
|
|
||||||
CommentThreadRelationsResolver,
|
|
||||||
PipelineRelationsResolver,
|
|
||||||
PipelineStageRelationsResolver,
|
|
||||||
PipelineProgressRelationsResolver,
|
|
||||||
],
|
|
||||||
})
|
|
||||||
export class ApiModule {}
|
|
@ -1,102 +0,0 @@
|
|||||||
import { Resolver, Args, Mutation, Query } from '@nestjs/graphql';
|
|
||||||
import { UseGuards } from '@nestjs/common';
|
|
||||||
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
|
|
||||||
import { PrismaService } from 'src/database/prisma.service';
|
|
||||||
import { Workspace } from '../@generated/workspace/workspace.model';
|
|
||||||
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
|
|
||||||
import { CommentThread } from '../@generated/comment-thread/comment-thread.model';
|
|
||||||
import { CreateOneCommentThreadArgs } from '../@generated/comment-thread/create-one-comment-thread.args';
|
|
||||||
import { CreateOneCommentThreadGuard } from './guards/create-one-comment-thread.guard';
|
|
||||||
import { FindManyCommentThreadArgs } from '../@generated/comment-thread/find-many-comment-thread.args';
|
|
||||||
import { ArgsService } from './services/args.service';
|
|
||||||
import { UpdateOneCommentThreadArgs } from '../@generated/comment-thread/update-one-comment-thread.args';
|
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Resolver(() => CommentThread)
|
|
||||||
export class CommentThreadResolver {
|
|
||||||
constructor(
|
|
||||||
private readonly prismaService: PrismaService,
|
|
||||||
private readonly argsService: ArgsService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@UseGuards(CreateOneCommentThreadGuard)
|
|
||||||
@Mutation(() => CommentThread, {
|
|
||||||
nullable: false,
|
|
||||||
})
|
|
||||||
async createOneCommentThread(
|
|
||||||
@Args() args: CreateOneCommentThreadArgs,
|
|
||||||
@AuthWorkspace() workspace: Workspace,
|
|
||||||
): Promise<CommentThread> {
|
|
||||||
const newCommentData = args.data.comments?.createMany?.data
|
|
||||||
? args.data.comments?.createMany?.data?.map((comment) => ({
|
|
||||||
...comment,
|
|
||||||
...{ workspaceId: workspace.id },
|
|
||||||
}))
|
|
||||||
: [];
|
|
||||||
|
|
||||||
const createdCommentThread = await this.prismaService.commentThread.create({
|
|
||||||
data: {
|
|
||||||
...args.data,
|
|
||||||
...{ commentThreadTargets: undefined },
|
|
||||||
...{ comments: { createMany: { data: newCommentData } } },
|
|
||||||
...{ workspace: { connect: { id: workspace.id } } },
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (args.data.commentThreadTargets?.createMany?.data) {
|
|
||||||
await this.prismaService.commentThreadTarget.createMany({
|
|
||||||
data: args.data.commentThreadTargets?.createMany?.data?.map(
|
|
||||||
(target) => ({
|
|
||||||
...target,
|
|
||||||
commentThreadId: args.data.id,
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
skipDuplicates:
|
|
||||||
args.data.commentThreadTargets?.createMany?.skipDuplicates ?? false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return await this.prismaService.commentThread.update({
|
|
||||||
where: { id: args.data.id },
|
|
||||||
data: {
|
|
||||||
commentThreadTargets: {
|
|
||||||
connect: args.data.commentThreadTargets?.connect,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return createdCommentThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mutation(() => CommentThread, {
|
|
||||||
nullable: false,
|
|
||||||
})
|
|
||||||
async updateOneCommentThread(
|
|
||||||
@Args() args: UpdateOneCommentThreadArgs,
|
|
||||||
): Promise<CommentThread> {
|
|
||||||
const updatedCommentThread = await this.prismaService.commentThread.update({
|
|
||||||
data: args.data,
|
|
||||||
where: args.where,
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedCommentThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Query(() => [CommentThread])
|
|
||||||
async findManyCommentThreads(
|
|
||||||
@Args() args: FindManyCommentThreadArgs,
|
|
||||||
@AuthWorkspace() workspace: Workspace,
|
|
||||||
) {
|
|
||||||
const preparedArgs =
|
|
||||||
await this.argsService.prepareFindManyArgs<FindManyCommentThreadArgs>(
|
|
||||||
args,
|
|
||||||
workspace,
|
|
||||||
);
|
|
||||||
|
|
||||||
const result = await this.prismaService.commentThread.findMany(
|
|
||||||
preparedArgs,
|
|
||||||
);
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
import { Resolver, Args, Mutation } from '@nestjs/graphql';
|
|
||||||
import { UseGuards } from '@nestjs/common';
|
|
||||||
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
|
|
||||||
import { PrismaService } from 'src/database/prisma.service';
|
|
||||||
import { Workspace } from '../@generated/workspace/workspace.model';
|
|
||||||
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
|
|
||||||
import { CreateOneCommentArgs } from '../@generated/comment/create-one-comment.args';
|
|
||||||
import { Comment } from '../@generated/comment/comment.model';
|
|
||||||
import { CreateOneCommentGuard } from './guards/create-one-comment.guard';
|
|
||||||
import { Prisma } from '@prisma/client';
|
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Resolver(() => Comment)
|
|
||||||
export class CommentResolver {
|
|
||||||
constructor(private readonly prismaService: PrismaService) {}
|
|
||||||
|
|
||||||
@UseGuards(CreateOneCommentGuard)
|
|
||||||
@Mutation(() => Comment, {
|
|
||||||
nullable: false,
|
|
||||||
})
|
|
||||||
async createOneComment(
|
|
||||||
@Args() args: CreateOneCommentArgs,
|
|
||||||
@AuthWorkspace() workspace: Workspace,
|
|
||||||
): Promise<Comment> {
|
|
||||||
return this.prismaService.comment.create({
|
|
||||||
data: {
|
|
||||||
...args.data,
|
|
||||||
...{ workspace: { connect: { id: workspace.id } } },
|
|
||||||
},
|
|
||||||
} satisfies CreateOneCommentArgs as Prisma.CommentCreateArgs);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,10 +0,0 @@
|
|||||||
import { ExecutionContext, createParamDecorator } from '@nestjs/common';
|
|
||||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
|
||||||
|
|
||||||
export const AuthUser = createParamDecorator(
|
|
||||||
(data: unknown, ctx: ExecutionContext) => {
|
|
||||||
const gqlContext = GqlExecutionContext.create(ctx);
|
|
||||||
const request = gqlContext.getContext().req;
|
|
||||||
return request.user;
|
|
||||||
},
|
|
||||||
);
|
|
@ -1,10 +0,0 @@
|
|||||||
import { ExecutionContext, createParamDecorator } from '@nestjs/common';
|
|
||||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
|
||||||
|
|
||||||
export const AuthWorkspace = createParamDecorator(
|
|
||||||
(data: unknown, ctx: ExecutionContext) => {
|
|
||||||
const gqlContext = GqlExecutionContext.create(ctx);
|
|
||||||
const request = gqlContext.getContext().req;
|
|
||||||
return request.workspace;
|
|
||||||
},
|
|
||||||
);
|
|
@ -1,73 +0,0 @@
|
|||||||
import { Resolver, Args, Query, Mutation } from '@nestjs/graphql';
|
|
||||||
import { UseGuards } from '@nestjs/common';
|
|
||||||
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
|
|
||||||
import { PrismaService } from 'src/database/prisma.service';
|
|
||||||
import { Workspace } from '../@generated/workspace/workspace.model';
|
|
||||||
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
|
|
||||||
import { ArgsService } from './services/args.service';
|
|
||||||
import { FindManyPipelineProgressArgs } from '../@generated/pipeline-progress/find-many-pipeline-progress.args';
|
|
||||||
import { PipelineProgress } from '../@generated/pipeline-progress/pipeline-progress.model';
|
|
||||||
import { UpdateOnePipelineProgressArgs } from '../@generated/pipeline-progress/update-one-pipeline-progress.args';
|
|
||||||
import { Prisma } from '@prisma/client';
|
|
||||||
import { AffectedRows } from '../@generated/prisma/affected-rows.output';
|
|
||||||
import { DeleteManyPipelineProgressArgs } from '../@generated/pipeline-progress/delete-many-pipeline-progress.args';
|
|
||||||
import { CreateOnePipelineProgressArgs } from '../@generated/pipeline-progress/create-one-pipeline-progress.args';
|
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Resolver(() => PipelineProgress)
|
|
||||||
export class PipelineProgressResolver {
|
|
||||||
constructor(
|
|
||||||
private readonly prismaService: PrismaService,
|
|
||||||
private readonly argsService: ArgsService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Query(() => [PipelineProgress])
|
|
||||||
async findManyPipelineProgress(
|
|
||||||
@Args() args: FindManyPipelineProgressArgs,
|
|
||||||
@AuthWorkspace() workspace: Workspace,
|
|
||||||
) {
|
|
||||||
const preparedArgs =
|
|
||||||
await this.argsService.prepareFindManyArgs<FindManyPipelineProgressArgs>(
|
|
||||||
args,
|
|
||||||
workspace,
|
|
||||||
);
|
|
||||||
return this.prismaService.pipelineProgress.findMany(preparedArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mutation(() => PipelineProgress, {
|
|
||||||
nullable: true,
|
|
||||||
})
|
|
||||||
async updateOnePipelineProgress(
|
|
||||||
@Args() args: UpdateOnePipelineProgressArgs,
|
|
||||||
): Promise<PipelineProgress | null> {
|
|
||||||
return this.prismaService.pipelineProgress.update({
|
|
||||||
...args,
|
|
||||||
} satisfies UpdateOnePipelineProgressArgs as Prisma.PipelineProgressUpdateArgs);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mutation(() => AffectedRows, {
|
|
||||||
nullable: false,
|
|
||||||
})
|
|
||||||
async deleteManyPipelineProgress(
|
|
||||||
@Args() args: DeleteManyPipelineProgressArgs,
|
|
||||||
): Promise<AffectedRows> {
|
|
||||||
return this.prismaService.pipelineProgress.deleteMany({
|
|
||||||
...args,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Mutation(() => PipelineProgress, {
|
|
||||||
nullable: false,
|
|
||||||
})
|
|
||||||
async createOnePipelineProgress(
|
|
||||||
@Args() args: CreateOnePipelineProgressArgs,
|
|
||||||
@AuthWorkspace() workspace: Workspace,
|
|
||||||
): Promise<PipelineProgress> {
|
|
||||||
return this.prismaService.pipelineProgress.create({
|
|
||||||
data: {
|
|
||||||
...args.data,
|
|
||||||
...{ workspace: { connect: { id: workspace.id } } },
|
|
||||||
},
|
|
||||||
} satisfies CreateOnePipelineProgressArgs as Prisma.PipelineProgressCreateArgs);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import { Resolver, Args, Query } from '@nestjs/graphql';
|
|
||||||
import { UseGuards } from '@nestjs/common';
|
|
||||||
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
|
|
||||||
import { PrismaService } from 'src/database/prisma.service';
|
|
||||||
import { Workspace } from '../@generated/workspace/workspace.model';
|
|
||||||
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
|
|
||||||
import { PipelineStage } from '../@generated/pipeline-stage/pipeline-stage.model';
|
|
||||||
import { FindManyPipelineStageArgs } from '../@generated/pipeline-stage/find-many-pipeline-stage.args';
|
|
||||||
import { ArgsService } from './services/args.service';
|
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Resolver(() => PipelineStage)
|
|
||||||
export class PipelineStageResolver {
|
|
||||||
constructor(
|
|
||||||
private readonly prismaService: PrismaService,
|
|
||||||
private readonly argsService: ArgsService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Query(() => [PipelineStage])
|
|
||||||
async findManyPipelineStage(
|
|
||||||
@Args() args: FindManyPipelineStageArgs,
|
|
||||||
@AuthWorkspace() workspace: Workspace,
|
|
||||||
) {
|
|
||||||
const preparedArgs =
|
|
||||||
await this.argsService.prepareFindManyArgs<FindManyPipelineStageArgs>(
|
|
||||||
args,
|
|
||||||
workspace,
|
|
||||||
);
|
|
||||||
return this.prismaService.pipelineStage.findMany(preparedArgs);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,31 +0,0 @@
|
|||||||
import { Resolver, Args, Query } from '@nestjs/graphql';
|
|
||||||
import { UseGuards } from '@nestjs/common';
|
|
||||||
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
|
|
||||||
import { PrismaService } from 'src/database/prisma.service';
|
|
||||||
import { Workspace } from '../@generated/workspace/workspace.model';
|
|
||||||
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
|
|
||||||
import { Pipeline } from '../@generated/pipeline/pipeline.model';
|
|
||||||
import { FindManyPipelineArgs } from '../@generated/pipeline/find-many-pipeline.args';
|
|
||||||
import { ArgsService } from './services/args.service';
|
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Resolver(() => Pipeline)
|
|
||||||
export class PipelineResolver {
|
|
||||||
constructor(
|
|
||||||
private readonly prismaService: PrismaService,
|
|
||||||
private readonly argsService: ArgsService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@Query(() => [Pipeline])
|
|
||||||
async findManyPipeline(
|
|
||||||
@Args() args: FindManyPipelineArgs,
|
|
||||||
@AuthWorkspace() workspace: Workspace,
|
|
||||||
) {
|
|
||||||
const preparedArgs =
|
|
||||||
await this.argsService.prepareFindManyArgs<FindManyPipelineArgs>(
|
|
||||||
args,
|
|
||||||
workspace,
|
|
||||||
);
|
|
||||||
return this.prismaService.pipeline.findMany(preparedArgs);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { Workspace } from '@prisma/client';
|
|
||||||
|
|
||||||
type FindManyArgsType = { where?: object; orderBy?: object };
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class ArgsService {
|
|
||||||
async prepareFindManyArgs<T extends FindManyArgsType>(
|
|
||||||
args: T,
|
|
||||||
workspace: Workspace,
|
|
||||||
): Promise<T> {
|
|
||||||
args.where = {
|
|
||||||
...args.where,
|
|
||||||
...{ workspace: { is: { id: { equals: workspace.id } } } },
|
|
||||||
};
|
|
||||||
return args;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
import { Resolver, Query, Args } from '@nestjs/graphql';
|
|
||||||
import { PrismaService } from 'src/database/prisma.service';
|
|
||||||
import { UseFilters, UseGuards } from '@nestjs/common';
|
|
||||||
|
|
||||||
import { User } from '../@generated/user/user.model';
|
|
||||||
import { FindManyUserArgs } from '../@generated/user/find-many-user.args';
|
|
||||||
import { Workspace } from '@prisma/client';
|
|
||||||
import { AuthWorkspace } from './decorators/auth-workspace.decorator';
|
|
||||||
import { ExceptionFilter } from './exception-filters/exception.filter';
|
|
||||||
import { JwtAuthGuard } from 'src/auth/guards/jwt.auth.guard';
|
|
||||||
|
|
||||||
@UseGuards(JwtAuthGuard)
|
|
||||||
@Resolver(() => User)
|
|
||||||
export class UserResolver {
|
|
||||||
constructor(private readonly prismaService: PrismaService) {}
|
|
||||||
|
|
||||||
@UseFilters(ExceptionFilter)
|
|
||||||
@Query(() => [User], {
|
|
||||||
nullable: false,
|
|
||||||
})
|
|
||||||
async findManyUser(
|
|
||||||
@Args() args: FindManyUserArgs,
|
|
||||||
@AuthWorkspace() workspace: Workspace,
|
|
||||||
): Promise<User[]> {
|
|
||||||
args.where = {
|
|
||||||
...args.where,
|
|
||||||
...{
|
|
||||||
workspaceMember: {
|
|
||||||
is: { workspace: { is: { id: { equals: workspace.id } } } },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
return await this.prismaService.user.findMany({
|
|
||||||
...args,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,14 +1,32 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
|
import { GraphQLModule } from '@nestjs/graphql';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { HealthController } from './health.controller';
|
|
||||||
import { TerminusModule } from '@nestjs/terminus';
|
|
||||||
|
|
||||||
import { AuthModule } from './auth/auth.module';
|
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { ApiModule } from './api/api.module';
|
import { CoreModule } from './core/core.module';
|
||||||
|
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
|
||||||
|
import { GraphQLError } from 'graphql';
|
||||||
|
import { PrismaModule } from './database/prisma.module';
|
||||||
|
import { HealthModule } from './health/health.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [ConfigModule.forRoot({}), TerminusModule, AuthModule, ApiModule],
|
imports: [
|
||||||
controllers: [HealthController],
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
}),
|
||||||
|
GraphQLModule.forRoot<ApolloDriverConfig>({
|
||||||
|
context: ({ req }) => ({ req }),
|
||||||
|
driver: ApolloDriver,
|
||||||
|
autoSchemaFile: true,
|
||||||
|
formatError: (error: GraphQLError) => {
|
||||||
|
error.extensions.stacktrace = undefined;
|
||||||
|
return error;
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
PrismaModule,
|
||||||
|
HealthModule,
|
||||||
|
CoreModule,
|
||||||
|
],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
})
|
})
|
||||||
export class AppModule {}
|
export class AppModule {}
|
||||||
|
@ -1,70 +0,0 @@
|
|||||||
import {
|
|
||||||
CanActivate,
|
|
||||||
ExecutionContext,
|
|
||||||
HttpException,
|
|
||||||
HttpStatus,
|
|
||||||
Injectable,
|
|
||||||
UnauthorizedException,
|
|
||||||
} from '@nestjs/common';
|
|
||||||
import { JwtService } from '@nestjs/jwt';
|
|
||||||
import { GqlExecutionContext } from '@nestjs/graphql';
|
|
||||||
import { Request } from 'express';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { PrismaService } from 'src/database/prisma.service';
|
|
||||||
import { JwtPayload } from '../strategies/jwt.auth.strategy';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class JwtAuthGuard implements CanActivate {
|
|
||||||
constructor(
|
|
||||||
private jwtService: JwtService,
|
|
||||||
private configService: ConfigService,
|
|
||||||
private prismaService: PrismaService,
|
|
||||||
) {}
|
|
||||||
|
|
||||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
||||||
const gqlContext = GqlExecutionContext.create(context);
|
|
||||||
const request = gqlContext.getContext().req;
|
|
||||||
const token = this.extractTokenFromHeader(request);
|
|
||||||
if (!token) {
|
|
||||||
throw new UnauthorizedException();
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
const payload: JwtPayload = await this.jwtService.verifyAsync(token, {
|
|
||||||
secret: this.configService.get('JWT_SECRET'),
|
|
||||||
});
|
|
||||||
|
|
||||||
const user = this.prismaService.user.findUniqueOrThrow({
|
|
||||||
where: { id: payload.userId },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!user) {
|
|
||||||
throw new HttpException(
|
|
||||||
{ reason: 'User does not exist' },
|
|
||||||
HttpStatus.FORBIDDEN,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const workspace = this.prismaService.workspace.findUniqueOrThrow({
|
|
||||||
where: { id: payload.workspaceId },
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!workspace) {
|
|
||||||
throw new HttpException(
|
|
||||||
{ reason: 'Workspace does not exist' },
|
|
||||||
HttpStatus.FORBIDDEN,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
request.user = user;
|
|
||||||
request.workspace = workspace;
|
|
||||||
} catch (exception) {
|
|
||||||
throw new UnauthorizedException();
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private extractTokenFromHeader(request: Request): string | undefined {
|
|
||||||
const [type, token] = request.headers.authorization?.split(' ') ?? [];
|
|
||||||
return type === 'Bearer' ? token : undefined;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
import { Strategy } from 'passport-jwt';
|
|
||||||
import { PassportStrategy } from '@nestjs/passport';
|
|
||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
|
|
||||||
export type JwtPayload = { userId: string; workspaceId: string };
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
|
||||||
constructor(configService: ConfigService) {
|
|
||||||
const extractJwtFromCookie = (req) => {
|
|
||||||
let token = null;
|
|
||||||
|
|
||||||
if (req && req.cookies) {
|
|
||||||
token = req.cookies['jwt'];
|
|
||||||
}
|
|
||||||
return token;
|
|
||||||
};
|
|
||||||
|
|
||||||
super({
|
|
||||||
jwtFromRequest: extractJwtFromCookie,
|
|
||||||
ignoreExpiration: false,
|
|
||||||
secretOrKey: configService.get<string>('JWT_SECRET'),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
async validate(payload: JwtPayload): Promise<JwtPayload> {
|
|
||||||
return { userId: payload.userId, workspaceId: payload.workspaceId };
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user