From 7dd819de7e3742a65d2a2e01d70bd1cd63325758 Mon Sep 17 00:00:00 2001 From: M1000fr Date: Mon, 25 Nov 2024 23:27:37 +0100 Subject: [PATCH] chore: switch to Fastify & Mercurius with auth for subscription, queries and mutations --- src/app.controller.ts | 9 -------- src/app.module.ts | 4 ---- src/auth/auth.guard.ts | 36 ----------------------------- src/auth/auth.module.ts | 4 ---- src/graphqlOptions.ts | 38 ++++++++++++++++++++++++++++--- src/schema.gql | 2 +- src/user/dto/update-user.input.ts | 6 ++--- src/user/user.resolver.ts | 16 +++++++++---- 8 files changed, 50 insertions(+), 65 deletions(-) delete mode 100644 src/app.controller.ts delete mode 100644 src/auth/auth.guard.ts delete mode 100644 src/auth/auth.module.ts diff --git a/src/app.controller.ts b/src/app.controller.ts deleted file mode 100644 index 8ab72d2..0000000 --- a/src/app.controller.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { Controller, Get } from '@nestjs/common'; - -@Controller() -export class AppController { - @Get() - getHello(): string { - return 'Hello World!'; - } -} diff --git a/src/app.module.ts b/src/app.module.ts index e6f02c8..a2b08b6 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -5,9 +5,7 @@ import { GraphQLModule } from "@nestjs/graphql"; import { MercuriusDriver, MercuriusDriverConfig } from "@nestjs/mercurius"; import { AppService } from "./app.service"; -import { AuthModule } from "./auth/auth.module"; import { UserModule } from "./user/user.module"; -import { AppController } from "./app.controller"; import { GraphqlOptions } from "./graphqlOptions"; @@ -24,10 +22,8 @@ import { GraphqlOptions } from "./graphqlOptions"; driver: MercuriusDriver, useClass: GraphqlOptions, }), - AuthModule, UserModule, ], providers: [AppService], - controllers: [AppController], }) export class AppModule {} diff --git a/src/auth/auth.guard.ts b/src/auth/auth.guard.ts deleted file mode 100644 index aa12011..0000000 --- a/src/auth/auth.guard.ts +++ /dev/null @@ -1,36 +0,0 @@ -import { - CanActivate, - ExecutionContext, - Injectable, - UnauthorizedException, -} from "@nestjs/common"; -import { JwtService } from "@nestjs/jwt"; - -import { Request } from "express"; - -@Injectable() -export class AuthGuard implements CanActivate { - constructor(private jwtService: JwtService) {} - - async canActivate(context: ExecutionContext): Promise { - const request = context.switchToHttp().getRequest(); - const token = this.extractTokenFromHeader(request); - if (!token) { - throw new UnauthorizedException(); - } - try { - const payload = await this.jwtService.verifyAsync(token, { - secret: process.env.JWT_SECRET, - }); - request["user"] = payload; - } catch { - throw new UnauthorizedException(); - } - return true; - } - - private extractTokenFromHeader(request: Request): string | undefined { - const [type, token] = request.headers.authorization?.split(" ") ?? []; - return type === "Bearer" ? token : undefined; - } -} diff --git a/src/auth/auth.module.ts b/src/auth/auth.module.ts deleted file mode 100644 index 7459c06..0000000 --- a/src/auth/auth.module.ts +++ /dev/null @@ -1,4 +0,0 @@ -import { Module } from '@nestjs/common'; - -@Module({}) -export class AuthModule {} diff --git a/src/graphqlOptions.ts b/src/graphqlOptions.ts index 30fd95d..77c25d1 100644 --- a/src/graphqlOptions.ts +++ b/src/graphqlOptions.ts @@ -1,14 +1,46 @@ +import { Injectable, UnauthorizedException } from "@nestjs/common"; import { GqlOptionsFactory } from "@nestjs/graphql"; -import { Injectable } from "@nestjs/common"; +import { JwtService } from "@nestjs/jwt"; import { MercuriusDriverConfig } from "@nestjs/mercurius"; @Injectable() export class GraphqlOptions implements GqlOptionsFactory { + constructor(private readonly jwtService: JwtService) {} + createGqlOptions(): Promise | MercuriusDriverConfig { return { autoSchemaFile: "src/schema.gql", - subscription: true, - graphiql: true, + subscription: { + context: (_connection, request) => { + const authorization = request.headers.authorization; + if (!authorization) throw new UnauthorizedException(); + + const token = authorization.split(" ")[1]; + if (!token) throw new UnauthorizedException(); + + try { + const user = this.jwtService.verify(token); + return { user }; + } catch (error) { + throw new UnauthorizedException(); + } + }, + }, + graphiql: false, + context: (req) => { + const authorization = req.headers.authorization; + if (!authorization) throw new UnauthorizedException(); + + const token = authorization.split(" ")[1]; + if (!token) throw new UnauthorizedException(); + + try { + const user = this.jwtService.verify(token); + return { user }; + } catch (error) { + throw new UnauthorizedException(); + } + }, }; } } diff --git a/src/schema.gql b/src/schema.gql index 9e63574..349b42c 100644 --- a/src/schema.gql +++ b/src/schema.gql @@ -24,7 +24,7 @@ input CreateUserInput { } input UpdateUserInput { - username: String! + username: String isAdmin: Boolean id: String! } diff --git a/src/user/dto/update-user.input.ts b/src/user/dto/update-user.input.ts index 91c84b7..076ce2c 100644 --- a/src/user/dto/update-user.input.ts +++ b/src/user/dto/update-user.input.ts @@ -3,9 +3,9 @@ import { CreateUserInput } from "./create-user.input"; @InputType() export class UpdateUserInput extends PartialType(CreateUserInput) { - @Field() - id: string; - @Field() + id: string; + + @Field({ nullable: true }) username: string; } diff --git a/src/user/user.resolver.ts b/src/user/user.resolver.ts index 4ec887b..e9691fb 100644 --- a/src/user/user.resolver.ts +++ b/src/user/user.resolver.ts @@ -1,12 +1,18 @@ -import { Args, Mutation, Query, Resolver, Subscription } from "@nestjs/graphql"; +import { + Args, + Mutation, + Query, + Resolver, + Subscription +} from "@nestjs/graphql"; -import { UserService } from "./user.service"; -import { User } from "./user.entity"; import { CreateUserInput } from "./dto/create-user.input"; -import { UpdateUserInput } from "./dto/update-user.input"; import { SetUserPasswordInput } from "./dto/setpassword-user.input"; +import { UpdateUserInput } from "./dto/update-user.input"; +import { User } from "./user.entity"; +import { UserService } from "./user.service"; -import { PubSub } from 'graphql-subscriptions'; +import { PubSub } from "graphql-subscriptions"; const pubSub = new PubSub();