ref: format with prettier

This commit is contained in:
M1000fr 2024-12-02 16:55:44 +01:00
parent ccf496a5d9
commit a6d24cee9d
22 changed files with 345 additions and 367 deletions

View File

@ -5,7 +5,7 @@
"license": "Apache-2.0", "license": "Apache-2.0",
"scripts": { "scripts": {
"build": "nest build", "build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"", "format": "prettier --write \"src/**/*.ts\"",
"start": "nest start", "start": "nest start",
"start:dev": "nest start --watch", "start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch", "start:debug": "nest start --debug --watch",

View File

@ -1,17 +1,17 @@
export default () => ({ export default () => ({
JWT: { JWT: {
secret: process.env.JWT_SECRET, secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN, expiresIn: process.env.JWT_EXPIRES_IN,
refresh: { refresh: {
secret: process.env.REFRESH_JWT_SECRET, secret: process.env.REFRESH_JWT_SECRET,
expiresIn: process.env.REFRESH_JWT_EXPIRES_IN, expiresIn: process.env.REFRESH_JWT_EXPIRES_IN,
}, },
}, },
oauth2: { oauth2: {
discord: { discord: {
clientId: process.env.DISCORD_CLIENT_ID, clientId: process.env.DISCORD_CLIENT_ID,
clientSecret: process.env.DISCORD_CLIENT_SECRET, clientSecret: process.env.DISCORD_CLIENT_SECRET,
callbackUrl: process.env.DISCORD_CALLBACK_URL, callbackUrl: process.env.DISCORD_CALLBACK_URL,
}, },
}, },
}); });

View File

@ -27,7 +27,7 @@ async function bootstrap() {
swaggerOptions: { swaggerOptions: {
tryItOutEnabled: true, tryItOutEnabled: true,
persistAuthorization: true, persistAuthorization: true,
} },
}); });
await app.listen(process.env.PORT ?? 3000, "0.0.0.0"); await app.listen(process.env.PORT ?? 3000, "0.0.0.0");

View File

@ -1,22 +1,12 @@
import { import { Controller, Get, Req, Res, UseGuards } from "@nestjs/common";
Body,
Controller,
Get,
Post,
Req,
Res,
UseGuards,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import { ApiBearerAuth, ApiBody, ApiOkResponse } from "@nestjs/swagger"; import { ApiBearerAuth, ApiOkResponse } from "@nestjs/swagger";
import { URLSearchParams } from "node:url"; import { URLSearchParams } from "node:url";
import { ResfreshDTO } from "./dto/refresh.dto"; import { AuthService } from "./auth.service";
import { DiscordAuthGuard } from "./guards/discord.guard"; import { DiscordAuthGuard } from "./guards/discord.guard";
import { RefreshJwtAuthGuard } from "./guards/refresh.guard"; import { RefreshJwtAuthGuard } from "./guards/refresh.guard";
import { AuthService } from "./auth.service";
@Controller("auth") @Controller("auth")
export class AuthController { export class AuthController {
@ -70,10 +60,9 @@ export class AuthController {
@Get("refresh") @Get("refresh")
@UseGuards(RefreshJwtAuthGuard) @UseGuards(RefreshJwtAuthGuard)
@ApiBearerAuth() @ApiBearerAuth()
@ApiBody({
type: ResfreshDTO,
})
refresh(@Req() req) { refresh(@Req() req) {
return this.authService.accessToken(req.user); return {
accessToken: this.authService.accessToken(req.user),
};
} }
} }

View File

@ -10,24 +10,24 @@ export class AuthService {
) {} ) {}
accessToken(user: { id: string }) { accessToken(user: { id: string }) {
const payload = { id: user.id }; return this.jwtService.sign(
return { { id: user.id },
accessToken: this.jwtService.sign(payload, { {
secret: this.configService.get<string>("JWT.secret"), secret: this.configService.get<string>("JWT.secret"),
expiresIn: this.configService.get<string>("JWT.expiresIn"), expiresIn: this.configService.get<string>("JWT.expiresIn"),
}), },
}; );
} }
refreshToken(user: { id: string }) { refreshToken(user: { id: string }) {
const payload = { id: user.id }; return this.jwtService.sign(
return { { id: user.id },
refreshToken: this.jwtService.sign(payload, { {
secret: this.configService.get<string>("JWT.refresh.secret"), secret: this.configService.get<string>("JWT.refresh.secret"),
expiresIn: this.configService.get<string>( expiresIn: this.configService.get<string>(
"JWT.refresh.expiresIn", "JWT.refresh.expiresIn",
), ),
}), },
}; );
} }
} }

