Add various module, service, and DTO files for new features
Integrate modules and services related to crypto, user, role, offer, and promoCode functionalities. Includes DTOs for validating data shapes, and services to handle business logic, with controller endpoints for different operations.
This commit is contained in:
parent
313a51f8db
commit
fa91630a29
22
src/app.controller.spec.ts
Normal file
22
src/app.controller.spec.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { AppController } from './app.controller';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
describe('AppController', () => {
|
||||||
|
let appController: AppController;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const app: TestingModule = await Test.createTestingModule({
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [AppService],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
appController = app.get<AppController>(AppController);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('root', () => {
|
||||||
|
it('should return "Hello World!"', () => {
|
||||||
|
expect(appController.getHello()).toBe('Hello World!');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
12
src/app.controller.ts
Normal file
12
src/app.controller.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
@Controller()
|
||||||
|
export class AppController {
|
||||||
|
constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getHello(): string {
|
||||||
|
return 'Hello';
|
||||||
|
}
|
||||||
|
}
|
38
src/app.module.ts
Normal file
38
src/app.module.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AppController } from '@/app.controller';
|
||||||
|
import { AppService } from '@/app.service';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
import { AuthModule } from '@/auth/auth.module';
|
||||||
|
import { PrismaModule } from '@/prisma/prisma.module';
|
||||||
|
import { RoleModule } from '@/role/role.module';
|
||||||
|
import { PromoCodeModule } from '@/promoCode/promoCode.module';
|
||||||
|
import { CryptoModule } from '@/crypto/crypto.module';
|
||||||
|
import { TradeModule } from '@/trade/trade.module';
|
||||||
|
import { OfferModule } from '@/offer/offer.module';
|
||||||
|
import { UserModule } from '@/user/user.module';
|
||||||
|
import { ThrottlerModule } from '@nestjs/throttler';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true,
|
||||||
|
}),
|
||||||
|
AuthModule,
|
||||||
|
PrismaModule,
|
||||||
|
RoleModule,
|
||||||
|
PromoCodeModule,
|
||||||
|
CryptoModule,
|
||||||
|
TradeModule,
|
||||||
|
OfferModule,
|
||||||
|
UserModule,
|
||||||
|
ThrottlerModule.forRoot([
|
||||||
|
{
|
||||||
|
ttl: 60000,
|
||||||
|
limit: 40,
|
||||||
|
},
|
||||||
|
]),
|
||||||
|
],
|
||||||
|
controllers: [AppController],
|
||||||
|
providers: [AppService],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
8
src/app.service.ts
Normal file
8
src/app.service.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AppService {
|
||||||
|
getHello(): string {
|
||||||
|
return 'Hello World!';
|
||||||
|
}
|
||||||
|
}
|
21
src/auth/auth.controller.ts
Normal file
21
src/auth/auth.controller.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { Body, Controller, HttpCode, HttpStatus, Post } from '@nestjs/common';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { AuthLoginDto, AuthRegisterDto } from './dto';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
@ApiTags('auth')
|
||||||
|
@Controller('auth')
|
||||||
|
export class AuthController {
|
||||||
|
constructor(private authService: AuthService) {}
|
||||||
|
|
||||||
|
@Post('sign-up')
|
||||||
|
signUp(@Body() dto: AuthRegisterDto) {
|
||||||
|
return this.authService.signup(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Post('sign-in')
|
||||||
|
signIn(@Body() dto: AuthLoginDto) {
|
||||||
|
return this.authService.signin(dto);
|
||||||
|
}
|
||||||
|
}
|
12
src/auth/auth.module.ts
Normal file
12
src/auth/auth.module.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
import { AuthController } from './auth.controller';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { JwtStrategy } from './strategy';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [JwtModule.register({})],
|
||||||
|
controllers: [AuthController],
|
||||||
|
providers: [AuthService, JwtStrategy],
|
||||||
|
})
|
||||||
|
export class AuthModule {}
|
@ -56,10 +56,8 @@ export class AuthService {
|
|||||||
firstName: dto.firstName,
|
firstName: dto.firstName,
|
||||||
lastName: dto.lastName,
|
lastName: dto.lastName,
|
||||||
pseudo: dto.pseudo,
|
pseudo: dto.pseudo,
|
||||||
city: dto.city,
|
|
||||||
email: dto.email,
|
email: dto.email,
|
||||||
hash,
|
hash,
|
||||||
age: dto.age,
|
|
||||||
roleId,
|
roleId,
|
||||||
isActive: true,
|
isActive: true,
|
||||||
dollarAvailables: balance,
|
dollarAvailables: balance,
|
||||||
|
11
src/auth/decorator/get-user.decorator.ts
Normal file
11
src/auth/decorator/get-user.decorator.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||||
|
|
||||||
|
export const GetUser = createParamDecorator(
|
||||||
|
(data: string | undefined, ctx: ExecutionContext) => {
|
||||||
|
const request: Express.Request = ctx.switchToHttp().getRequest();
|
||||||
|
if (data) {
|
||||||
|
return request.user[data];
|
||||||
|
}
|
||||||
|
return request.user;
|
||||||
|
},
|
||||||
|
);
|
1
src/auth/decorator/index.ts
Normal file
1
src/auth/decorator/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './get-user.decorator';
|
13
src/auth/dto/auth.login.dto.ts
Normal file
13
src/auth/dto/auth.login.dto.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { IsEmail, IsNotEmpty, IsString } from 'class-validator';
|
||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
export class AuthLoginDto {
|
||||||
|
@IsEmail()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@ApiProperty({ type: String, description: 'email' })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@ApiProperty({ type: String, description: 'password' })
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
password: string;
|
||||||
|
}
|
73
src/auth/dto/auth.register.dto.ts
Normal file
73
src/auth/dto/auth.register.dto.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsEmail,
|
||||||
|
IsInt,
|
||||||
|
IsNotEmpty,
|
||||||
|
IsOptional,
|
||||||
|
IsString,
|
||||||
|
Max,
|
||||||
|
MaxLength,
|
||||||
|
Min,
|
||||||
|
MinLength,
|
||||||
|
} from 'class-validator';
|
||||||
|
export class AuthRegisterDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'FirstName',
|
||||||
|
example: 'Thomas',
|
||||||
|
})
|
||||||
|
@MinLength(1)
|
||||||
|
@MaxLength(50)
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
firstName: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Last Name',
|
||||||
|
example: 'Anderson',
|
||||||
|
})
|
||||||
|
@MinLength(1)
|
||||||
|
@MaxLength(50)
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
lastName: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Pseudo',
|
||||||
|
example: 'Néo',
|
||||||
|
})
|
||||||
|
@MinLength(1)
|
||||||
|
@MaxLength(50)
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
pseudo: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'email',
|
||||||
|
example: 'neo@matrix.fr',
|
||||||
|
})
|
||||||
|
@MaxLength(255)
|
||||||
|
@IsEmail()
|
||||||
|
@IsNotEmpty()
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'password',
|
||||||
|
example: 'AAaa11&&&&',
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsNotEmpty()
|
||||||
|
password: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'promoCode',
|
||||||
|
example: 'FILOU20',
|
||||||
|
})
|
||||||
|
@IsOptional()
|
||||||
|
promoCode: string;
|
||||||
|
}
|
2
src/auth/dto/index.ts
Normal file
2
src/auth/dto/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './auth.register.dto';
|
||||||
|
export * from './auth.login.dto';
|
1
src/auth/guard/index.ts
Normal file
1
src/auth/guard/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './jwt.guard';
|
7
src/auth/guard/jwt.guard.ts
Normal file
7
src/auth/guard/jwt.guard.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
|
||||||
|
export class JwtGuard extends AuthGuard('jwt') {
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
}
|
||||||
|
}
|
1
src/auth/strategy/index.ts
Normal file
1
src/auth/strategy/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './jwt.strategy';
|
28
src/auth/strategy/jwt.strategy.ts
Normal file
28
src/auth/strategy/jwt.strategy.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||||
|
import { PrismaService } from "@/prisma/prisma.service";
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||||
|
constructor(
|
||||||
|
config: ConfigService,
|
||||||
|
private prisma: PrismaService,
|
||||||
|
) {
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
secretOrKey: config.get('JWT_SECRET'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(payload: { sub: string; email: string }) {
|
||||||
|
const user = await this.prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: payload.sub,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
delete user.hash;
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
73
src/crypto/crypto.controller.ts
Normal file
73
src/crypto/crypto.controller.ts
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { GetUser } from '../auth/decorator';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
import { CryptoService } from './crypto.service';
|
||||||
|
import { CryptoDto } from './dto';
|
||||||
|
import { JwtGuard } from 'src/auth/guard';
|
||||||
|
import { BuyCryptoDto } from './dto/buy.crypto.dto';
|
||||||
|
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
@ApiTags('crypto')
|
||||||
|
@Controller('crypto')
|
||||||
|
export class CryptoController {
|
||||||
|
constructor(private cryptoService: CryptoService) {}
|
||||||
|
|
||||||
|
@Get('/all')
|
||||||
|
getAllPromoCodes(@GetUser() user: User) {
|
||||||
|
return this.cryptoService.getCryptos(user.id);
|
||||||
|
}
|
||||||
|
@Get('/search/:name')
|
||||||
|
searchCrypto(@GetUser() user: User, @Param('name') cryptoName: string) {
|
||||||
|
return this.cryptoService.searchCryptos(user.id, cryptoName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/history/:id')
|
||||||
|
CryptoHistory(@GetUser() user: User, @Param('id') cryptoId: string) {
|
||||||
|
return this.cryptoService.getCryptoHistory(user.id, cryptoId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
@Post('/create')
|
||||||
|
createPromoCode(
|
||||||
|
@Body()
|
||||||
|
dto: CryptoDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.cryptoService.createCrypto(user.id, dto);
|
||||||
|
}
|
||||||
|
@Post('/buy')
|
||||||
|
buyCrypto(
|
||||||
|
@Body()
|
||||||
|
dto: BuyCryptoDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.cryptoService.buyCrypto(user.id, dto);
|
||||||
|
}
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Patch('/update/:id')
|
||||||
|
editCryptoById(
|
||||||
|
@Param('id') cryptoId: string,
|
||||||
|
@Body() dto: CryptoDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.cryptoService.editCryptoById(user.id, cryptoId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
|
@Delete('/delete/:id')
|
||||||
|
deleteOfferById(@Param('id') roleId: string, @GetUser() user: User) {
|
||||||
|
return this.cryptoService.deleteCryptoById(user.id, roleId);
|
||||||
|
}
|
||||||
|
}
|
9
src/crypto/crypto.module.ts
Normal file
9
src/crypto/crypto.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CryptoService } from './crypto.service';
|
||||||
|
import { CryptoController } from './crypto.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [CryptoService],
|
||||||
|
controllers: [CryptoController],
|
||||||
|
})
|
||||||
|
export class CryptoModule {}
|
192
src/crypto/crypto.service.ts
Normal file
192
src/crypto/crypto.service.ts
Normal file
@ -0,0 +1,192 @@
|
|||||||
|
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { checkUserHasAccount, checkUserIsAdmin } from 'src/utils/checkUser';
|
||||||
|
import { CryptoDto } from './dto';
|
||||||
|
import { BuyCryptoDto } from './dto/buy.crypto.dto';
|
||||||
|
@Injectable()
|
||||||
|
export class CryptoService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getCryptos(userId: string) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
|
||||||
|
return this.prisma.crypto.findMany({
|
||||||
|
orderBy: {
|
||||||
|
name: 'asc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async searchCryptos(userId: string, cryptoName: string) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
|
||||||
|
return this.prisma.crypto.findMany({
|
||||||
|
where: {
|
||||||
|
name: {
|
||||||
|
contains: cryptoName,
|
||||||
|
mode: 'insensitive',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
orderBy: {
|
||||||
|
name: 'asc',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async getCryptoHistory(userId: string, cryptoId: string) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
|
||||||
|
if (cryptoId) {
|
||||||
|
return this.prisma.crypto.findMany({
|
||||||
|
where: {
|
||||||
|
id: cryptoId,
|
||||||
|
},
|
||||||
|
|
||||||
|
orderBy: {
|
||||||
|
created_at: 'desc',
|
||||||
|
},
|
||||||
|
take: 50,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
throw new ForbiddenException('Crypto UUID required');
|
||||||
|
}
|
||||||
|
|
||||||
|
async createCrypto(userId: string, dto: CryptoDto) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const existingCryptosWithSameName = await this.prisma.crypto.findMany({
|
||||||
|
where: {
|
||||||
|
name: dto.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (existingCryptosWithSameName.length > 0) {
|
||||||
|
throw new ForbiddenException('Name already taken');
|
||||||
|
}
|
||||||
|
const crypto = await this.prisma.crypto.create({
|
||||||
|
data: {
|
||||||
|
name: dto.name,
|
||||||
|
image: dto.image,
|
||||||
|
value: dto.value,
|
||||||
|
quantity: dto.quantity,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return crypto;
|
||||||
|
}
|
||||||
|
|
||||||
|
async buyCrypto(userId: string, dto: BuyCryptoDto) {
|
||||||
|
const crypto = await this.prisma.crypto.findFirst({
|
||||||
|
where: {
|
||||||
|
id: dto.id_crypto,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await this.prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (crypto.quantity < dto.amount) {
|
||||||
|
throw new ForbiddenException('No more tokens available');
|
||||||
|
}
|
||||||
|
const necessaryAmount = crypto.value * dto.amount;
|
||||||
|
console.log(necessaryAmount, user.dollarAvailables);
|
||||||
|
|
||||||
|
if (necessaryAmount > user.dollarAvailables) {
|
||||||
|
throw new ForbiddenException('Make money first :) ');
|
||||||
|
}
|
||||||
|
const userAsset = await this.prisma.userHasCrypto.findFirst({
|
||||||
|
where: {
|
||||||
|
id_crypto: dto.id_crypto,
|
||||||
|
id_user: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const newBalance = user.dollarAvailables - necessaryAmount;
|
||||||
|
console.log(newBalance);
|
||||||
|
|
||||||
|
await this.prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
dollarAvailables: newBalance,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (userAsset) {
|
||||||
|
const newBalance = userAsset.amount + dto.amount;
|
||||||
|
await this.prisma.userHasCrypto.update({
|
||||||
|
where: {
|
||||||
|
id: userAsset.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
amount: newBalance,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
await this.prisma.userHasCrypto.create({
|
||||||
|
data: {
|
||||||
|
id_crypto: dto.id_crypto,
|
||||||
|
id_user: userId,
|
||||||
|
amount: dto.amount,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
const newCryptoValue = crypto.value * 1.1;
|
||||||
|
|
||||||
|
await this.prisma.cryptoHistory.create({
|
||||||
|
data: {
|
||||||
|
id_crypto: crypto.id,
|
||||||
|
value: newCryptoValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const newQuantity = (crypto.quantity -= dto.amount);
|
||||||
|
return this.prisma.crypto.update({
|
||||||
|
where: {
|
||||||
|
id: dto.id_crypto,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
value: newCryptoValue,
|
||||||
|
quantity: newQuantity,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async editCryptoById(userId: string, cryptoId: string, dto: CryptoDto) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const crypto = await this.prisma.crypto.findUnique({
|
||||||
|
where: {
|
||||||
|
id: cryptoId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!crypto || crypto.id !== cryptoId)
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
|
||||||
|
return this.prisma.crypto.update({
|
||||||
|
where: {
|
||||||
|
id: crypto.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
...dto,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteCryptoById(userId: string, id: string) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const crypto = await this.prisma.crypto.findUnique({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!crypto || crypto.id !== id)
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
|
||||||
|
await this.prisma.crypto.delete({
|
||||||
|
where: {
|
||||||
|
id: crypto.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
32
src/crypto/dto/buy.crypto.dto.ts
Normal file
32
src/crypto/dto/buy.crypto.dto.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsNumber,
|
||||||
|
IsString,
|
||||||
|
IsUUID,
|
||||||
|
Max,
|
||||||
|
MaxLength,
|
||||||
|
Min,
|
||||||
|
MinLength,
|
||||||
|
} from 'class-validator';
|
||||||
|
export class BuyCryptoDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Cryptocurrency UUID',
|
||||||
|
example: '12121-DSZD-E221212-2121221',
|
||||||
|
})
|
||||||
|
@MinLength(1)
|
||||||
|
@MaxLength(50)
|
||||||
|
@IsString()
|
||||||
|
@IsUUID()
|
||||||
|
id_crypto: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: Number,
|
||||||
|
description: 'Amount of token traded',
|
||||||
|
example: 2,
|
||||||
|
})
|
||||||
|
@Min(1)
|
||||||
|
@Max(1000)
|
||||||
|
@IsNumber()
|
||||||
|
amount: number;
|
||||||
|
}
|
54
src/crypto/dto/crypto.dto.ts
Normal file
54
src/crypto/dto/crypto.dto.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsNumber,
|
||||||
|
IsPositive,
|
||||||
|
IsString,
|
||||||
|
IsUrl,
|
||||||
|
Max,
|
||||||
|
MaxLength,
|
||||||
|
Min,
|
||||||
|
MinLength,
|
||||||
|
} from 'class-validator';
|
||||||
|
export class CryptoDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Cryptocurrency name',
|
||||||
|
example: 'BTC',
|
||||||
|
})
|
||||||
|
@MaxLength(50)
|
||||||
|
@MinLength(1)
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: Number,
|
||||||
|
description: 'Value for the cryptocurrency in $',
|
||||||
|
example: 1,
|
||||||
|
})
|
||||||
|
@Min(1)
|
||||||
|
@Max(10000)
|
||||||
|
@IsPositive()
|
||||||
|
@IsNumber()
|
||||||
|
value: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: Number,
|
||||||
|
description: 'Quantity of tokens available on the platform',
|
||||||
|
example: 100,
|
||||||
|
})
|
||||||
|
@Min(1)
|
||||||
|
@Max(10000)
|
||||||
|
@IsPositive()
|
||||||
|
@IsNumber()
|
||||||
|
quantity: number;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Image for the cryptocurrency in ',
|
||||||
|
example: 'https://myImage/com',
|
||||||
|
})
|
||||||
|
@MaxLength(255)
|
||||||
|
@IsUrl()
|
||||||
|
@IsString()
|
||||||
|
image: string;
|
||||||
|
}
|
1
src/crypto/dto/index.ts
Normal file
1
src/crypto/dto/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './crypto.dto';
|
1
src/offer/dto/index.ts
Normal file
1
src/offer/dto/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './offer.dto';
|
33
src/offer/dto/offer.dto.ts
Normal file
33
src/offer/dto/offer.dto.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsNumber,
|
||||||
|
IsPositive,
|
||||||
|
IsString,
|
||||||
|
IsUUID,
|
||||||
|
Max,
|
||||||
|
MaxLength,
|
||||||
|
Min,
|
||||||
|
} from 'class-validator';
|
||||||
|
export class OfferDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Cryptocurrency UUID',
|
||||||
|
example: '12121-DSZD-E221212-6227933',
|
||||||
|
})
|
||||||
|
@IsString()
|
||||||
|
@IsUUID()
|
||||||
|
id_crypto: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: 'number',
|
||||||
|
|
||||||
|
description: 'Amount traded ',
|
||||||
|
example: 21,
|
||||||
|
})
|
||||||
|
@Min(1)
|
||||||
|
@Max(1000)
|
||||||
|
@IsNumber()
|
||||||
|
@IsPositive()
|
||||||
|
amount: number;
|
||||||
|
}
|
||||||
|
|
58
src/offer/offer.controller.ts
Normal file
58
src/offer/offer.controller.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
// UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { GetUser } from '../auth/decorator';
|
||||||
|
// import { JwtGuard } from '../auth/guard';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
import { JwtGuard } from 'src/auth/guard';
|
||||||
|
import { OfferService } from './offer.service';
|
||||||
|
import { OfferDto } from './dto';
|
||||||
|
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
@ApiTags('offer')
|
||||||
|
@Controller('offer')
|
||||||
|
export class OfferController {
|
||||||
|
constructor(private offerService: OfferService) {}
|
||||||
|
|
||||||
|
@Get('/all')
|
||||||
|
getAllRoles(@GetUser() user: User) {
|
||||||
|
return this.offerService.getOffers(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
@Post('/create')
|
||||||
|
createRole(
|
||||||
|
@Body()
|
||||||
|
dto: OfferDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.offerService.createOffer(user.id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Patch('/update/:id')
|
||||||
|
editOfferById(
|
||||||
|
@Param('id') offerId: string,
|
||||||
|
@Body() dto: OfferDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.offerService.editOfferById(user.id, offerId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
|
@Delete('/delete/:id')
|
||||||
|
deleteOfferById(@Param('id') roleId: string, @GetUser() user: User) {
|
||||||
|
return this.offerService.deleteOfferById(user.id, roleId);
|
||||||
|
}
|
||||||
|
}
|
9
src/offer/offer.module.ts
Normal file
9
src/offer/offer.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { OfferService } from './offer.service';
|
||||||
|
import { OfferController } from './offer.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [OfferService],
|
||||||
|
controllers: [OfferController],
|
||||||
|
})
|
||||||
|
export class OfferModule {}
|
35
src/offer/offer.service.sql
Normal file
35
src/offer/offer.service.sql
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
SELECT
|
||||||
|
o.amount,
|
||||||
|
o.created_at,
|
||||||
|
o.id_user,
|
||||||
|
c.id AS crypto_id,
|
||||||
|
c.name AS crypto_name,
|
||||||
|
c.value AS crypto_value,
|
||||||
|
c.image AS crypto_image,
|
||||||
|
c.quantity AS crypto_quantity
|
||||||
|
FROM
|
||||||
|
"Offer" o
|
||||||
|
JOIN
|
||||||
|
"Crypto" c ON o.id_crypto = c.id
|
||||||
|
ORDER BY
|
||||||
|
o.created_at DESC;
|
||||||
|
|
||||||
|
INSERT INTO "Offer" (id, id_crypto, id_user, amount, created_at, updated_at)
|
||||||
|
VALUES (gen_random_uuid(), 'dto.id_crypto', 'userId', 'dto.amount', NOW(), NOW());
|
||||||
|
|
||||||
|
SELECT * FROM "Offer" WHERE id = 'offerId';
|
||||||
|
|
||||||
|
SELECT * FROM "Crypto" WHERE id = 'dto.id_crypto';
|
||||||
|
|
||||||
|
UPDATE "Offer"
|
||||||
|
SET
|
||||||
|
id_crypto = 'dto.id_crypto',
|
||||||
|
amount = 'dto.amount',
|
||||||
|
updated_at = NOW()
|
||||||
|
WHERE
|
||||||
|
id = 'offerId';
|
||||||
|
|
||||||
|
SELECT * FROM "Offer" WHERE id = 'id';
|
||||||
|
|
||||||
|
DELETE FROM "Offer" WHERE id = 'id';
|
||||||
|
|
88
src/offer/offer.service.ts
Normal file
88
src/offer/offer.service.ts
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import {ForbiddenException, Injectable} from "@nestjs/common";
|
||||||
|
import {PrismaService} from "@/prisma/prisma.service";
|
||||||
|
import {checkUserHasAccount, checkUserIsAdmin} from "src/utils/checkUser";
|
||||||
|
import {OfferDto} from "./dto";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class OfferService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getOffers(userId: string) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
return this.prisma.offer.findMany({
|
||||||
|
orderBy: {
|
||||||
|
created_at: 'desc',
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
amount: true,
|
||||||
|
created_at: true,
|
||||||
|
id_user: true,
|
||||||
|
Crypto: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async createOffer(userId: string, dto: OfferDto) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
return this.prisma.offer.create({
|
||||||
|
data: {
|
||||||
|
id_crypto: dto.id_crypto,
|
||||||
|
id_user: userId,
|
||||||
|
amount: dto.amount,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async editOfferById(userId: string, offerId: string, dto: OfferDto) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
|
||||||
|
const offer = await this.prisma.offer.findUnique({
|
||||||
|
where: {
|
||||||
|
id: offerId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const crypto = await this.prisma.crypto.findUnique({
|
||||||
|
where: {
|
||||||
|
id: dto.id_crypto,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!crypto || !crypto.id) {
|
||||||
|
throw new ForbiddenException('Crypto doesnt exist');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!offer || offer.id !== offerId)
|
||||||
|
throw new ForbiddenException('Offer id mandatory');
|
||||||
|
|
||||||
|
return this.prisma.offer.update({
|
||||||
|
where: {
|
||||||
|
id: offerId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
...dto,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async deleteOfferById(userId: string, id: string) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const offer = await this.prisma.offer.findUnique({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!offer || offer.id !== id)
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
|
||||||
|
await this.prisma.offer.delete({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
9
src/prisma/prisma.module.ts
Normal file
9
src/prisma/prisma.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Global, Module } from '@nestjs/common';
|
||||||
|
import { PrismaService } from './prisma.service';
|
||||||
|
|
||||||
|
@Global()
|
||||||
|
@Module({
|
||||||
|
providers: [PrismaService],
|
||||||
|
exports: [PrismaService],
|
||||||
|
})
|
||||||
|
export class PrismaModule {}
|
18
src/prisma/prisma.service.ts
Normal file
18
src/prisma/prisma.service.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
// biome-ignore lint/style/useImportType:
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class PrismaService extends PrismaClient {
|
||||||
|
constructor(config: ConfigService) {
|
||||||
|
super({
|
||||||
|
datasources: {
|
||||||
|
db: {
|
||||||
|
url: config.get('DATABASE_URL'),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
1
src/promoCode/dto/index.ts
Normal file
1
src/promoCode/dto/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './promoCode.dto';
|
32
src/promoCode/dto/promoCode.dto.ts
Normal file
32
src/promoCode/dto/promoCode.dto.ts
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import {
|
||||||
|
IsNumber,
|
||||||
|
IsPositive,
|
||||||
|
IsString,
|
||||||
|
Max,
|
||||||
|
MaxLength,
|
||||||
|
Min,
|
||||||
|
MinLength,
|
||||||
|
} from 'class-validator';
|
||||||
|
export class PromoCodeDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Name of the PromoCOde',
|
||||||
|
example: 'FILOU10',
|
||||||
|
})
|
||||||
|
@MinLength(1)
|
||||||
|
@MaxLength(50)
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@ApiProperty({
|
||||||
|
type: Number,
|
||||||
|
description: 'Dollars given for account creation when promoCode applied',
|
||||||
|
example: 100,
|
||||||
|
})
|
||||||
|
@IsPositive()
|
||||||
|
@Min(1)
|
||||||
|
@Max(3000)
|
||||||
|
@IsNumber()
|
||||||
|
value: number;
|
||||||
|
}
|
57
src/promoCode/promoCode.controller.ts
Normal file
57
src/promoCode/promoCode.controller.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { GetUser } from '../auth/decorator';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
import { PromoCodeDto } from './dto';
|
||||||
|
import { PromoCodeService } from './promoCode.service';
|
||||||
|
import { JwtGuard } from 'src/auth/guard';
|
||||||
|
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
@ApiTags('promoCode')
|
||||||
|
@Controller('promoCode')
|
||||||
|
export class PromoCodeController {
|
||||||
|
constructor(private promoService: PromoCodeService) {}
|
||||||
|
|
||||||
|
@Get('/all')
|
||||||
|
getAllPromoCodes(@GetUser() user: User) {
|
||||||
|
return this.promoService.getPromoCodes(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
@Post('/create')
|
||||||
|
createPromoCode(
|
||||||
|
// @GetUser() user: User,
|
||||||
|
@Body()
|
||||||
|
dto: PromoCodeDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.promoService.createPromoCode(user.id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Patch('/update/:id')
|
||||||
|
editPromoCodeById(
|
||||||
|
@Param('id') promoCodeId: string,
|
||||||
|
@Body() dto: PromoCodeDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.promoService.editPromoCodeById(user.id, promoCodeId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
|
@Delete('/delete/:id')
|
||||||
|
deletePromoCodeById(@Param('id') promoCodeId: string, @GetUser() user: User) {
|
||||||
|
return this.promoService.deletePromoCodeById(user.id, promoCodeId);
|
||||||
|
}
|
||||||
|
}
|
9
src/promoCode/promoCode.module.ts
Normal file
9
src/promoCode/promoCode.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { PromoCodeController } from './promoCode.controller';
|
||||||
|
import { PromoCodeService } from './promoCode.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [PromoCodeService],
|
||||||
|
controllers: [PromoCodeController],
|
||||||
|
})
|
||||||
|
export class PromoCodeModule {}
|
71
src/promoCode/promoCode.service.spec.ts
Normal file
71
src/promoCode/promoCode.service.spec.ts
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
import { ForbiddenException } from '@nestjs/common';
|
||||||
|
// biome-ignore lint/style/useImportType:
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { PrismaService } from "@/prisma/prisma.service";
|
||||||
|
import { PromoCodeService } from './promoCode.service';
|
||||||
|
import { checkUserIsAdmin } from "@/utils/checkUser";
|
||||||
|
|
||||||
|
jest.mock('../utils/checkUser');
|
||||||
|
|
||||||
|
describe('PromoCodeService', () => {
|
||||||
|
let service: PromoCodeService;
|
||||||
|
let prisma: PrismaService;
|
||||||
|
|
||||||
|
const mockPrismaService = {
|
||||||
|
promoCode: {
|
||||||
|
findMany: jest.fn(),
|
||||||
|
create: jest.fn(),
|
||||||
|
findUnique: jest.fn(),
|
||||||
|
update: jest.fn(),
|
||||||
|
delete: jest.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
PromoCodeService,
|
||||||
|
{ provide: PrismaService, useValue: mockPrismaService },
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
service = module.get<PromoCodeService>(PromoCodeService);
|
||||||
|
prisma = module.get<PrismaService>(PrismaService);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getPromoCodes', () => {
|
||||||
|
it('should get promo codes if user is admin', async () => {
|
||||||
|
(checkUserIsAdmin as jest.Mock).mockResolvedValue(true);
|
||||||
|
const mockPromoCodes = [
|
||||||
|
{ id: '1', name: 'PROMO10', value: 10 },
|
||||||
|
{ id: '2', name: 'PROMO20', value: 20 },
|
||||||
|
];
|
||||||
|
mockPrismaService.promoCode.findMany.mockResolvedValue(mockPromoCodes);
|
||||||
|
|
||||||
|
const result = await service.getPromoCodes('user-id');
|
||||||
|
|
||||||
|
expect(checkUserIsAdmin).toHaveBeenCalledWith('user-id');
|
||||||
|
expect(prisma.promoCode.findMany).toHaveBeenCalledWith({
|
||||||
|
orderBy: { name: 'asc' },
|
||||||
|
select: { id: true, name: true, value: true },
|
||||||
|
});
|
||||||
|
expect(result).toEqual(mockPromoCodes);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw ForbiddenException if user is not admin', async () => {
|
||||||
|
(checkUserIsAdmin as jest.Mock).mockRejectedValue(new ForbiddenException('Not an admin'));
|
||||||
|
|
||||||
|
await expect(service.getPromoCodes('user-id')).rejects.toThrow(
|
||||||
|
ForbiddenException,
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(checkUserIsAdmin).toHaveBeenCalledWith('user-id');
|
||||||
|
expect(prisma.promoCode.findMany).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
79
src/promoCode/promoCode.service.ts
Normal file
79
src/promoCode/promoCode.service.ts
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { PromoCodeDto } from './dto';
|
||||||
|
import { checkUserIsAdmin } from '../utils/checkUser';
|
||||||
|
@Injectable()
|
||||||
|
export class PromoCodeService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getPromoCodes(userId: string) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
return this.prisma.promoCode.findMany({
|
||||||
|
orderBy: {
|
||||||
|
name: 'asc',
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
value: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPromoCode(userId: string, dto: PromoCodeDto) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const promoCode = await this.prisma.promoCode.create({
|
||||||
|
data: {
|
||||||
|
name: dto.name,
|
||||||
|
value: dto.value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return promoCode;
|
||||||
|
}
|
||||||
|
async editPromoCodeById(
|
||||||
|
userId: string,
|
||||||
|
promoCodeId: string,
|
||||||
|
dto: PromoCodeDto,
|
||||||
|
) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const promoCode = await this.prisma.promoCode.findUnique({
|
||||||
|
where: {
|
||||||
|
id: promoCodeId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!promoCode || promoCode.id !== promoCodeId)
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
|
||||||
|
return this.prisma.promoCode.update({
|
||||||
|
where: {
|
||||||
|
id: promoCode.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
...dto,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async deletePromoCodeById(userId: string, id: string) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const promoCode = await this.prisma.promoCode.findUnique({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!promoCode || promoCode.id !== id)
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
|
||||||
|
await this.prisma.promoCode.delete({
|
||||||
|
where: {
|
||||||
|
id: promoCode.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
1
src/role/dto/index.ts
Normal file
1
src/role/dto/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './role.dto';
|
13
src/role/dto/role.dto.ts
Normal file
13
src/role/dto/role.dto.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsString, MaxLength, MinLength } from 'class-validator';
|
||||||
|
export class RoleDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Role Name',
|
||||||
|
example: 'user',
|
||||||
|
})
|
||||||
|
@MinLength(1)
|
||||||
|
@MaxLength(50)
|
||||||
|
@IsString()
|
||||||
|
name: string;
|
||||||
|
}
|
63
src/role/role.controller.ts
Normal file
63
src/role/role.controller.ts
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Param,
|
||||||
|
Patch,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
// UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { GetUser } from '../auth/decorator';
|
||||||
|
// import { JwtGuard } from '../auth/guard';
|
||||||
|
import { RoleDto } from './dto';
|
||||||
|
import { RoleService } from './role.service';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
import { JwtGuard } from 'src/auth/guard';
|
||||||
|
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
@ApiTags('role')
|
||||||
|
@Controller('role')
|
||||||
|
export class RoleController {
|
||||||
|
constructor(private roleService: RoleService) {}
|
||||||
|
|
||||||
|
@Get('/all')
|
||||||
|
getAllRoles(@GetUser() user: User) {
|
||||||
|
return this.roleService.getRolesAdmin(user.id);
|
||||||
|
}
|
||||||
|
// @Get('/cm/all')
|
||||||
|
// getRolesCm(@GetUser() user: User) {
|
||||||
|
// return this.roleService.getRolesCm(user)
|
||||||
|
// }
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
@Post('/create')
|
||||||
|
createRole(
|
||||||
|
// @GetUser() user: User,
|
||||||
|
@Body()
|
||||||
|
dto: RoleDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.roleService.createRole(user.id, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.OK)
|
||||||
|
@Patch('/update/:id')
|
||||||
|
editRoleById(
|
||||||
|
@Param('id') roleId: string,
|
||||||
|
@Body() dto: RoleDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.roleService.editRoleById(user.id, roleId, dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.NO_CONTENT)
|
||||||
|
@Delete('/delete/:id')
|
||||||
|
deleteRoleById(@Param('id') roleId: string, @GetUser() user: User) {
|
||||||
|
return this.roleService.deleteRoleById(user.id, roleId);
|
||||||
|
}
|
||||||
|
}
|
8
src/role/role.module.ts
Normal file
8
src/role/role.module.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { RoleController } from './role.controller';
|
||||||
|
import { RoleService } from './role.service';
|
||||||
|
@Module({
|
||||||
|
providers: [RoleService],
|
||||||
|
controllers: [RoleController],
|
||||||
|
})
|
||||||
|
export class RoleModule {}
|
72
src/role/role.service.ts
Normal file
72
src/role/role.service.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { RoleDto } from './dto';
|
||||||
|
import { checkUserIsAdmin } from 'src/utils/checkUser';
|
||||||
|
// import { checkRoleLevel, checkUserIsStaff } from 'src/utils/checkUser';
|
||||||
|
@Injectable()
|
||||||
|
export class RoleService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getRolesAdmin(userId: string) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
return this.prisma.role.findMany({
|
||||||
|
orderBy: {
|
||||||
|
name: 'asc',
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
name: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async createRole(userId: string, dto: RoleDto) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
const role = await this.prisma.role.create({
|
||||||
|
data: {
|
||||||
|
name: dto.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return role;
|
||||||
|
}
|
||||||
|
async editRoleById(userId: string, roleId: string, dto: RoleDto) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const role = await this.prisma.role.findUnique({
|
||||||
|
where: {
|
||||||
|
id: roleId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!role || role.id !== roleId)
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
|
||||||
|
return this.prisma.role.update({
|
||||||
|
where: {
|
||||||
|
id: roleId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
...dto,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async deleteRoleById(userId: string, id: string) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
const role = await this.prisma.role.findUnique({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!role || role.id !== id)
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
|
||||||
|
await this.prisma.role.delete({
|
||||||
|
where: {
|
||||||
|
id: id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
1
src/trade/dto/index.ts
Normal file
1
src/trade/dto/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export * from './trade.dto';
|
13
src/trade/dto/trade.dto.ts
Normal file
13
src/trade/dto/trade.dto.ts
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
import { IsNotEmpty, IsString, IsUUID } from 'class-validator';
|
||||||
|
export class TradeDto {
|
||||||
|
@ApiProperty({
|
||||||
|
type: String,
|
||||||
|
description: 'Offer UUID ',
|
||||||
|
example: '121212-DSDZ1-21212DJDZ-31313',
|
||||||
|
})
|
||||||
|
@IsUUID()
|
||||||
|
@IsNotEmpty()
|
||||||
|
@IsString()
|
||||||
|
id_offer: string;
|
||||||
|
}
|
38
src/trade/trade.controller.ts
Normal file
38
src/trade/trade.controller.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Get,
|
||||||
|
HttpCode,
|
||||||
|
HttpStatus,
|
||||||
|
Post,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { GetUser } from '../auth/decorator';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
import { TradeService } from './trade.service';
|
||||||
|
import { TradeDto } from './dto';
|
||||||
|
import { JwtGuard } from 'src/auth/guard';
|
||||||
|
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
@ApiTags('trade')
|
||||||
|
@Controller('trade')
|
||||||
|
export class TradeController {
|
||||||
|
constructor(private tradeService: TradeService) {}
|
||||||
|
|
||||||
|
@Get('/all')
|
||||||
|
getAllPromoCodes(@GetUser() user: User) {
|
||||||
|
return this.tradeService.getTrades(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@HttpCode(HttpStatus.CREATED)
|
||||||
|
@Post('/create')
|
||||||
|
createPromoCode(
|
||||||
|
// @GetUser() user: User,
|
||||||
|
@Body()
|
||||||
|
dto: TradeDto,
|
||||||
|
@GetUser() user: User,
|
||||||
|
) {
|
||||||
|
return this.tradeService.createTrade(user.id, dto);
|
||||||
|
}
|
||||||
|
}
|
9
src/trade/trade.module.ts
Normal file
9
src/trade/trade.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TradeService } from './trade.service';
|
||||||
|
import { TradeController } from './trade.controller';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [TradeService],
|
||||||
|
controllers: [TradeController],
|
||||||
|
})
|
||||||
|
export class TradeModule {}
|
173
src/trade/trade.service.ts
Normal file
173
src/trade/trade.service.ts
Normal file
@ -0,0 +1,173 @@
|
|||||||
|
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { checkUserHasAccount, checkUserIsAdmin } from 'src/utils/checkUser';
|
||||||
|
import { TradeDto } from './dto';
|
||||||
|
@Injectable()
|
||||||
|
export class TradeService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getTrades(userId: string) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
return this.prisma.trade.findMany({
|
||||||
|
orderBy: {
|
||||||
|
created_at: 'desc',
|
||||||
|
},
|
||||||
|
// include: {
|
||||||
|
// Giver: true,
|
||||||
|
// Receiver: true,
|
||||||
|
// Crypto: true,
|
||||||
|
// },
|
||||||
|
|
||||||
|
select: {
|
||||||
|
Giver: {
|
||||||
|
select: {
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
pseudo: true,
|
||||||
|
dollarAvailables: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Receiver: {
|
||||||
|
select: {
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
pseudo: true,
|
||||||
|
dollarAvailables: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Crypto: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
async getLastTrades() {
|
||||||
|
return this.prisma.trade.findMany({
|
||||||
|
orderBy: {
|
||||||
|
created_at: 'desc',
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
amount_traded: true,
|
||||||
|
Crypto: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async createTrade(userId: string, dto: TradeDto) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
|
||||||
|
const offer = await this.prisma.offer.findUnique({
|
||||||
|
where: {
|
||||||
|
id: dto.id_offer,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const crypto = await this.prisma.crypto.findFirst({
|
||||||
|
where: {
|
||||||
|
id: offer.id_crypto,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const buyer = await this.prisma.user.findFirst({
|
||||||
|
where: {
|
||||||
|
id: offer.id_user,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const price = crypto.value * offer.amount;
|
||||||
|
if (buyer.dollarAvailables < price) {
|
||||||
|
throw new ForbiddenException(
|
||||||
|
`Acqueror ${buyer.pseudo} doesnt have enough money to make this trade`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const asset = await this.prisma.userHasCrypto.findFirst({
|
||||||
|
where: {
|
||||||
|
id_crypto: offer.id_crypto,
|
||||||
|
id_user: offer.id_user,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!asset || asset.amount < offer.amount) {
|
||||||
|
throw new ForbiddenException(`Seller doesnt have enough ${crypto.name} `);
|
||||||
|
}
|
||||||
|
|
||||||
|
const trade = await this.prisma.trade.create({
|
||||||
|
data: {
|
||||||
|
id_giver: offer.id_user,
|
||||||
|
id_receiver: userId,
|
||||||
|
id_crypto: offer.id_crypto,
|
||||||
|
amount_traded: offer.amount,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const newBalanceGiver = (asset.amount -= offer.amount);
|
||||||
|
await this.prisma.userHasCrypto.update({
|
||||||
|
where: {
|
||||||
|
id: asset.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
amount: newBalanceGiver,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const receiverAssets = await this.prisma.userHasCrypto.findFirst({
|
||||||
|
where: {
|
||||||
|
id_user: userId,
|
||||||
|
id_crypto: offer.id_crypto,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!receiverAssets) {
|
||||||
|
await this.prisma.userHasCrypto.create({
|
||||||
|
data: {
|
||||||
|
id_user: userId,
|
||||||
|
id_crypto: offer.id_crypto,
|
||||||
|
amount: offer.amount,
|
||||||
|
createdAt: new Date(),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const newBalanceReceiver = receiverAssets.amount + offer.amount;
|
||||||
|
await this.prisma.userHasCrypto.update({
|
||||||
|
where: {
|
||||||
|
id: receiverAssets.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
amount: newBalanceReceiver,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const newValue = crypto.value * 1.1;
|
||||||
|
await this.prisma.cryptoHistory.create({
|
||||||
|
data: {
|
||||||
|
id_crypto: crypto.id,
|
||||||
|
value: newValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.prisma.crypto.update({
|
||||||
|
where: {
|
||||||
|
id: crypto.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
value: newValue,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const prevAmount = buyer.dollarAvailables;
|
||||||
|
|
||||||
|
await this.prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
dollarAvailables: prevAmount - price,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await this.prisma.offer.delete({
|
||||||
|
where: {
|
||||||
|
id: offer.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return trade;
|
||||||
|
}
|
||||||
|
}
|
36
src/user/user.controller.ts
Normal file
36
src/user/user.controller.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { Controller, Get, UseGuards } from '@nestjs/common';
|
||||||
|
import { GetUser } from '../auth/decorator';
|
||||||
|
import { JwtGuard } from '../auth/guard';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
|
||||||
|
@ApiTags('user')
|
||||||
|
@UseGuards(JwtGuard)
|
||||||
|
@Controller('user')
|
||||||
|
export class UserController {
|
||||||
|
constructor(private userService: UserService) {}
|
||||||
|
|
||||||
|
@Get('/my-assets')
|
||||||
|
GetMyAssets(
|
||||||
|
@GetUser()
|
||||||
|
user: User,
|
||||||
|
) {
|
||||||
|
return this.userService.getMyAssets(user.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/users-assets')
|
||||||
|
GetAlLAssets(
|
||||||
|
@GetUser()
|
||||||
|
user: User,
|
||||||
|
) {
|
||||||
|
return this.userService.getUsersAssets(user.id);
|
||||||
|
}
|
||||||
|
@Get('/my-trades')
|
||||||
|
GetMyTrades(
|
||||||
|
@GetUser()
|
||||||
|
user: User,
|
||||||
|
) {
|
||||||
|
return this.userService.getMyTrades(user.id);
|
||||||
|
}
|
||||||
|
}
|
8
src/user/user.module.ts
Normal file
8
src/user/user.module.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { UserService } from './user.service';
|
||||||
|
import { UserController } from './user.controller';
|
||||||
|
@Module({
|
||||||
|
providers: [UserService],
|
||||||
|
controllers: [UserController],
|
||||||
|
})
|
||||||
|
export class UserModule {}
|
81
src/user/user.service.ts
Normal file
81
src/user/user.service.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import {Injectable} from "@nestjs/common";
|
||||||
|
import {PrismaService} from "@/prisma/prisma.service";
|
||||||
|
import {checkUserHasAccount, checkUserIsAdmin} from "src/utils/checkUser";
|
||||||
|
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserService {
|
||||||
|
constructor(private prisma: PrismaService) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the assets of a given user, including their first name, last name, available dollars,
|
||||||
|
* pseudo, and associated cryptocurrencies.
|
||||||
|
*
|
||||||
|
* @param {string} userId - The unique identifier of the user whose assets are being retrieved.
|
||||||
|
* @return A promise that resolves to an object containing the user's assets and associated data.
|
||||||
|
*/
|
||||||
|
async getMyAssets(userId: string) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
|
||||||
|
return this.prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
dollarAvailables: true,
|
||||||
|
pseudo: true,
|
||||||
|
UserHasCrypto: {
|
||||||
|
select: {
|
||||||
|
Crypto: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the assets of users based on user ID.
|
||||||
|
*
|
||||||
|
* @param {string} userId - The ID of the user requesting the assets.
|
||||||
|
* @return A promise that resolves to an array of user assets.
|
||||||
|
*/
|
||||||
|
async getUsersAssets(userId: string) {
|
||||||
|
await checkUserIsAdmin(userId);
|
||||||
|
|
||||||
|
return this.prisma.user.findMany({
|
||||||
|
select: {
|
||||||
|
firstName: true,
|
||||||
|
lastName: true,
|
||||||
|
pseudo: true,
|
||||||
|
dollarAvailables: true,
|
||||||
|
UserHasCrypto: {
|
||||||
|
select: {
|
||||||
|
Crypto: true,
|
||||||
|
amount: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
take: 20,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches all trades associated with a given user.
|
||||||
|
*
|
||||||
|
* @param {string} userId - The unique identifier of the user.
|
||||||
|
* @return A promise that resolves to an array of trade objects.
|
||||||
|
*/
|
||||||
|
async getMyTrades(userId: string) {
|
||||||
|
await checkUserHasAccount(userId);
|
||||||
|
return this.prisma.trade.findMany({
|
||||||
|
where: {
|
||||||
|
OR: [{id_giver: userId}, {id_receiver: userId}],
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Crypto: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
86
src/utils/checkUser.ts
Normal file
86
src/utils/checkUser.ts
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import { ForbiddenException } from '@nestjs/common';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import { Roles } from './const/const';
|
||||||
|
|
||||||
|
const prisma = new PrismaClient();
|
||||||
|
|
||||||
|
export async function checkRoleLevel(userId: string, level: string) {
|
||||||
|
if (!userId || !level) {
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
}
|
||||||
|
|
||||||
|
checkRoleExist(level);
|
||||||
|
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: userId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (user?.roleId) {
|
||||||
|
const role = await prisma.role.findFirst({
|
||||||
|
where: {
|
||||||
|
id: user.roleId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (role?.id) {
|
||||||
|
checkRoleExist(role.name);
|
||||||
|
if (level === Roles.ADMIN && role.name !== Roles.ADMIN) {
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function checkRoleExist(role: string) {
|
||||||
|
switch (role) {
|
||||||
|
case Roles.ADMIN:
|
||||||
|
case Roles.USER:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkUserHasAccount(jwtId: string) {
|
||||||
|
if (jwtId) {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: jwtId,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!user || !user.id) {
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ForbiddenException('Access to resources denied');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function checkUserIsAdmin(jwtId: string) {
|
||||||
|
if (jwtId) {
|
||||||
|
const user = await prisma.user.findUnique({
|
||||||
|
where: {
|
||||||
|
id: jwtId,
|
||||||
|
isActive: true,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Role: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!user || !user.id) {
|
||||||
|
throw new ForbiddenException('Access to resources denied2');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (user.Role.name !== Roles.ADMIN) {
|
||||||
|
throw new ForbiddenException('Access to resources denied3');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw new ForbiddenException('Access to resources denied4');
|
||||||
|
}
|
||||||
|
}
|
4
src/utils/const/const.ts
Normal file
4
src/utils/const/const.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export const Roles = {
|
||||||
|
ADMIN: 'admin',
|
||||||
|
USER: 'user',
|
||||||
|
};
|
0
src/utils/styles.ts
Normal file
0
src/utils/styles.ts
Normal file
16
src/utils/tests/user-mock.ts
Normal file
16
src/utils/tests/user-mock.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// auth/test/mocks.ts
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
|
||||||
|
export const getMockUser = (): User => ({
|
||||||
|
id: 'user-id',
|
||||||
|
email: 'test@example.com',
|
||||||
|
firstName: 'Test User',
|
||||||
|
lastName: 'Test',
|
||||||
|
hash: 'test-hash',
|
||||||
|
pseudo: 'test-pseudo',
|
||||||
|
isActive: true,
|
||||||
|
created_at: new Date(),
|
||||||
|
updated_at: new Date(),
|
||||||
|
roleId: 'user',
|
||||||
|
dollarAvailables: 1000,
|
||||||
|
});
|
0
src/utils/types.ts
Normal file
0
src/utils/types.ts
Normal file
Loading…
x
Reference in New Issue
Block a user