feat: add ConfigService with Env validator
- add Role decorator
This commit is contained in:
parent
a95ed47302
commit
1988673f80
12
.env.example
12
.env.example
@ -1 +1,11 @@
|
||||
JWT_SECRET=
|
||||
DATABASE_URL="mysql://USER:PASS@IP:PORT/DB"
|
||||
|
||||
JWT_SECRET=
|
||||
JWT_EXPIRES_IN=1h
|
||||
|
||||
REFRESH_JWT_SECRET=
|
||||
REFRESH_JWT_EXPIRES_IN=7d
|
||||
|
||||
DISCORD_CLIENT_ID=
|
||||
DISCORD_CLIENT_SECRET=
|
||||
DISCORD_CALLBACK_URL=http://localhost:3000/auth/discord/callback
|
@ -25,6 +25,7 @@
|
||||
"axios": "^1.7.7",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"joi": "^17.13.3",
|
||||
"passport": "^0.7.0",
|
||||
"passport-discord": "^0.1.4",
|
||||
"passport-jwt": "^4.0.1",
|
||||
@ -38,6 +39,7 @@
|
||||
"@types/express": "^5.0.0",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/passport-discord": "^0.1.14",
|
||||
"@types/passport-jwt": "^4.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.0.0",
|
||||
"@typescript-eslint/parser": "^8.0.0",
|
||||
"eslint": "^9.0.0",
|
||||
|
@ -60,5 +60,5 @@ model RoomDocument {
|
||||
|
||||
enum Role {
|
||||
STUDENT
|
||||
TEACHER
|
||||
ADMIN
|
||||
}
|
||||
|
@ -1,24 +1,31 @@
|
||||
import env from "@Config/env";
|
||||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { JwtModule } from "@nestjs/jwt";
|
||||
import { envValidation } from "@Validations/env.validation";
|
||||
|
||||
import { AppService } from "./app.service";
|
||||
import { UserModule } from "./user/user.module";
|
||||
|
||||
import { AuthModule } from './auth/auth.module';
|
||||
import { AuthModule } from "@Modules/auth/auth.module";
|
||||
import { UserModule } from "@Modules/user/user.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
load: [env],
|
||||
validationSchema: envValidation,
|
||||
}),
|
||||
JwtModule.register({
|
||||
JwtModule.registerAsync({
|
||||
global: true,
|
||||
secret: process.env.JWT_SECRET,
|
||||
inject: [ConfigService],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
secret: configService.get<string>("JWT.secret"),
|
||||
signOptions: {
|
||||
expiresIn: configService.get<string>("JWT.expiresIn"),
|
||||
},
|
||||
}),
|
||||
}),
|
||||
UserModule,
|
||||
AuthModule,
|
||||
],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
@ -1,10 +0,0 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getHello() {
|
||||
return {
|
||||
message: "Hello World!",
|
||||
};
|
||||
}
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { JwtService } from "@nestjs/jwt";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { Profile, Strategy } from "passport-discord";
|
||||
|
||||
@Injectable()
|
||||
export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
|
||||
constructor(private jwtService: JwtService) {
|
||||
super({
|
||||
clientID: process.env.DISCORD_CLIENT_ID,
|
||||
clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
||||
callbackURL: process.env.DISCORD_CALLBACK_URL,
|
||||
scope: ["identify", "email"]
|
||||
});
|
||||
}
|
||||
|
||||
async validate(
|
||||
_accessToken: string,
|
||||
_refreshToken: string,
|
||||
profile: Profile,
|
||||
done: Function,
|
||||
) {
|
||||
const jwtPayload = {
|
||||
id: profile.id,
|
||||
username: profile.username,
|
||||
email: profile.email,
|
||||
};
|
||||
|
||||
const jwt = this.jwtService.sign(jwtPayload);
|
||||
|
||||
done(null, { jwt });
|
||||
}
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { Profile } from "passport-discord";
|
||||
import { Strategy, ExtractJwt } from "passport-jwt";
|
||||
|
||||
@Injectable()
|
||||
export class JWTStrategy extends PassportStrategy(Strategy) {
|
||||
constructor() {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExipration: false,
|
||||
secretOrKey: process.env.JWT_SECRET,
|
||||
});
|
||||
}
|
||||
|
||||
async validate(profile: Profile): Promise<any> {
|
||||
return profile;
|
||||
}
|
||||
}
|
17
src/config/env.ts
Normal file
17
src/config/env.ts
Normal file
@ -0,0 +1,17 @@
|
||||
export default () => ({
|
||||
JWT: {
|
||||
secret: process.env.JWT_SECRET,
|
||||
expiresIn: process.env.JWT_EXPIRES_IN,
|
||||
refresh: {
|
||||
secret: process.env.REFRESH_JWT_SECRET,
|
||||
expiresIn: process.env.REFRESH_JWT_EXPIRES_IN,
|
||||
},
|
||||
},
|
||||
oauth2: {
|
||||
discord: {
|
||||
clientId: process.env.DISCORD_CLIENT_ID,
|
||||
clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
||||
callbackUrl: process.env.DISCORD_CALLBACK_URL,
|
||||
},
|
||||
},
|
||||
});
|
@ -1,6 +1,6 @@
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { NestFactory, Reflector } from "@nestjs/core";
|
||||
import { AppModule } from "./app.module";
|
||||
import { ValidationPipe } from "@nestjs/common";
|
||||
import { ClassSerializerInterceptor, ValidationPipe } from "@nestjs/common";
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
@ -11,6 +11,10 @@ async function bootstrap() {
|
||||
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
|
||||
app.useGlobalInterceptors(new ClassSerializerInterceptor(app.get(Reflector), {
|
||||
excludeExtraneousValues: true,
|
||||
}));
|
||||
|
||||
await app.listen(process.env.PORT ?? 3000, "0.0.0.0");
|
||||
}
|
||||
bootstrap();
|
||||
|
4
src/modules/auth/Decorators/roles.decorator.ts
Normal file
4
src/modules/auth/Decorators/roles.decorator.ts
Normal file
@ -0,0 +1,4 @@
|
||||
import { SetMetadata } from "@nestjs/common";
|
||||
import { $Enums } from "@prisma/client";
|
||||
|
||||
export const Role = (role: $Enums.Role) => SetMetadata("role", role);
|
39
src/modules/auth/Guards/role.guard.ts
Normal file
39
src/modules/auth/Guards/role.guard.ts
Normal file
@ -0,0 +1,39 @@
|
||||
import {
|
||||
Injectable,
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
ForbiddenException,
|
||||
UnauthorizedException,
|
||||
} from "@nestjs/common";
|
||||
import { Reflector } from "@nestjs/core";
|
||||
|
||||
@Injectable()
|
||||
export class RolesGuard implements CanActivate {
|
||||
constructor(private readonly reflector: Reflector) {}
|
||||
|
||||
canActivate(context: ExecutionContext): boolean {
|
||||
const role = this.reflector.get<string>(
|
||||
"role",
|
||||
context.getHandler(),
|
||||
);
|
||||
if (!role) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user = request.user;
|
||||
|
||||
if (!user) {
|
||||
throw new ForbiddenException("User not authenticated");
|
||||
}
|
||||
|
||||
const hasRole = role === user.role;
|
||||
if (!hasRole) {
|
||||
throw new UnauthorizedException(
|
||||
`You need to have the role ${role} to access this resource`,
|
||||
);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
37
src/modules/auth/Strategy/discord.strategy.ts
Normal file
37
src/modules/auth/Strategy/discord.strategy.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { JwtService } from "@nestjs/jwt";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { Profile, Strategy } from "passport-discord";
|
||||
|
||||
@Injectable()
|
||||
export class DiscordStrategy extends PassportStrategy(Strategy, "discord") {
|
||||
constructor(
|
||||
private readonly jwtService: JwtService,
|
||||
configService: ConfigService,
|
||||
) {
|
||||
super({
|
||||
clientID: configService.get<string>("oauth2.discord.clientId"),
|
||||
clientSecret: configService.get<string>(
|
||||
"oauth2.discord.clientSecret",
|
||||
),
|
||||
callbackURL: configService.get<string>(
|
||||
"oauth2.discord.callbackUrl",
|
||||
),
|
||||
scope: ["identify", "email"],
|
||||
});
|
||||
}
|
||||
|
||||
async validate(
|
||||
_accessToken: string,
|
||||
_refreshToken: string,
|
||||
profile: Profile,
|
||||
done: Function,
|
||||
) {
|
||||
const jwt = this.jwtService.sign({
|
||||
id: profile.id,
|
||||
});
|
||||
|
||||
done(null, { jwt });
|
||||
}
|
||||
}
|
30
src/modules/auth/Strategy/jwt.strategy.ts
Normal file
30
src/modules/auth/Strategy/jwt.strategy.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { Injectable, UnauthorizedException } from "@nestjs/common";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { User } from "@prisma/client";
|
||||
import { Strategy, ExtractJwt } from "passport-jwt";
|
||||
import { UserService } from "@Modules/user/user.service";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
|
||||
@Injectable()
|
||||
export class JWTStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(
|
||||
private readonly userService: UserService,
|
||||
private readonly configService: ConfigService,
|
||||
) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExipration: false,
|
||||
secretOrKey: configService.get<string>("JWT.secret"),
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any): Promise<User> {
|
||||
const user = await this.userService.findById(payload.id);
|
||||
|
||||
if (!user) {
|
||||
throw new UnauthorizedException("User not found");
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
@ -1,16 +1,18 @@
|
||||
import { Controller, Get, Query, Req, Res, UseGuards } from "@nestjs/common";
|
||||
import { Controller, Get, Req, Res, UseGuards } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { URLSearchParams } from "node:url";
|
||||
import { JwtAuthGuard } from "./Guards/jwt.guard";
|
||||
import { DiscordAuthGuard } from "./Guards/discord.guard";
|
||||
|
||||
@Controller("auth")
|
||||
export class AuthController {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
@Get("providers")
|
||||
Providers() {
|
||||
getProviders() {
|
||||
const discordOauth2Params = new URLSearchParams({
|
||||
client_id: process.env.DISCORD_CLIENT_ID,
|
||||
client_id: this.configService.get<string>("oauth2.discord.clientId"),
|
||||
response_type: "code",
|
||||
redirect_uri: process.env.DISCORD_CALLBACK_URL,
|
||||
redirect_uri: this.configService.get<string>("oauth2.discord.callbackUrl"),
|
||||
scope: "identify email",
|
||||
});
|
||||
|
||||
@ -28,10 +30,4 @@ export class AuthController {
|
||||
|
||||
res.send(user);
|
||||
}
|
||||
|
||||
@Get("profile")
|
||||
@UseGuards(JwtAuthGuard)
|
||||
Profile(@Req() req) {
|
||||
return req.user;
|
||||
}
|
||||
}
|
@ -2,10 +2,12 @@ import { Module } from "@nestjs/common";
|
||||
import { AuthController } from "./auth.controller";
|
||||
import { AuthService } from "./auth.service";
|
||||
|
||||
import { UserModule } from "@Modules/user/user.module";
|
||||
import { DiscordStrategy } from "./Strategy/discord.strategy";
|
||||
import { JWTStrategy } from "./Strategy/jwt.strategy";
|
||||
|
||||
@Module({
|
||||
imports: [UserModule],
|
||||
controllers: [AuthController],
|
||||
providers: [AuthService, DiscordStrategy, JWTStrategy],
|
||||
})
|
9
src/modules/user/dto/bulk-delete-user.dto.ts
Normal file
9
src/modules/user/dto/bulk-delete-user.dto.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import { ArrayMaxSize, ArrayMinSize, IsArray, IsString } from "class-validator";
|
||||
|
||||
export class BulkDeleteUserDTO {
|
||||
@IsArray()
|
||||
@IsString({ each: true })
|
||||
@ArrayMinSize(1)
|
||||
@ArrayMaxSize(10)
|
||||
ids: string[];
|
||||
}
|
6
src/modules/user/dto/create-user.dto.ts
Normal file
6
src/modules/user/dto/create-user.dto.ts
Normal file
@ -0,0 +1,6 @@
|
||||
import { IsString } from "class-validator";
|
||||
|
||||
export class CreateUserDTO {
|
||||
@IsString()
|
||||
username: string;
|
||||
}
|
@ -1,9 +1,9 @@
|
||||
import { PartialType } from "@nestjs/mapped-types";
|
||||
import { IsString } from "class-validator";
|
||||
|
||||
import { CreateUserInput } from "./create-user.input";
|
||||
import { CreateUserDTO } from "./create-user.dto";
|
||||
|
||||
export class UpdateUserInput extends PartialType(CreateUserInput) {
|
||||
export class UpdateUserDTO extends PartialType(CreateUserDTO) {
|
||||
@IsString()
|
||||
id: string;
|
||||
|
52
src/modules/user/user.controller.ts
Normal file
52
src/modules/user/user.controller.ts
Normal file
@ -0,0 +1,52 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Post,
|
||||
Query,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
|
||||
import { Role } from "@Modules/auth/Decorators/roles.decorator";
|
||||
import { JwtAuthGuard } from "@Modules/auth/Guards/jwt.guard";
|
||||
import { RolesGuard } from "@Modules/auth/Guards/role.guard";
|
||||
|
||||
import { BulkDeleteUserDTO } from "./dto/bulk-delete-user.dto";
|
||||
import { CreateUserDTO } from "./dto/create-user.dto";
|
||||
|
||||
import { UserEntity } from "./user.entity";
|
||||
import { UserService } from "./user.service";
|
||||
|
||||
@Controller("user")
|
||||
@UseGuards(RolesGuard)
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class UserController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
@Get()
|
||||
@Role("ADMIN")
|
||||
async findAll(): Promise<UserEntity[]> {
|
||||
const users = await this.userService.findAll();
|
||||
|
||||
return users.map((user) => new UserEntity(user));
|
||||
}
|
||||
|
||||
@Post()
|
||||
@Role("ADMIN")
|
||||
create(@Body() createUserInput: CreateUserDTO) {
|
||||
return this.userService.create(createUserInput);
|
||||
}
|
||||
|
||||
@Delete()
|
||||
@Role("ADMIN")
|
||||
delete(@Query("id") id: string) {
|
||||
return this.userService.delete(id);
|
||||
}
|
||||
|
||||
@Delete("/bulk")
|
||||
@Role("ADMIN")
|
||||
bulkDelete(@Body() { ids }: BulkDeleteUserDTO) {
|
||||
return this.userService.bulkDelete(ids);
|
||||
}
|
||||
}
|
17
src/modules/user/user.entity.ts
Normal file
17
src/modules/user/user.entity.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import { $Enums } from "@prisma/client";
|
||||
import { Expose } from "class-transformer";
|
||||
|
||||
export class UserEntity {
|
||||
@Expose()
|
||||
id: string;
|
||||
|
||||
@Expose()
|
||||
username: string;
|
||||
|
||||
@Expose()
|
||||
role: $Enums.Role
|
||||
|
||||
constructor(partial: Partial<UserEntity>) {
|
||||
Object.assign(this, partial);
|
||||
}
|
||||
}
|
@ -2,11 +2,12 @@ import { Module } from "@nestjs/common";
|
||||
|
||||
import { UserService } from "./user.service";
|
||||
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
import { PrismaService } from "@Modules/prisma/prisma.service";
|
||||
import { UserController } from './user.controller';
|
||||
|
||||
@Module({
|
||||
providers: [UserService, PrismaService],
|
||||
controllers: [UserController]
|
||||
controllers: [UserController],
|
||||
exports: [UserService],
|
||||
})
|
||||
export class UserModule {}
|
56
src/modules/user/user.service.ts
Normal file
56
src/modules/user/user.service.ts
Normal file
@ -0,0 +1,56 @@
|
||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { PrismaService } from "@Modules/prisma/prisma.service";
|
||||
|
||||
import { CreateUserDTO } from "./dto/create-user.dto";
|
||||
import { UpdateUserDTO } from "./dto/update-user.dto";
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async findAll() {
|
||||
return await this.prisma.user.findMany();
|
||||
}
|
||||
|
||||
async findById(id: string) {
|
||||
return await this.prisma.user.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
async create(createUserInput: CreateUserDTO) {
|
||||
return await this.prisma.user.create({
|
||||
data: {
|
||||
username: createUserInput.username,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async update(updateUserInput: UpdateUserDTO) {
|
||||
return await this.prisma.user.update({
|
||||
where: { id: updateUserInput.id },
|
||||
data: {
|
||||
username: updateUserInput.username,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async delete(id: string) {
|
||||
const exist = await this.prisma.user.findUnique({ where: { id } });
|
||||
if (!exist) throw new NotFoundException("User not found");
|
||||
|
||||
return await this.prisma.user.delete({
|
||||
where: { id },
|
||||
});
|
||||
}
|
||||
|
||||
async bulkDelete(ids: string[]) {
|
||||
return await this.prisma.user.deleteMany({
|
||||
where: {
|
||||
id: {
|
||||
in: ids,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { IsBoolean, IsString } from "class-validator";
|
||||
|
||||
export class CreateUserInput {
|
||||
@IsString()
|
||||
username: string;
|
||||
|
||||
@IsBoolean()
|
||||
isAdmin: boolean;
|
||||
}
|
@ -1,9 +0,0 @@
|
||||
import { IsString } from "class-validator";
|
||||
|
||||
export class SetUserPasswordInput {
|
||||
@IsString()
|
||||
id: string;
|
||||
|
||||
@IsString()
|
||||
password: string;
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
import {
|
||||
Body,
|
||||
Controller,
|
||||
Delete,
|
||||
Get,
|
||||
Param,
|
||||
Post,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
import { UserService } from "./user.service";
|
||||
|
||||
import { JwtAuthGuard } from "src/auth/Guards/jwt.guard";
|
||||
import { CreateUserInput } from "./dto/create-user.input";
|
||||
|
||||
@Controller("user")
|
||||
@UseGuards(JwtAuthGuard)
|
||||
export class UserController {
|
||||
constructor(private readonly userService: UserService) {}
|
||||
|
||||
@Post()
|
||||
create(@Body() createUserInput: CreateUserInput) {
|
||||
return this.userService.create(createUserInput);
|
||||
}
|
||||
|
||||
@Get()
|
||||
findAll() {
|
||||
return this.userService.findAll();
|
||||
}
|
||||
|
||||
@Delete(":id")
|
||||
removeUser(@Param("id") id: string) {
|
||||
return this.userService.remove(id);
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
import { Exclude } from "class-transformer";
|
||||
|
||||
export class UserEntity {
|
||||
id: string;
|
||||
|
||||
username: string;
|
||||
|
||||
isAdmin: boolean;
|
||||
|
||||
@Exclude()
|
||||
password: string;
|
||||
}
|
@ -1,77 +0,0 @@
|
||||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { plainToClass } from "class-transformer";
|
||||
import { PrismaService } from "src/prisma/prisma.service";
|
||||
|
||||
import { CreateUserInput } from "./dto/create-user.input";
|
||||
import { UpdateUserInput } from "./dto/update-user.input";
|
||||
import { SetUserPasswordInput } from "./dto/setpassword-user.input";
|
||||
import { UserEntity } from "./user.entity";
|
||||
|
||||
@Injectable()
|
||||
export class UserService {
|
||||
constructor(private readonly prisma: PrismaService) {}
|
||||
|
||||
async create(createUserInput: CreateUserInput) {
|
||||
const user = await this.prisma.user.create({
|
||||
data: {
|
||||
username: createUserInput.username,
|
||||
isAdmin: createUserInput.isAdmin,
|
||||
},
|
||||
});
|
||||
|
||||
return plainToClass(UserEntity, user);
|
||||
}
|
||||
|
||||
async update(updateUserInput: UpdateUserInput) {
|
||||
const user = await this.prisma.user.update({
|
||||
where: { id: updateUserInput.id },
|
||||
data: {
|
||||
username: updateUserInput.username,
|
||||
isAdmin: updateUserInput.isAdmin,
|
||||
},
|
||||
});
|
||||
|
||||
return plainToClass(UserEntity, user);
|
||||
}
|
||||
|
||||
async setPassword(setUserPasswordInput: SetUserPasswordInput) {
|
||||
const exist = await this.prisma.user.findUnique({
|
||||
where: { id: setUserPasswordInput.id },
|
||||
});
|
||||
if (!exist) throw new NotFoundException("User not found");
|
||||
|
||||
const user = await this.prisma.user.update({
|
||||
where: { id: setUserPasswordInput.id },
|
||||
data: {
|
||||
password: setUserPasswordInput.password,
|
||||
},
|
||||
});
|
||||
|
||||
return plainToClass(UserEntity, user);
|
||||
}
|
||||
|
||||
async findAll() {
|
||||
const users = await this.prisma.user.findMany();
|
||||
|
||||
return users.map((user) => plainToClass(UserEntity, user));
|
||||
}
|
||||
|
||||
async findOne(id: string) {
|
||||
const user = await this.prisma.user.findUnique({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return plainToClass(UserEntity, user);
|
||||
}
|
||||
|
||||
async remove(id: string) {
|
||||
const exist = await this.prisma.user.findUnique({ where: { id } });
|
||||
if (!exist) throw new NotFoundException("User not found");
|
||||
|
||||
const user = await this.prisma.user.delete({
|
||||
where: { id },
|
||||
});
|
||||
|
||||
return plainToClass(UserEntity, user);
|
||||
}
|
||||
}
|
17
src/validations/env.validation.ts
Normal file
17
src/validations/env.validation.ts
Normal file
@ -0,0 +1,17 @@
|
||||
import * as Joi from 'joi';
|
||||
|
||||
export const envValidation = Joi.object({
|
||||
DATABASE_URL: Joi.string().required(),
|
||||
|
||||
JWT_SECRET: Joi.string().required(),
|
||||
JWT_EXPIRES_IN: Joi.string().required(),
|
||||
|
||||
REFRESH_JWT_SECRET: Joi.string().required(),
|
||||
REFRESH_JWT_EXPIRES_IN: Joi.string().required(),
|
||||
|
||||
DISCORD_CLIENT_ID: Joi.string().required(),
|
||||
DISCORD_CLIENT_SECRET: Joi.string().required(),
|
||||
DISCORD_CALLBACK_URL: Joi.string().required(),
|
||||
|
||||
PORT: Joi.number().optional(),
|
||||
});
|
@ -9,7 +9,14 @@
|
||||
"target": "ES2021",
|
||||
"sourceMap": true,
|
||||
"outDir": "./dist",
|
||||
"baseUrl": "./",
|
||||
"baseUrl": "./src",
|
||||
"paths": {
|
||||
"@/*": ["*"],
|
||||
"@Modules/*": ["modules/*"],
|
||||
"@Interfaces/*": ["interfaces/*"],
|
||||
"@Config/*": ["config/*"],
|
||||
"@Validations/*": ["validations/*"],
|
||||
},
|
||||
"incremental": true,
|
||||
"skipLibCheck": true,
|
||||
"strictNullChecks": false,
|
||||
|
63
yarn.lock
63
yarn.lock
@ -121,6 +121,18 @@
|
||||
dependencies:
|
||||
levn "^0.4.1"
|
||||
|
||||
"@hapi/hoek@^9.0.0", "@hapi/hoek@^9.3.0":
|
||||
version "9.3.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
|
||||
integrity sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==
|
||||
|
||||
"@hapi/topo@^5.1.0":
|
||||
version "5.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@hapi/topo/-/topo-5.1.0.tgz#dc448e332c6c6e37a4dc02fd84ba8d44b9afb012"
|
||||
integrity sha512-foQZKJig7Ob0BMAYBfcJk8d77QtOe7Wo4ox7ff1lQYoNNAb6jwcY1ncdoy2e9wQZzvNy7ODZCYJkK8kzmcAnAg==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@humanfs/core@^0.19.1":
|
||||
version "0.19.1"
|
||||
resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77"
|
||||
@ -397,6 +409,23 @@
|
||||
dependencies:
|
||||
"@prisma/debug" "5.22.0"
|
||||
|
||||
"@sideway/address@^4.1.5":
|
||||
version "4.1.5"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
|
||||
integrity sha512-IqO/DUQHUkPeixNQ8n0JA6102hT9CmaljNTPmQ1u8MEhBo/R4Q8eKLN/vGZxuebwOroDB4cbpjheD4+/sKFK4Q==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.0.0"
|
||||
|
||||
"@sideway/formula@^3.0.1":
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/formula/-/formula-3.0.1.tgz#80fcbcbaf7ce031e0ef2dd29b1bfc7c3f583611f"
|
||||
integrity sha512-/poHZJJVjx3L+zVD6g9KgHfYnb443oi7wLu/XKojDviHy6HOEOA6z1Trk5aR1dGcmPenJEgb2sK2I80LeS3MIg==
|
||||
|
||||
"@sideway/pinpoint@^2.0.0":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@sideway/pinpoint/-/pinpoint-2.0.0.tgz#cff8ffadc372ad29fd3f78277aeb29e632cc70df"
|
||||
integrity sha512-RNiOoTPkptFtSVzQevY/yWtZwf/RxyVnPy/OcA9HBM3MlGDnBEYL5B41H0MTn0Uec8Hi+2qUtTfG2WWZBmMejQ==
|
||||
|
||||
"@tsconfig/node10@^1.0.7":
|
||||
version "1.0.11"
|
||||
resolved "https://registry.yarnpkg.com/@tsconfig/node10/-/node10-1.0.11.tgz#6ee46400685f130e278128c7b38b7e031ff5b2f2"
|
||||
@ -483,6 +512,13 @@
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
|
||||
"@types/jsonwebtoken@*":
|
||||
version "9.0.7"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.7.tgz#e49b96c2b29356ed462e9708fc73b833014727d2"
|
||||
integrity sha512-ugo316mmTYBl2g81zDFnZ7cfxlut3o+/EQdaP7J8QN2kY6lJ22hmQYCK5EHcJHbrW+dkCGSCPgbG8JtYj6qSrg==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/jsonwebtoken@9.0.5":
|
||||
version "9.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz#0bd9b841c9e6c5a937c17656e2368f65da025588"
|
||||
@ -525,6 +561,14 @@
|
||||
"@types/passport" "*"
|
||||
"@types/passport-oauth2" "*"
|
||||
|
||||
"@types/passport-jwt@^4.0.1":
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-jwt/-/passport-jwt-4.0.1.tgz#080fbe934fb9f6954fb88ec4cdf4bb2cc7c4d435"
|
||||
integrity sha512-Y0Ykz6nWP4jpxgEUYq8NoVZeCQPo1ZndJLfapI249g1jHChvRfZRO/LS3tqu26YgAS/laI1qx98sYGz0IalRXQ==
|
||||
dependencies:
|
||||
"@types/jsonwebtoken" "*"
|
||||
"@types/passport-strategy" "*"
|
||||
|
||||
"@types/passport-oauth2@*":
|
||||
version "1.4.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-oauth2/-/passport-oauth2-1.4.17.tgz#d5d54339d44f6883d03e69dc0cc0e2114067abb4"
|
||||
@ -534,6 +578,14 @@
|
||||
"@types/oauth" "*"
|
||||
"@types/passport" "*"
|
||||
|
||||
"@types/passport-strategy@*":
|
||||
version "0.2.38"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport-strategy/-/passport-strategy-0.2.38.tgz#482abba0b165cd4553ec8b748f30b022bd6c04d3"
|
||||
integrity sha512-GC6eMqqojOooq993Tmnmp7AUTbbQSgilyvpCYQjT+H6JfG/g6RGc7nXEniZlp0zyKJ0WUdOiZWLBZft9Yug1uA==
|
||||
dependencies:
|
||||
"@types/express" "*"
|
||||
"@types/passport" "*"
|
||||
|
||||
"@types/passport@*":
|
||||
version "1.0.17"
|
||||
resolved "https://registry.yarnpkg.com/@types/passport/-/passport-1.0.17.tgz#718a8d1f7000ebcf6bbc0853da1bc8c4bc7ea5e6"
|
||||
@ -2057,6 +2109,17 @@ jest-worker@^27.4.5:
|
||||
merge-stream "^2.0.0"
|
||||
supports-color "^8.0.0"
|
||||
|
||||
joi@^17.13.3:
|
||||
version "17.13.3"
|
||||
resolved "https://registry.yarnpkg.com/joi/-/joi-17.13.3.tgz#0f5cc1169c999b30d344366d384b12d92558bcec"
|
||||
integrity sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==
|
||||
dependencies:
|
||||
"@hapi/hoek" "^9.3.0"
|
||||
"@hapi/topo" "^5.1.0"
|
||||
"@sideway/address" "^4.1.5"
|
||||
"@sideway/formula" "^3.0.1"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
|
Loading…
Reference in New Issue
Block a user