View File

@ -1,4 +1,4 @@
import { SetMetadata } from "@nestjs/common"; import { SetMetadata } from "@nestjs/common";
import { $Enums } from "@prisma/client"; import { $Enums } from "@prisma/client";
export const Role = (role: $Enums.Role) => SetMetadata("role", role); export const Role = (role: $Enums.Role) => SetMetadata("role", role);

View File

@ -1,8 +0,0 @@
import { ApiProperty } from "@nestjs/swagger";
import { IsJWT } from "class-validator";
export class ResfreshDTO {
@ApiProperty()
@IsJWT()
refreshToken: string;
}

View File

@ -1,13 +1,13 @@
import { Injectable, UnauthorizedException } from "@nestjs/common"; import { Injectable, UnauthorizedException } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport"; import { AuthGuard } from "@nestjs/passport";
@Injectable() @Injectable()
export class DiscordAuthGuard extends AuthGuard("discord") { export class DiscordAuthGuard extends AuthGuard("discord") {
handleRequest(err, user, info, context) { handleRequest(err, user, info, context) {
if (err || !user) { if (err || !user) {
const errorMessage = info?.message || "Authentication failed"; const errorMessage = info?.message || "Authentication failed";
throw new UnauthorizedException(errorMessage); throw new UnauthorizedException(errorMessage);
} }
return user; return user;
} }
} }

View File

@ -1,5 +1,5 @@
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport"; import { AuthGuard } from "@nestjs/passport";
@Injectable() @Injectable()
export class JwtAuthGuard extends AuthGuard("jwt") {} export class JwtAuthGuard extends AuthGuard("jwt") {}

View File

@ -1,5 +1,5 @@
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
import { AuthGuard } from "@nestjs/passport"; import { AuthGuard } from "@nestjs/passport";
@Injectable() @Injectable()
export class RefreshJwtAuthGuard extends AuthGuard("refresh") {} export class RefreshJwtAuthGuard extends AuthGuard("refresh") {}

View File

@ -1,39 +1,36 @@
import { import {
Injectable, Injectable,
CanActivate, CanActivate,
ExecutionContext, ExecutionContext,
ForbiddenException, ForbiddenException,
UnauthorizedException, UnauthorizedException,
} from "@nestjs/common"; } from "@nestjs/common";
import { Reflector } from "@nestjs/core"; import { Reflector } from "@nestjs/core";
@Injectable() @Injectable()
export class RolesGuard implements CanActivate { export class RolesGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {} constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean { canActivate(context: ExecutionContext): boolean {
const role = this.reflector.get<string>( const role = this.reflector.get<string>("role", context.getHandler());
"role", if (!role) {
context.getHandler(), return true;
); }
if (!role) {
return true; const request = context.switchToHttp().getRequest();
} const user = request.user;
const request = context.switchToHttp().getRequest(); if (!user) {
const user = request.user; throw new ForbiddenException("User not authenticated");
}
if (!user) {
throw new ForbiddenException("User not authenticated"); const hasRole = role === user.role;
} if (!hasRole) {
throw new UnauthorizedException(
const hasRole = role === user.role; `You need to have the role ${role} to access this resource`,
if (!hasRole) { );
throw new UnauthorizedException( }
`You need to have the role ${role} to access this resource`,
); return true;
} }
}
return true;
}
}

View File

