chore: switch to Fastify & Mercurius with auth for subscription, queries and mutations

This commit is contained in:
M1000fr 2024-11-25 23:27:37 +01:00
parent eb3f3a8de9
commit 7dd819de7e
8 changed files with 50 additions and 65 deletions

View File

@ -1,9 +0,0 @@
import { Controller, Get } from '@nestjs/common';
@Controller()
export class AppController {
@Get()
getHello(): string {
return 'Hello World!';
}
}

View File

@ -5,9 +5,7 @@ import { GraphQLModule } from "@nestjs/graphql";
import { MercuriusDriver, MercuriusDriverConfig } from "@nestjs/mercurius"; import { MercuriusDriver, MercuriusDriverConfig } from "@nestjs/mercurius";
import { AppService } from "./app.service"; import { AppService } from "./app.service";
import { AuthModule } from "./auth/auth.module";
import { UserModule } from "./user/user.module"; import { UserModule } from "./user/user.module";
import { AppController } from "./app.controller";
import { GraphqlOptions } from "./graphqlOptions"; import { GraphqlOptions } from "./graphqlOptions";
@ -24,10 +22,8 @@ import { GraphqlOptions } from "./graphqlOptions";
driver: MercuriusDriver, driver: MercuriusDriver,
useClass: GraphqlOptions, useClass: GraphqlOptions,
}), }),
AuthModule,
UserModule, UserModule,
], ],
providers: [AppService], providers: [AppService],
controllers: [AppController],
}) })
export class AppModule {} export class AppModule {}

View File

@ -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<boolean> {
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;
}
}

View File

@ -1,4 +0,0 @@
import { Module } from '@nestjs/common';
@Module({})
export class AuthModule {}

View File

@ -1,14 +1,46 @@
import { Injectable, UnauthorizedException } from "@nestjs/common";
import { GqlOptionsFactory } from "@nestjs/graphql"; import { GqlOptionsFactory } from "@nestjs/graphql";
import { Injectable } from "@nestjs/common"; import { JwtService } from "@nestjs/jwt";
import { MercuriusDriverConfig } from "@nestjs/mercurius"; import { MercuriusDriverConfig } from "@nestjs/mercurius";
@Injectable() @Injectable()
export class GraphqlOptions implements GqlOptionsFactory { export class GraphqlOptions implements GqlOptionsFactory {
constructor(private readonly jwtService: JwtService) {}
createGqlOptions(): Promise<MercuriusDriverConfig> | MercuriusDriverConfig { createGqlOptions(): Promise<MercuriusDriverConfig> | MercuriusDriverConfig {
return { return {
autoSchemaFile: "src/schema.gql", autoSchemaFile: "src/schema.gql",
subscription: true, subscription: {
graphiql: true, 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();
}
},
}; };
} }
} }

View File

@ -24,7 +24,7 @@ input CreateUserInput {
} }
input UpdateUserInput { input UpdateUserInput {
username: String! username: String
isAdmin: Boolean isAdmin: Boolean
id: String! id: String!
} }

View File

@ -3,9 +3,9 @@ import { CreateUserInput } from "./create-user.input";
@InputType() @InputType()
export class UpdateUserInput extends PartialType(CreateUserInput) { export class UpdateUserInput extends PartialType(CreateUserInput) {
@Field()
id: string;
@Field() @Field()
id: string;
@Field({ nullable: true })
username: string; username: string;
} }

View File

@ -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 { CreateUserInput } from "./dto/create-user.input";
import { UpdateUserInput } from "./dto/update-user.input";
import { SetUserPasswordInput } from "./dto/setpassword-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(); const pubSub = new PubSub();