mirror of
https://github.com/MrSedan/neuro-reply-website.git
synced 2026-01-14 12:49:41 +03:00
2
.gitmodules
vendored
2
.gitmodules
vendored
@@ -1,4 +1,4 @@
|
||||
[submodule "neuro-reply-bot-reworked"]
|
||||
path = neuro-reply-bot-reworked
|
||||
url = git@github.com:MrSedan/neuro-reply-bot-reworked.git
|
||||
url = https://github.com/MrSedan/neuro-reply-bot-reworked.git
|
||||
branch = dev
|
||||
2
backend/.dockerignore
Normal file
2
backend/.dockerignore
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules
|
||||
dockerfile
|
||||
9
backend/Dockerfile
Normal file
9
backend/Dockerfile
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM node:lts-alpine
|
||||
WORKDIR /app
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
COPY package.json pnpm-*.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
COPY . .
|
||||
RUN pnpm build
|
||||
EXPOSE 3000
|
||||
CMD [ "pnpm", "start:prod" ]
|
||||
8
backend/Dockerfile.dev
Normal file
8
backend/Dockerfile.dev
Normal file
@@ -0,0 +1,8 @@
|
||||
FROM node:lts-alpine
|
||||
WORKDIR /app
|
||||
RUN corepack enable && corepack prepare pnpm@latest --activate
|
||||
COPY package.json pnpm-*.yaml ./
|
||||
RUN pnpm install --frozen-lockfile
|
||||
COPY . .
|
||||
EXPOSE 3000
|
||||
CMD [ "pnpm", "start:dev" ]
|
||||
22
backend/libs/database/payment.entity.ts
Normal file
22
backend/libs/database/payment.entity.ts
Normal file
@@ -0,0 +1,22 @@
|
||||
import { Column, Entity, JoinColumn, ManyToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { ProxyUser } from './proxy_user.entity';
|
||||
|
||||
@Entity()
|
||||
export class Payment {
|
||||
constructor(props?: Partial<Payment>) {
|
||||
Object.assign(this, props);
|
||||
}
|
||||
|
||||
@PrimaryGeneratedColumn('increment')
|
||||
public id!: number;
|
||||
|
||||
@Column()
|
||||
public user_uuid!: string;
|
||||
|
||||
@Column({ type: 'timestamptz' })
|
||||
public payTime!: Date;
|
||||
|
||||
@ManyToOne(() => ProxyUser, { onDelete: 'CASCADE' })
|
||||
@JoinColumn({ name: 'user_uuid' })
|
||||
user: ProxyUser;
|
||||
}
|
||||
@@ -22,6 +22,9 @@ export class Post {
|
||||
@Column({ type: 'timestamptz' })
|
||||
public timestamp!: Date;
|
||||
|
||||
@Column({ type: 'timestamptz', nullable: true })
|
||||
public edit_timestamp?: Date;
|
||||
|
||||
@Column({ nullable: false })
|
||||
public from_user_id!: string;
|
||||
|
||||
|
||||
35
backend/libs/database/proxy_user.entity.ts
Normal file
35
backend/libs/database/proxy_user.entity.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { Column, Entity, JoinColumn, OneToMany, OneToOne, PrimaryGeneratedColumn } from 'typeorm';
|
||||
import { Payment } from './payment.entity';
|
||||
import { User } from './user.entity';
|
||||
|
||||
@Entity()
|
||||
export class ProxyUser {
|
||||
constructor(props?: Partial<ProxyUser>) {
|
||||
Object.assign(this, props);
|
||||
}
|
||||
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
public uuid!: string;
|
||||
|
||||
@Column({ nullable: false, unique: true })
|
||||
public userName!: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
public description?: string;
|
||||
|
||||
@Column({ nullable: false })
|
||||
public link!: string;
|
||||
|
||||
@Column({ nullable: false, type: 'timestamptz' })
|
||||
public connectDate!: Date;
|
||||
|
||||
@Column({ nullable: true })
|
||||
public user_id!: string;
|
||||
|
||||
@OneToOne(() => User, (user) => user.id, { onDelete: 'CASCADE', onUpdate: 'CASCADE', nullable: true })
|
||||
@JoinColumn({ name: 'user_id' })
|
||||
public user?: User;
|
||||
|
||||
@OneToMany(() => Payment, (payment) => payment.user)
|
||||
public payments: Payment[];
|
||||
}
|
||||
@@ -4,8 +4,10 @@ import { User } from './database/user.entity';
|
||||
import { Admin } from './database/admin.entity';
|
||||
import { Post } from './database/post.entity';
|
||||
import { Image } from './database/image.entity';
|
||||
import { Payment } from './database/payment.entity';
|
||||
import { ProxyUser } from './database/proxy_user.entity';
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([User, Admin, Post, Image])],
|
||||
imports: [TypeOrmModule.forFeature([User, Admin, Post, Image, Payment, ProxyUser])],
|
||||
exports: [TypeOrmModule],
|
||||
})
|
||||
export class LibsModule {}
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"start": "nest start",
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"start:prod": "node dist/src/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
|
||||
12
backend/src/app.controller.ts
Normal file
12
backend/src/app.controller.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import { Controller, Get } from '@nestjs/common';
|
||||
import { ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
@ApiTags('App')
|
||||
@Controller()
|
||||
export class AppController {
|
||||
@ApiOperation({ description: 'check site availability' })
|
||||
@Get('ping')
|
||||
pingpong() {
|
||||
return 'pong';
|
||||
}
|
||||
}
|
||||
@@ -2,15 +2,25 @@ import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule, TypeOrmModuleOptions } from '@nestjs/typeorm';
|
||||
import { config } from 'config';
|
||||
import { LibsModule } from 'libs/libs.module';
|
||||
import { AppController } from './app.controller';
|
||||
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(<TypeOrmModuleOptions>config.database)],
|
||||
controllers: [],
|
||||
imports: [
|
||||
LibsModule,
|
||||
PostModule,
|
||||
AdminModule,
|
||||
UserModule,
|
||||
ImageModule,
|
||||
ProxyModule,
|
||||
TypeOrmModule.forRoot(<TypeOrmModuleOptions>config.database),
|
||||
],
|
||||
controllers: [AppController],
|
||||
providers: [AppInitService],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -39,4 +39,10 @@ export class PostController {
|
||||
async editPost(@Param('postId') postId: string, @Body() data: IEditPost) {
|
||||
return await this.postService.editPost(postId, data);
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'Get post to post' })
|
||||
@Get('post')
|
||||
async post() {
|
||||
return await this.postService.post();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ export class PostService {
|
||||
}
|
||||
if (post.text !== data.text) {
|
||||
post.text = data.text;
|
||||
post.timestamp = new Date();
|
||||
post.edit_timestamp = new Date();
|
||||
await this.postRepository.save(post);
|
||||
}
|
||||
return post;
|
||||
@@ -95,4 +95,23 @@ export class PostService {
|
||||
throw new HttpException("Can't find post with this media group id", HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
|
||||
async post() {
|
||||
try {
|
||||
const posts = await this.postRepository.find({ order: { timestamp: 'ASC' }, where: { posted: false }, relations: { images: true } });
|
||||
if (!posts.length) throw new HttpException('Nothing to post', HttpStatus.NOT_FOUND);
|
||||
const post = posts[0];
|
||||
post.posted = true;
|
||||
this.logger.log(`[post.post] Post ${post.uuid} is posted`);
|
||||
await this.postRepository.save(post);
|
||||
return post;
|
||||
} catch (error) {
|
||||
if (error instanceof HttpException) {
|
||||
this.logger.debug('[post.post] Not found');
|
||||
throw error;
|
||||
}
|
||||
this.logger.debug(`[post.post] error: ${JSON.stringify(error)}`);
|
||||
throw new HttpException('Bad data', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
46
backend/src/modules/proxy/proxy.controller.ts
Normal file
46
backend/src/modules/proxy/proxy.controller.ts
Normal file
@@ -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('new-user')
|
||||
async newUser(@Body() data: IProxyUser) {
|
||||
return await this.proxyService.newUser(data);
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'get user by its username' })
|
||||
@Get('get-user/:userName')
|
||||
async getUser(@Param('userName') userName: string) {
|
||||
return await this.proxyService.getUser(userName);
|
||||
}
|
||||
|
||||
@ApiOperation({ description: 'get all users of proxy' })
|
||||
@Get('get-all-users')
|
||||
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();
|
||||
}
|
||||
}
|
||||
12
backend/src/modules/proxy/proxy.dto.ts
Normal file
12
backend/src/modules/proxy/proxy.dto.ts
Normal file
@@ -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;
|
||||
}
|
||||
11
backend/src/modules/proxy/proxy.module.ts
Normal file
11
backend/src/modules/proxy/proxy.module.ts
Normal file
@@ -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 {}
|
||||
106
backend/src/modules/proxy/proxy.service.ts
Normal file
106
backend/src/modules/proxy/proxy.service.ts
Normal file
@@ -0,0 +1,106 @@
|
||||
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<ProxyUser>,
|
||||
@InjectRepository(Payment) private paymentRepository: Repository<Payment>,
|
||||
@InjectRepository(User) private userRepository: Repository<User>,
|
||||
) {}
|
||||
|
||||
async newUser(data: IProxyUser) {
|
||||
try {
|
||||
if (
|
||||
(await this.proxyUserRepository.findOne({ where: { userName: data.userName } })) || data.user_id
|
||||
? await this.proxyUserRepository.findOne({ where: { user_id: data.user_id } })
|
||||
: false
|
||||
)
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
74
docker-compose.dev.yml
Normal file
74
docker-compose.dev.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
version: '3.9'
|
||||
|
||||
services:
|
||||
db:
|
||||
container_name: neuro_db_dev
|
||||
image: postgres:alpine
|
||||
environment:
|
||||
- POSTGRES_USER=${DATABASE_USER}
|
||||
- POSTGRES_PASSWORD=${DATABASE_PASSWORD}
|
||||
- POSTGRES_DB=${DATABASE_NAME}
|
||||
- PGDATA=/var/lib/postgresql/data/pgdata
|
||||
ports:
|
||||
- "${DATABASE_PORT}:5432"
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
- labnet
|
||||
volumes:
|
||||
- neuro_postgres_db:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
backend:
|
||||
container_name: neuro_backend_dev
|
||||
build:
|
||||
context: ./backend
|
||||
dockerfile: Dockerfile.dev
|
||||
develop:
|
||||
watch:
|
||||
- action: sync
|
||||
path: ./backend/src
|
||||
target: ./app/src
|
||||
ignore:
|
||||
- node_modules/
|
||||
- action: rebuild
|
||||
path: package.json
|
||||
- action: rebuild
|
||||
path: ./libs
|
||||
environment:
|
||||
- DATABASE_PORT=5432
|
||||
- DATABASE_HOST=db
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
- labnet
|
||||
ports:
|
||||
- 3000:3000
|
||||
depends_on:
|
||||
- db
|
||||
restart: always
|
||||
|
||||
bot:
|
||||
container_name: neuro_bot_dev
|
||||
|
||||
build:
|
||||
context: ./neuro-reply-bot-reworked
|
||||
dockerfile: Dockerfile.dev
|
||||
environment:
|
||||
- API_URL=http://backend:3000
|
||||
networks:
|
||||
- labnet
|
||||
depends_on:
|
||||
- backend
|
||||
restart: always
|
||||
develop:
|
||||
watch:
|
||||
- action: sync
|
||||
path: ./neuro-reply-bot-reworked/
|
||||
target: ./app/
|
||||
|
||||
volumes:
|
||||
neuro_postgres_db:
|
||||
driver: local
|
||||
networks:
|
||||
labnet:
|
||||
@@ -13,9 +13,41 @@ services:
|
||||
- "${DATABASE_PORT}:5432"
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
- labnet
|
||||
volumes:
|
||||
- neuro_postgres_db:/var/lib/postgresql/data
|
||||
restart: always
|
||||
|
||||
backend:
|
||||
container_name: neuro_backend
|
||||
build: ./backend
|
||||
environment:
|
||||
- DATABASE_PORT=5432
|
||||
- DATABASE_HOST=db
|
||||
env_file:
|
||||
- .env
|
||||
networks:
|
||||
- labnet
|
||||
ports:
|
||||
- 3000:3000
|
||||
depends_on:
|
||||
- db
|
||||
restart: always
|
||||
|
||||
bot:
|
||||
container_name: neuro_bot
|
||||
build: ./neuro-reply-bot-reworked
|
||||
environment:
|
||||
- API_URL=http://backend:3000
|
||||
networks:
|
||||
- labnet
|
||||
depends_on:
|
||||
- backend
|
||||
restart: always
|
||||
|
||||
volumes:
|
||||
neuro_postgres_db:
|
||||
driver: local
|
||||
networks:
|
||||
labnet:
|
||||
|
||||
Submodule neuro-reply-bot-reworked updated: d286da698e...142d18bac3
Reference in New Issue
Block a user