@ -1,40 +1,40 @@
import { Injectable } from "@nestjs/common"; import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
import { PassportStrategy } from "@nestjs/passport"; import { PassportStrategy } from "@nestjs/passport";
import { Profile, Strategy } from "passport-discord"; import { Profile, Strategy } from "passport-discord";
import { AuthService } from "../auth.service"; import { AuthService } from "../auth.service";
@Injectable() @Injectable()
export class DiscordStrategy extends PassportStrategy(Strategy, "discord") { export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
configService: ConfigService; configService: ConfigService;
constructor( constructor(
private readonly authService: AuthService, private readonly authService: AuthService,
configService: ConfigService, configService: ConfigService,
) { ) {
super({ super({
clientID: configService.get<string>("oauth2.discord.clientId"), clientID: configService.get<string>("oauth2.discord.clientId"),
clientSecret: configService.get<string>( clientSecret: configService.get<string>(
"oauth2.discord.clientSecret", "oauth2.discord.clientSecret",
), ),
callbackURL: configService.get<string>( callbackURL: configService.get<string>(
"oauth2.discord.callbackUrl", "oauth2.discord.callbackUrl",
), ),
scope: ["identify", "email"], scope: ["identify", "email"],
}); });
this.configService = configService; this.configService = configService;
} }
async validate( async validate(
_accessToken: string, _accessToken: string,
_refreshToken: string, _refreshToken: string,
profile: Profile, profile: Profile,
done: Function, done: Function,
) { ) {
const accessToken = this.authService.accessToken({ id: profile.id }); const accessToken = this.authService.accessToken({ id: profile.id });
const refreshToken = this.authService.refreshToken({ id: profile.id }); const refreshToken = this.authService.refreshToken({ id: profile.id });
done(null, { accessToken, refreshToken }); done(null, { accessToken, refreshToken });
} }
} }

View File

@ -1,30 +1,30 @@
import { Injectable, UnauthorizedException } from "@nestjs/common"; import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport"; import { PassportStrategy } from "@nestjs/passport";
import { User } from "@prisma/client"; import { User } from "@prisma/client";
import { Strategy, ExtractJwt } from "passport-jwt"; import { Strategy, ExtractJwt } from "passport-jwt";
import { UserService } from "@Modules/user/user.service"; import { UserService } from "@Modules/user/user.service";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
@Injectable() @Injectable()
export class JWTStrategy extends PassportStrategy(Strategy) { export class JWTStrategy extends PassportStrategy(Strategy) {
constructor( constructor(
private readonly userService: UserService, private readonly userService: UserService,
configService: ConfigService, configService: ConfigService,
) { ) {
super({ super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExipration: false, ignoreExipration: false,
secretOrKey: configService.get<string>("JWT.secret"), secretOrKey: configService.get<string>("JWT.secret"),
}); });
} }
async validate(payload: any): Promise<User> { async validate(payload: any): Promise<User> {
const user = await this.userService.findById(payload.id); const user = await this.userService.findById(payload.id);
if (!user) { if (!user) {
throw new UnauthorizedException("User not found"); throw new UnauthorizedException("User not found");
} }
return user; return user;
} }
} }

View File

@ -1,30 +1,30 @@
import { Injectable, UnauthorizedException } from "@nestjs/common"; import { Injectable, UnauthorizedException } from "@nestjs/common";
import { PassportStrategy } from "@nestjs/passport"; import { PassportStrategy } from "@nestjs/passport";
import { User } from "@prisma/client"; import { User } from "@prisma/client";
import { Strategy, ExtractJwt } from "passport-jwt"; import { Strategy, ExtractJwt } from "passport-jwt";
import { UserService } from "@Modules/user/user.service"; import { UserService } from "@Modules/user/user.service";
import { ConfigService } from "@nestjs/config"; import { ConfigService } from "@nestjs/config";
@Injectable() @Injectable()
export class RefreshJWTStrategy extends PassportStrategy(Strategy, "refresh") { export class RefreshJWTStrategy extends PassportStrategy(Strategy, "refresh") {
constructor( constructor(
private readonly userService: UserService, private readonly userService: UserService,
configService: ConfigService, configService: ConfigService,
) { ) {
super({ super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExipration: false, ignoreExipration: false,
secretOrKey: configService.get<string>("JWT.refresh.secret"), secretOrKey: configService.get<string>("JWT.refresh.secret"),
}); });
} }
async validate(payload: any): Promise<User> { async validate(payload: any): Promise<User> {
const user = await this.userService.findById(payload.id); const user = await this.userService.findById(payload.id);
if (!user) { if (!user) {
throw new UnauthorizedException("User not found"); throw new UnauthorizedException("User not found");
} }
return user; return user;
} }
} }

