diff --git a/backend/libs/database/proxy_user.entity.ts b/backend/libs/database/proxy_user.entity.ts index d89415a..95f50ab 100644 --- a/backend/libs/database/proxy_user.entity.ts +++ b/backend/libs/database/proxy_user.entity.ts @@ -1,4 +1,5 @@ -import { Column, Entity, JoinColumn, OneToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { Column, Entity, JoinColumn, OneToMany, OneToOne, PrimaryGeneratedColumn } from 'typeorm'; +import { Payment } from './payment.entity'; import { User } from './user.entity'; @Entity() @@ -10,11 +11,11 @@ export class ProxyUser { @PrimaryGeneratedColumn('uuid') public uuid!: string; - @Column({ nullable: true }) - public userName: string; + @Column({ nullable: false, unique: true }) + public userName!: string; @Column({ nullable: true }) - public description: string; + public description?: string; @Column({ nullable: false }) public link!: string; @@ -27,5 +28,8 @@ export class ProxyUser { @OneToOne(() => User, (user) => user.id, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true }) @JoinColumn({ name: 'user_id' }) - public user: User; + public user?: User; + + @OneToMany(() => Payment, (payment) => payment.user) + public payments: Payment[]; } diff --git a/backend/src/app.module.ts b/backend/src/app.module.ts index 65d3643..1adba1e 100644 --- a/backend/src/app.module.ts +++ b/backend/src/app.module.ts @@ -6,10 +6,19 @@ import { AdminModule } from './modules/admin/admin.module'; import { ImageModule } from './modules/image/image.module'; import { AppInitService } from './modules/initialization/app.init.service'; import { PostModule } from './modules/post/post.module'; +import { ProxyModule } from './modules/proxy/proxy.module'; import { UserModule } from './modules/user/user.module'; @Module({ - imports: [LibsModule, PostModule, AdminModule, UserModule, ImageModule, TypeOrmModule.forRoot(config.database)], + imports: [ + LibsModule, + PostModule, + AdminModule, + UserModule, + ImageModule, + ProxyModule, + TypeOrmModule.forRoot(config.database), + ], controllers: [], providers: [AppInitService], }) diff --git a/backend/src/modules/proxy/proxy.controller.ts b/backend/src/modules/proxy/proxy.controller.ts new file mode 100644 index 0000000..87e565e --- /dev/null +++ b/backend/src/modules/proxy/proxy.controller.ts @@ -0,0 +1,46 @@ +import { Body, Controller, Get, Param, Post } from '@nestjs/common'; +import { ApiOperation, ApiTags } from '@nestjs/swagger'; +import { IOperation, IProxyUser } from './proxy.dto'; +import { ProxyService } from './proxy.service'; + +@ApiTags('Proxy') +@Controller('proxy') +export class ProxyController { + constructor(private proxyService: ProxyService) {} + + @ApiOperation({ description: 'Method to create a new proxy user' }) + @Post('newUser') + async newUser(@Body() data: IProxyUser) { + return await this.proxyService.newUser(data); + } + + @ApiOperation({ description: 'get user by its username' }) + @Get('getUser/:userName') + async getUser(@Param('userName') userName: string) { + return await this.proxyService.getUser(userName); + } + + @ApiOperation({ description: 'get all users of proxy' }) + @Get('getAllUsers') + async getAllUsers() { + return await this.proxyService.getAllUsers(); + } + + @ApiOperation({ description: 'adding an operation to user' }) + @Post('operation/add') + async addOperation(@Body() data: IOperation) { + return await this.proxyService.addOperation(data); + } + + @ApiOperation({ description: 'get user payments' }) + @Get('operation/get/:userName') + async getOperations(@Param('userName') userName: string) { + return this.proxyService.getOperations(userName); + } + + @ApiOperation({ description: 'get all payments' }) + @Get('operation/get-all') + async getAllOperations() { + return this.proxyService.getAllOperations(); + } +} diff --git a/backend/src/modules/proxy/proxy.dto.ts b/backend/src/modules/proxy/proxy.dto.ts new file mode 100644 index 0000000..0dbd923 --- /dev/null +++ b/backend/src/modules/proxy/proxy.dto.ts @@ -0,0 +1,12 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class IProxyUser { + @ApiProperty({ description: 'user name of user to identify them', example: 'username' }) readonly userName!: string; + @ApiProperty({ description: 'some user description if you want', example: 'Description of user' }) readonly description?: string; + @ApiProperty({ description: 'user link to connect to the proxy', example: 'vless://....' }) readonly link!: string; + @ApiProperty({ description: 'telegram user id to connect to user entity', example: '187564' }) readonly user_id?: string; +} + +export class IOperation { + @ApiProperty({ description: 'user name of user, that made new operation', example: 'username' }) readonly userName!: string; +} diff --git a/backend/src/modules/proxy/proxy.module.ts b/backend/src/modules/proxy/proxy.module.ts new file mode 100644 index 0000000..41835f0 --- /dev/null +++ b/backend/src/modules/proxy/proxy.module.ts @@ -0,0 +1,11 @@ +import { Module } from '@nestjs/common'; +import { LibsModule } from 'libs/libs.module'; +import { ProxyController } from './proxy.controller'; +import { ProxyService } from './proxy.service'; + +@Module({ + imports: [LibsModule], + controllers: [ProxyController], + providers: [ProxyService], +}) +export class ProxyModule {} diff --git a/backend/src/modules/proxy/proxy.service.ts b/backend/src/modules/proxy/proxy.service.ts new file mode 100644 index 0000000..932cb36 --- /dev/null +++ b/backend/src/modules/proxy/proxy.service.ts @@ -0,0 +1,105 @@ +import { HttpException, HttpStatus, Injectable, Logger } from '@nestjs/common'; +import { InjectRepository } from '@nestjs/typeorm'; +import { Payment } from 'libs/database/payment.entity'; +import { ProxyUser } from 'libs/database/proxy_user.entity'; +import { User } from 'libs/database/user.entity'; +import { Repository } from 'typeorm'; +import { IOperation, IProxyUser } from './proxy.dto'; + +@Injectable() +export class ProxyService { + private readonly logger: Logger = new Logger(ProxyService.name); + constructor( + @InjectRepository(ProxyUser) private proxyUserRepository: Repository, + @InjectRepository(Payment) private paymentRepository: Repository, + @InjectRepository(User) private userRepository: Repository, + ) {} + + async newUser(data: IProxyUser) { + try { + if ( + (await this.proxyUserRepository.findOne({ where: { userName: data.userName } })) || + (await this.proxyUserRepository.findOne({ where: { user_id: data.user_id } })) + ) + throw new HttpException('User already exists', HttpStatus.FOUND); + const proxyUser = new ProxyUser(); + const user = data.user_id ? await this.userRepository.findOne({ where: { id: data.user_id } }) : null; + if (user) proxyUser.user = user; + proxyUser.description = data.description; + proxyUser.connectDate = new Date(); + proxyUser.userName = data.userName; + proxyUser.link = data.link; + return await this.proxyUserRepository.save(proxyUser); + } catch (error) { + if (error instanceof HttpException) { + this.logger.debug(`[proxy.newUser] error: user already created`); + throw error; + } + this.logger.debug(`[proxy.newUser] error: ${JSON.stringify(error)}`); + throw new HttpException('Bad data', HttpStatus.BAD_REQUEST); + } + } + + async getUser(userName: string) { + try { + const user = await this.proxyUserRepository.findOne({ where: { userName: userName } }); + if (!user) throw new HttpException('Not found', HttpStatus.NOT_FOUND); + return user; + } catch (error) { + if (error instanceof HttpException) { + this.logger.debug(`[proxy.getUser] error: not found`); + throw error; + } + this.logger.debug(`[proxy.getUser] error: ${JSON.stringify(error)}`); + throw new HttpException('Bad data', HttpStatus.BAD_REQUEST); + } + } + + async getAllUsers() { + try { + return await this.proxyUserRepository.find(); + } catch (error) { + this.logger.debug(`[proxy.getAllUsers] error: ${JSON.stringify(error)}`); + throw new HttpException('Bad data', HttpStatus.BAD_REQUEST); + } + } + + async addOperation(data: IOperation) { + try { + const user = await this.proxyUserRepository.findOne({ where: { userName: data.userName } }); + if (!user) throw new HttpException('Not found', HttpStatus.NOT_FOUND); + return await this.paymentRepository.save({ payTime: new Date(), user: user }); + } catch (error) { + if (error instanceof HttpException) { + this.logger.debug(`[proxy.addOperation] error: not found`); + throw error; + } + this.logger.debug(`[proxy.addOperation] error: ${JSON.stringify(error)}`); + throw new HttpException('Bad data', HttpStatus.BAD_REQUEST); + } + } + + async getOperations(userName: string) { + try { + const user = await this.proxyUserRepository.findOne({ where: { userName: userName }, relations: { payments: true } }); + if (!user) throw new HttpException('Not found', HttpStatus.NOT_FOUND); + return user.payments; + } catch (error) { + if (error instanceof HttpException) { + this.logger.debug(`[proxy.addOperation] error: not found`); + throw error; + } + this.logger.debug(`[proxy.addOperation] error: ${error}`); + throw new HttpException('Bad data', HttpStatus.BAD_REQUEST); + } + } + + async getAllOperations() { + try { + return await this.paymentRepository.find(); + } catch (error) { + this.logger.debug(`[proxy.addOperation] error: ${error}`); + throw new HttpException('Bad data', HttpStatus.BAD_REQUEST); + } + } +}