View File

@ -1,9 +1,9 @@
import { Injectable, OnModuleInit } from "@nestjs/common"; import { Injectable, OnModuleInit } from "@nestjs/common";
import { PrismaClient } from "@prisma/client"; import { PrismaClient } from "@prisma/client";
@Injectable() @Injectable()
export class PrismaService extends PrismaClient implements OnModuleInit { export class PrismaService extends PrismaClient implements OnModuleInit {
async onModuleInit() { async onModuleInit() {
await this.$connect(); await this.$connect();
} }
} }

View File

@ -1,9 +1,9 @@
import { ArrayMaxSize, ArrayMinSize, IsArray, IsString } from "class-validator"; import { ArrayMaxSize, ArrayMinSize, IsArray, IsString } from "class-validator";
export class BulkDeleteUserDTO { export class BulkDeleteUserDTO {
@IsArray() @IsArray()
@IsString({ each: true }) @IsString({ each: true })
@ArrayMinSize(1) @ArrayMinSize(1)
@ArrayMaxSize(10) @ArrayMaxSize(10)
ids: string[]; ids: string[];
} }

View File

@ -1,8 +1,8 @@
import { ApiProperty } from "@nestjs/swagger"; import { ApiProperty } from "@nestjs/swagger";
import { IsString } from "class-validator"; import { IsString } from "class-validator";
export class CreateUserDTO { export class CreateUserDTO {
@IsString() @IsString()
@ApiProperty() @ApiProperty()
username: string; username: string;
} }

View File

@ -1,12 +1,12 @@
import { PartialType } from "@nestjs/mapped-types"; import { PartialType } from "@nestjs/mapped-types";
import { IsString } from "class-validator"; import { IsString } from "class-validator";
import { CreateUserDTO } from "./create-user.dto"; import { CreateUserDTO } from "./create-user.dto";
export class UpdateUserDTO extends PartialType(CreateUserDTO) { export class UpdateUserDTO extends PartialType(CreateUserDTO) {
@IsString() @IsString()
id: string; id: string;
@IsString() @IsString()
username: string; username: string;
} }

View File

@ -1,22 +1,22 @@
import { ApiProperty, ApiSchema } from "@nestjs/swagger"; import { ApiProperty, ApiSchema } from "@nestjs/swagger";
import { $Enums } from "@prisma/client"; import { $Enums } from "@prisma/client";
import { Expose } from "class-transformer"; import { Expose } from "class-transformer";
@ApiSchema({ name: "User" }) @ApiSchema({ name: "User" })
export class UserEntity { export class UserEntity {
@Expose() @Expose()
@ApiProperty() @ApiProperty()
id: string; id: string;
@Expose() @Expose()
@ApiProperty() @ApiProperty()
username: string; username: string;
@Expose() @Expose()
@ApiProperty() @ApiProperty()
role: $Enums.Role role: $Enums.Role;
constructor(partial: Partial<UserEntity>) { constructor(partial: Partial<UserEntity>) {
Object.assign(this, partial); Object.assign(this, partial);
} }
} }

View File

@ -3,7 +3,7 @@ import { Module } from "@nestjs/common";
import { UserService } from "./user.service"; import { UserService } from "./user.service";
import { PrismaService } from "@Modules/prisma/prisma.service"; import { PrismaService } from "@Modules/prisma/prisma.service";
import { UserController } from './user.controller'; import { UserController } from "./user.controller";
@Module({ @Module({
providers: [UserService, PrismaService], providers: [UserService, PrismaService],

View File

@ -1,69 +1,69 @@
import { Injectable, NotFoundException } from "@nestjs/common"; import { Injectable, NotFoundException } from "@nestjs/common";
import { PrismaService } from "@Modules/prisma/prisma.service"; import { PrismaService } from "@Modules/prisma/prisma.service";
import { CreateUserDTO } from "./dto/create-user.dto"; import { CreateUserDTO } from "./dto/create-user.dto";
import { UpdateUserDTO } from "./dto/update-user.dto"; import { UpdateUserDTO } from "./dto/update-user.dto";
import { Prisma } from "@prisma/client"; import { Prisma } from "@prisma/client";
@Injectable() @Injectable()
export class UserService { export class UserService {
constructor(private readonly prisma: PrismaService) {} constructor(private readonly prisma: PrismaService) {}
async findAll({ async findAll({
include, include,
cursor, cursor,
take, take,
}: { }: {
include?: Prisma.UserInclude; include?: Prisma.UserInclude;
cursor?: string; cursor?: string;
take?: number; take?: number;
} = {}) { } = {}) {
return await this.prisma.user.findMany({ return await this.prisma.user.findMany({
include, include,
cursor: cursor ? { id: cursor } : undefined, cursor: cursor ? { id: cursor } : undefined,
take: take || 10, take: take || 10,
}); });
} }
async findById(id: string) { async findById(id: string) {
return await this.prisma.user.findUnique({ return await this.prisma.user.findUnique({
where: { id }, where: { id },
}); });
} }
async create(createUserInput: CreateUserDTO) { async create(createUserInput: CreateUserDTO) {
return await this.prisma.user.create({ return await this.prisma.user.create({
data: { data: {
username: createUserInput.username, username: createUserInput.username,
}, },
}); });
} }
async update(updateUserInput: UpdateUserDTO) { async update(updateUserInput: UpdateUserDTO) {
return await this.prisma.user.update({ return await this.prisma.user.update({
where: { id: updateUserInput.id }, where: { id: updateUserInput.id },
data: { data: {
username: updateUserInput.username, username: updateUserInput.username,
}, },
}); });
} }
async delete(id: string) { async delete(id: string) {
const exist = await this.prisma.user.findUnique({ where: { id } }); const exist = await this.prisma.user.findUnique({ where: { id } });
if (!exist) throw new NotFoundException("User not found"); if (!exist) throw new NotFoundException("User not found");
return await this.prisma.user.delete({ return await this.prisma.user.delete({
where: { id }, where: { id },
}); });
} }
async bulkDelete(ids: string[]) { async bulkDelete(ids: string[]) {
return await this.prisma.user.deleteMany({ return await this.prisma.user.deleteMany({
where: { where: {
id: { id: {
in: ids, in: ids,
}, },
}, },
}); });
} }
} }

View File

@ -1,17 +1,17 @@
import * as Joi from 'joi'; import * as Joi from "joi";
export const envValidation = Joi.object({ export const envValidation = Joi.object({
DATABASE_URL: Joi.string().required(), DATABASE_URL: Joi.string().required(),
JWT_SECRET: Joi.string().required(), JWT_SECRET: Joi.string().required(),
JWT_EXPIRES_IN: Joi.string().required(), JWT_EXPIRES_IN: Joi.string().required(),
REFRESH_JWT_SECRET: Joi.string().required(), REFRESH_JWT_SECRET: Joi.string().required(),
REFRESH_JWT_EXPIRES_IN: Joi.string().required(), REFRESH_JWT_EXPIRES_IN: Joi.string().required(),
DISCORD_CLIENT_ID: Joi.string().required(), DISCORD_CLIENT_ID: Joi.string().required(),
DISCORD_CLIENT_SECRET: Joi.string().required(), DISCORD_CLIENT_SECRET: Joi.string().required(),
DISCORD_CALLBACK_URL: Joi.string().required(), DISCORD_CALLBACK_URL: Joi.string().required(),
PORT: Joi.number().optional(), PORT: Joi.number().optional(),
}); });