initial commit
31
.eslintrc.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
tsconfigRootDir: __dirname,
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||||
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
],
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
ignorePatterns: ['.eslintrc.js'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/interface-name-prefix': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'prettier/prettier': [
|
||||||
|
'error',
|
||||||
|
{
|
||||||
|
endOfLine: 'auto',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
38
.gitignore
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
# compiled output
|
||||||
|
/dist
|
||||||
|
/node_modules
|
||||||
|
yarn.lock
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
|
||||||
|
# Tests
|
||||||
|
/coverage
|
||||||
|
/.nyc_output
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
.c9/
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
*.sublime-workspace
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
.env
|
||||||
4
.prettierrc
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "all"
|
||||||
|
}
|
||||||
73
README.md
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<p align="center">
|
||||||
|
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="200" alt="Nest Logo" /></a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
|
||||||
|
[circleci-url]: https://circleci.com/gh/nestjs/nest
|
||||||
|
|
||||||
|
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
|
||||||
|
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
|
||||||
|
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
|
||||||
|
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
|
||||||
|
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
|
||||||
|
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
|
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
|
||||||
|
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg"/></a>
|
||||||
|
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
|
||||||
|
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow"></a>
|
||||||
|
</p>
|
||||||
|
<!--[](https://opencollective.com/nest#backer)
|
||||||
|
[](https://opencollective.com/nest#sponsor)-->
|
||||||
|
|
||||||
|
## Description
|
||||||
|
|
||||||
|
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the app
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# development
|
||||||
|
$ yarn run start
|
||||||
|
|
||||||
|
# watch mode
|
||||||
|
$ yarn run start:dev
|
||||||
|
|
||||||
|
# production mode
|
||||||
|
$ yarn run start:prod
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# unit tests
|
||||||
|
$ yarn run test
|
||||||
|
|
||||||
|
# e2e tests
|
||||||
|
$ yarn run test:e2e
|
||||||
|
|
||||||
|
# test coverage
|
||||||
|
$ yarn run test:cov
|
||||||
|
```
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||||
|
|
||||||
|
## Stay in touch
|
||||||
|
|
||||||
|
- Author - [Kamil Myśliwiec](https://kamilmysliwiec.com)
|
||||||
|
- Website - [https://nestjs.com](https://nestjs.com/)
|
||||||
|
- Twitter - [@nestframework](https://twitter.com/nestframework)
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Nest is [MIT licensed](LICENSE).
|
||||||
15
docker-compose.dev.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mysql:8.0
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 4dj3sK!BH4MrLhLn
|
||||||
|
MYSQL_DATABASE: magicow
|
||||||
|
MYSQL_PASSWORD: 4dj3sK!BH4MrLhLn
|
||||||
|
ports:
|
||||||
|
# <Port exposed> : <MySQL Port running inside container>
|
||||||
|
- '3307:3306'
|
||||||
|
volumes:
|
||||||
|
- magicow:/var/lib/mysql
|
||||||
|
# Names our volume
|
||||||
|
volumes:
|
||||||
|
magicow:
|
||||||
15
docker-compose.yml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: mysql:8.0
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: 4dj3sK!BH4MrLhLn
|
||||||
|
MYSQL_DATABASE: magicow
|
||||||
|
MYSQL_PASSWORD: 4dj3sK!BH4MrLhLn
|
||||||
|
ports:
|
||||||
|
# <Port exposed> : <MySQL Port running inside container>
|
||||||
|
- '3306:3306'
|
||||||
|
volumes:
|
||||||
|
- magicow:/var/lib/mysql
|
||||||
|
# Names our volume
|
||||||
|
volumes:
|
||||||
|
magicow:
|
||||||
557
dump_with_orders.sql
Normal file
73
migrater.txt
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
CREATE TABLE Gifts (
|
||||||
|
-> id INT AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
-> lvl INT NOT NULL,
|
||||||
|
-> iconUrl VARCHAR(255),
|
||||||
|
-> name VARCHAR(255) NOT NULL,
|
||||||
|
-> type ENUM('item', 'product', 'service', 'privilege', 'set') NOT NULL
|
||||||
|
-> );
|
||||||
|
|
||||||
|
INSERT INTO Gifts (lvl, iconUrl, name, type) VALUES
|
||||||
|
(1, 'src/icons/lvl1.png', 'Rifle M39', 'item'),
|
||||||
|
(2, 'src/icons/lvl2.png', 'Assault Rifle', 'item'),
|
||||||
|
(3, 'src/icons/lvl3.png', '500 Silver Coins', 'product'),
|
||||||
|
(4, 'src/icons/lvl4.png', 'Metabolism for 7 Days', 'service'),
|
||||||
|
(5, 'src/icons/lvl5.png', 'Recycler', 'item'),
|
||||||
|
(6, 'src/icons/lvl6.png', 'LMG M249', 'item'),
|
||||||
|
(7, 'src/icons/lvl7.png', 'Workbench Level 3', 'item'),
|
||||||
|
(8, 'src/icons/lvl8.png', 'VIP', 'privilege'),
|
||||||
|
(9, 'src/icons/lvl9.png', 'Raider Set', 'set'),
|
||||||
|
(10, 'src/icons/lvl10.png', '5 Levels of Battle Pass', 'service'),
|
||||||
|
(11, 'src/icons/lvl11.png', 'GOD Kits', 'service'),
|
||||||
|
(12, 'src/icons/lvl12.png', '10000 Silver Coins', 'product'),
|
||||||
|
(13, 'src/icons/lvl13.png', '+100% Battle Pass Experience', 'product'),
|
||||||
|
(14, 'src/icons/lvl14.png', 'GOD', 'privilege'),
|
||||||
|
(15, 'src/icons/lvl15.png', 'Unique Nick Color + Respawn Set', 'item'),
|
||||||
|
(16, 'src/icons/lvl16.png', '25 Levels of Magic Pass', 'product'),
|
||||||
|
(17, 'src/icons/lvl17.png', 'All Recipes', 'product'),
|
||||||
|
(18, 'src/icons/lvl18.png', 'All Privileges for 6 Days', 'privilege'),
|
||||||
|
(19, 'src/icons/lvl19.png', 'Priority Queue on Main/Long Servers for 90 Days', 'service'),
|
||||||
|
(20, 'src/icons/lvl20.png', '50000 Silver', 'product'),
|
||||||
|
(21, 'src/icons/lvl21.png', 'Unique Set and Prefix [DONATER] in Red', 'item');
|
||||||
|
|
||||||
|
CREATE TABLE UserGifts (
|
||||||
|
userId INT,
|
||||||
|
giftId INT,
|
||||||
|
PRIMARY KEY (userId, giftId),
|
||||||
|
FOREIGN KEY (userId) REFERENCES User(id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (giftId) REFERENCES Gifts(id) ON DELETE CASCADE
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE UserGifts
|
||||||
|
ADD COLUMN amount INT DEFAULT 0;
|
||||||
|
|
||||||
|
ALTER TABLE Product
|
||||||
|
ADD COLUMN isGift TINYINT(1) DEFAULT 0,
|
||||||
|
ADD COLUMN lvl INT DEFAULT NULL,
|
||||||
|
ADD COLUMN iconUrl VARCHAR(255) DEFAULT NULL;
|
||||||
|
|
||||||
|
INSERT INTO Product (name_ru, name_en, type, image, iconUrl, isGift, lvl, price, amount, hidden)
|
||||||
|
VALUES
|
||||||
|
('Rifle M39', 'Rifle M39', 'GAME_ITEM', 'src/icons/lvl1.png', 'src/icons/lvl1.png', true, 1, 100, 1, 1),
|
||||||
|
('Assault Rifle', 'Assault Rifle', 'GAME_ITEM', 'src/icons/lvl2.png', 'src/icons/lvl2.png', true, 2, 150, 1, 1),
|
||||||
|
('500 Silver Coins', '500 Silver Coins', 'CURRENCY', 'src/icons/lvl3.png', 'src/icons/lvl3.png', true, 3, 500, 1, 1),
|
||||||
|
('Metabolism for 7 Days', 'Metabolism for 7 Days', 'SERVICE', 'src/icons/lvl4.png', 'src/icons/lvl4.png', true, 4, 200, 1, 1),
|
||||||
|
('Recycler', 'Recycler', 'GAME_ITEM', 'src/icons/lvl5.png', 'src/icons/lvl5.png', true, 5, 80, 1, 1),
|
||||||
|
('LMG M249', 'LMG M249', 'GAME_ITEM', 'src/icons/lvl6.png', 'src/icons/lvl6.png', true, 6, 120, 1, 1),
|
||||||
|
('Workbench Level 3', 'Workbench Level 3', 'GAME_ITEM', 'src/icons/lvl7.png', 'src/icons/lvl7.png', true, 7, 200, 1, 1),
|
||||||
|
('VIP', 'VIP', 'SERVICE', 'src/icons/lvl8.png', 'src/icons/lvl8.png', true, 8, 300, 1, 1),
|
||||||
|
('Raider Set', 'Raider Set', 'SETS_OF_PRODUCTS', 'src/icons/lvl9.png', 'src/icons/lvl9.png', true, 9, 400, 1, 1),
|
||||||
|
('5 Levels of Battle Pass', '5 Levels of Battle Pass', 'SERVICE', 'src/icons/lvl10.png', 'src/icons/lvl10.png', true, 10, 250, 1, 1),
|
||||||
|
('GOD Kits', 'GOD Kits', 'SERVICE', 'src/icons/lvl11.png', 'src/icons/lvl11.png', true, 11, 350, 1, 1),
|
||||||
|
('10000 Silver Coins', '10000 Silver Coins', 'CURRENCY', 'src/icons/lvl12.png', 'src/icons/lvl12.png', true, 12, 10000, 1, 1),
|
||||||
|
('+100% Battle Pass Experience', '+100% Battle Pass Experience', 'CURRENCY', 'src/icons/lvl13.png', 'src/icons/lvl13.png', true, 13, 600, 1, 1),
|
||||||
|
('GOD', 'GOD', 'SERVICE', 'src/icons/lvl14.png', 'src/icons/lvl14.png', true, 14, 500, 1, 1),
|
||||||
|
('Unique Nick Color + Respawn Set', 'Unique Nick Color + Respawn Set', 'GAME_ITEM', 'src/icons/lvl15.png', 'src/icons/lvl15.png', true, 15, 90, 1, 1),
|
||||||
|
('25 Levels of Magic Pass', '25 Levels of Magic Pass', 'CURRENCY', 'src/icons/lvl16.png', 'src/icons/lvl16.png', true, 16, 750, 1, 1),
|
||||||
|
('All Recipes', 'All Recipes', 'CURRENCY', 'src/icons/lvl17.png', 'src/icons/lvl17.png', true, 17, 300, 1, 1),
|
||||||
|
('All Privileges for 6 Days', 'All Privileges for 6 Days', 'SERVICE', 'src/icons/lvl18.png', 'src/icons/lvl18.png', true, 18, 450, 1, 1),
|
||||||
|
('Priority Queue on Main/Long Servers for 90 Days', 'Priority Queue on Main/Long Servers for 90 Days', 'SERVICE', 'src/icons/lvl19.png', 'src/icons/lvl19.png', true, 19, 550, 1, 1),
|
||||||
|
('50000 Silver', '50000 Silver', 'CURRENCY', 'src/icons/lvl20.png', 'src/icons/lvl20.png', true, 20, 50000, 1, 1),
|
||||||
|
('Unique Set and Prefix [DONATER] in Red', 'Unique Set and Prefix [DONATER] in Red', 'GAME_ITEM', 'src/icons/lvl21.png', 'src/icons/lvl21.png', true, 21, 120, 1, 1);
|
||||||
|
|
||||||
|
ALTER TABLE Inventory
|
||||||
|
MODIFY historyOfPurchaseId INT NULL;
|
||||||
8
nest-cli.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://json.schemastore.org/nest-cli",
|
||||||
|
"collection": "@nestjs/schematics",
|
||||||
|
"sourceRoot": "src",
|
||||||
|
"compilerOptions": {
|
||||||
|
"deleteOutDir": true
|
||||||
|
}
|
||||||
|
}
|
||||||
9522
package-lock.json
generated
Normal file
99
package.json
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
{
|
||||||
|
"name": "project",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"description": "",
|
||||||
|
"author": "",
|
||||||
|
"private": true,
|
||||||
|
"license": "UNLICENSED",
|
||||||
|
"scripts": {
|
||||||
|
"build": "nest build",
|
||||||
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:dev": "nest start --watch",
|
||||||
|
"start:debug": "nest start --debug --watch",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:cov": "jest --coverage",
|
||||||
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
|
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||||
|
"migrate:up": "npx prisma migrate deploy",
|
||||||
|
"migrate:dev": "npx prisma migrate dev"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs-modules/ioredis": "^2.0.2",
|
||||||
|
"@nestjs/axios": "^3.0.0",
|
||||||
|
"@nestjs/common": "^9.0.0",
|
||||||
|
"@nestjs/config": "^3.2.3",
|
||||||
|
"@nestjs/core": "^9.0.0",
|
||||||
|
"@nestjs/jwt": "^10.0.3",
|
||||||
|
"@nestjs/passport": "^9.0.3",
|
||||||
|
"@nestjs/platform-express": "^9.0.0",
|
||||||
|
"@nestjs/schedule": "^3.0.4",
|
||||||
|
"@nestjs/serve-static": "^4.0.2",
|
||||||
|
"@nestjs/swagger": "^6.2.1",
|
||||||
|
"@prisma/client": "^4.12.0",
|
||||||
|
"@types/bcrypt": "^5.0.0",
|
||||||
|
"@types/cookie-parser": "^1.4.3",
|
||||||
|
"@types/multer": "^1.4.7",
|
||||||
|
"@types/passport-jwt": "^3.0.8",
|
||||||
|
"axios": "^1.4.0",
|
||||||
|
"bcrypt": "^5.1.0",
|
||||||
|
"class-transformer": "^0.5.1",
|
||||||
|
"class-validator": "^0.14.0",
|
||||||
|
"cookie-parser": "^1.4.6",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"ioredis": "^5.4.1",
|
||||||
|
"nodemailer": "^6.9.3",
|
||||||
|
"passport": "^0.6.0",
|
||||||
|
"passport-jwt": "^4.0.1",
|
||||||
|
"passport-local": "^1.0.0",
|
||||||
|
"passport-steam": "^1.0.18",
|
||||||
|
"redis": "^4.7.0",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"rxjs": "^7.2.0",
|
||||||
|
"ua-parser-js": "^1.0.35"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "^9.0.0",
|
||||||
|
"@nestjs/schematics": "^9.0.0",
|
||||||
|
"@nestjs/testing": "^9.0.0",
|
||||||
|
"@types/express": "^4.17.13",
|
||||||
|
"@types/jest": "29.2.4",
|
||||||
|
"@types/node": "18.11.18",
|
||||||
|
"@types/supertest": "^2.0.11",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.0.0",
|
||||||
|
"@typescript-eslint/parser": "^5.0.0",
|
||||||
|
"eslint": "^8.0.1",
|
||||||
|
"eslint-config-prettier": "^8.3.0",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"jest": "29.3.1",
|
||||||
|
"prettier": "^2.3.2",
|
||||||
|
"prisma": "4.12.0",
|
||||||
|
"source-map-support": "^0.5.20",
|
||||||
|
"supertest": "^6.1.3",
|
||||||
|
"ts-jest": "29.0.3",
|
||||||
|
"ts-loader": "^9.2.3",
|
||||||
|
"ts-node": "^10.0.0",
|
||||||
|
"tsconfig-paths": "4.1.1",
|
||||||
|
"typescript": "^4.7.4"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"**/*.(t|j)s"
|
||||||
|
],
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
|
}
|
||||||
|
}
|
||||||
223
prisma/migrations/20230710123045_initialization/migration.sql
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `User` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`steamName` LONGTEXT NOT NULL,
|
||||||
|
`steamID` VARCHAR(191) NOT NULL,
|
||||||
|
`email` VARCHAR(191) NULL,
|
||||||
|
`steamAvatar` LONGTEXT NOT NULL,
|
||||||
|
`mainBalance` INTEGER NOT NULL,
|
||||||
|
`bonusBalance` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`firstDateAuth` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`lvl` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`experience` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`sumOfDeposits` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`sumOfRefunds` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
`discordLink` LONGTEXT NULL,
|
||||||
|
`VKLink` LONGTEXT NULL,
|
||||||
|
`TGLink` LONGTEXT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`lastActivity` DATETIME(3) NULL,
|
||||||
|
`role` ENUM('CLIENT', 'ADMINISTRATOR') NOT NULL DEFAULT 'CLIENT',
|
||||||
|
|
||||||
|
UNIQUE INDEX `User_steamID_key`(`steamID`),
|
||||||
|
UNIQUE INDEX `User_email_key`(`email`),
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Transaction` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`userId` INTEGER NULL,
|
||||||
|
`method` LONGTEXT NOT NULL,
|
||||||
|
`amount` INTEGER NOT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`status` ENUM('SUCCESS', 'FALSE', 'DENIED') NOT NULL DEFAULT 'FALSE',
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Purchase` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`userId` INTEGER NULL,
|
||||||
|
`amount` INTEGER NOT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`lostMainBalance` INTEGER NOT NULL,
|
||||||
|
`lostBonusBalance` INTEGER NOT NULL,
|
||||||
|
`refund` BOOLEAN NOT NULL,
|
||||||
|
`productId` INTEGER NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Transfers` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`senderId` INTEGER NOT NULL,
|
||||||
|
`receiverId` INTEGER NOT NULL,
|
||||||
|
`amount` INTEGER NOT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Inventory` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`amount` INTEGER NOT NULL,
|
||||||
|
`status` ENUM('INVENTORY', 'ON_SERVER') NOT NULL DEFAULT 'INVENTORY',
|
||||||
|
`dateOfReceive` DATETIME(3) NULL,
|
||||||
|
`historyOfPurchaseId` INTEGER NOT NULL,
|
||||||
|
`userId` INTEGER NOT NULL,
|
||||||
|
`serverTypeId` INTEGER NOT NULL,
|
||||||
|
`serverId` INTEGER NOT NULL,
|
||||||
|
`serverName` LONGTEXT NULL,
|
||||||
|
`productId` INTEGER NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `ServerType` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` LONGTEXT NOT NULL,
|
||||||
|
`description` LONGTEXT NOT NULL,
|
||||||
|
`number` INTEGER NOT NULL,
|
||||||
|
`hidden` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Server` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`serverTypeId` INTEGER NOT NULL,
|
||||||
|
`IP` LONGTEXT NOT NULL,
|
||||||
|
`port` LONGTEXT NOT NULL,
|
||||||
|
`apiKey` LONGTEXT NOT NULL,
|
||||||
|
`name` VARCHAR(191) NOT NULL DEFAULT 'SERVER',
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Product` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` LONGTEXT NOT NULL,
|
||||||
|
`description` LONGTEXT NULL,
|
||||||
|
`image` LONGTEXT NULL,
|
||||||
|
`type` ENUM('GAME_ITEM', 'SERVICE', 'SETS_OF_PRODUCTS', 'HTTP_REQUEST', 'CURRENCY', 'CARDS') NOT NULL DEFAULT 'GAME_ITEM',
|
||||||
|
`productContent` JSON NULL,
|
||||||
|
`serverTypeId` INTEGER NULL,
|
||||||
|
`amount` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`isChangeAmount` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
`price` INTEGER NOT NULL,
|
||||||
|
`discount` INTEGER NULL,
|
||||||
|
`saleDiscount` INTEGER NULL,
|
||||||
|
`saleDeadline` DATETIME(3) NULL,
|
||||||
|
`maxCountOfSale` INTEGER NULL,
|
||||||
|
`hidden` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
`number` INTEGER NULL,
|
||||||
|
`autoactivation` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
`isBackground` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
`previewImage` VARCHAR(191) NULL,
|
||||||
|
`blockSize` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`label` INTEGER NOT NULL DEFAULT 1,
|
||||||
|
`isBackgroundImage` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Promocodes` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` LONGTEXT NOT NULL,
|
||||||
|
`countOfActivation` INTEGER NOT NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
`startDate` DATETIME(3) NOT NULL,
|
||||||
|
`endDate` DATETIME(3) NOT NULL,
|
||||||
|
`discountAmount` INTEGER NULL,
|
||||||
|
`depositBonus` INTEGER NULL,
|
||||||
|
`plusBonusBalance` INTEGER NULL,
|
||||||
|
`limitActivation` INTEGER NOT NULL,
|
||||||
|
`groupId` LONGTEXT NOT NULL,
|
||||||
|
`itemSet` LONGTEXT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `baseSettings` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`header` LONGTEXT NOT NULL,
|
||||||
|
`saleMode` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
`startBalance` INTEGER NOT NULL,
|
||||||
|
`mainPage` LONGTEXT NOT NULL,
|
||||||
|
`apiKey` LONGTEXT NOT NULL,
|
||||||
|
`IPWhiteList` LONGTEXT NOT NULL,
|
||||||
|
`panelURLs` JSON NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `urlSettings` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`icon` LONGTEXT NOT NULL,
|
||||||
|
`text` LONGTEXT NOT NULL,
|
||||||
|
`typeUrl` ENUM('SITE_SECTION', 'CUSTOM_PAGE', 'EXTERNAL_LINK', 'DROPDOWN_LIST') NOT NULL,
|
||||||
|
`url` LONGTEXT NOT NULL,
|
||||||
|
`hidden` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
`name` LONGTEXT NULL,
|
||||||
|
`sections` LONGTEXT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Token` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`token` LONGTEXT NOT NULL,
|
||||||
|
`userId` INTEGER NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Transaction` ADD CONSTRAINT `Transaction_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Purchase` ADD CONSTRAINT `Purchase_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Purchase` ADD CONSTRAINT `Purchase_productId_fkey` FOREIGN KEY (`productId`) REFERENCES `Product`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Transfers` ADD CONSTRAINT `Transfers_senderId_fkey` FOREIGN KEY (`senderId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Transfers` ADD CONSTRAINT `Transfers_receiverId_fkey` FOREIGN KEY (`receiverId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Inventory` ADD CONSTRAINT `Inventory_historyOfPurchaseId_fkey` FOREIGN KEY (`historyOfPurchaseId`) REFERENCES `Purchase`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Inventory` ADD CONSTRAINT `Inventory_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Inventory` ADD CONSTRAINT `Inventory_serverTypeId_fkey` FOREIGN KEY (`serverTypeId`) REFERENCES `ServerType`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Inventory` ADD CONSTRAINT `Inventory_serverId_fkey` FOREIGN KEY (`serverId`) REFERENCES `Server`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Inventory` ADD CONSTRAINT `Inventory_productId_fkey` FOREIGN KEY (`productId`) REFERENCES `Product`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Server` ADD CONSTRAINT `Server_serverTypeId_fkey` FOREIGN KEY (`serverTypeId`) REFERENCES `ServerType`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Product` ADD CONSTRAINT `Product_serverTypeId_fkey` FOREIGN KEY (`serverTypeId`) REFERENCES `ServerType`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Token` ADD CONSTRAINT `Token_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE RESTRICT ON UPDATE CASCADE;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Product` MODIFY `hidden` BOOLEAN NOT NULL DEFAULT true;
|
||||||
11
prisma/migrations/20230711090047_upd_product_2/migration.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to alter the column `discount` on the `Product` table. The data in that column could be lost. The data in that column will be cast from `Int` to `Double`.
|
||||||
|
- You are about to alter the column `saleDiscount` on the `Product` table. The data in that column could be lost. The data in that column will be cast from `Int` to `Double`.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Product` ADD COLUMN `nameID` VARCHAR(191) NULL,
|
||||||
|
MODIFY `discount` DOUBLE NULL DEFAULT 1,
|
||||||
|
MODIFY `saleDiscount` DOUBLE NULL DEFAULT 1;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `serverID` to the `Server` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Server` ADD COLUMN `serverID` INTEGER NOT NULL;
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `ServerType` MODIFY `hidden` BOOLEAN NOT NULL DEFAULT true;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `urlSettings` MODIFY `hidden` BOOLEAN NOT NULL DEFAULT true;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE `Inventory` DROP FOREIGN KEY `Inventory_serverId_fkey`;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Inventory` MODIFY `serverId` INTEGER NULL;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Inventory` ADD CONSTRAINT `Inventory_serverId_fkey` FOREIGN KEY (`serverId`) REFERENCES `Server`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Inventory` ADD COLUMN `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3);
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `isHaveSidebar` to the `urlSettings` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `item` to the `urlSettings` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `urlSettings` ADD COLUMN `isHaveSidebar` BOOLEAN NOT NULL,
|
||||||
|
ADD COLUMN `item` JSON NOT NULL,
|
||||||
|
MODIFY `text` LONGTEXT NULL;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `urlSettings` MODIFY `typeUrl` ENUM('SITE_SECTION', 'CUSTOM_PAGE', 'EXTERNAL_LINK', 'DROPDOWN_LIST') NOT NULL DEFAULT 'CUSTOM_PAGE';
|
||||||
16
prisma/migrations/20230717081320_upd_token/migration.sql
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `browser` to the `Token` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `clientIp` to the `Token` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `deviceName` to the `Token` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `deviceType` to the `Token` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `os` to the `Token` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Token` ADD COLUMN `browser` VARCHAR(191) NOT NULL,
|
||||||
|
ADD COLUMN `clientIp` VARCHAR(191) NOT NULL,
|
||||||
|
ADD COLUMN `deviceName` VARCHAR(191) NOT NULL,
|
||||||
|
ADD COLUMN `deviceType` VARCHAR(191) NOT NULL,
|
||||||
|
ADD COLUMN `os` VARCHAR(191) NOT NULL;
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Token` MODIFY `browser` VARCHAR(191) NULL DEFAULT 'postman',
|
||||||
|
MODIFY `clientIp` VARCHAR(191) NULL,
|
||||||
|
MODIFY `deviceName` VARCHAR(191) NULL,
|
||||||
|
MODIFY `deviceType` VARCHAR(191) NULL,
|
||||||
|
MODIFY `os` VARCHAR(191) NULL;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Token` MODIFY `os` VARCHAR(191) NULL DEFAULT 'windows';
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Token` ALTER COLUMN `browser` DROP DEFAULT,
|
||||||
|
ALTER COLUMN `os` DROP DEFAULT;
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Product` ADD COLUMN `buttonColor` ENUM('GREEN', 'BLUE') NOT NULL DEFAULT 'BLUE',
|
||||||
|
ADD COLUMN `height` INTEGER NULL,
|
||||||
|
ADD COLUMN `iconButton` VARCHAR(191) NULL,
|
||||||
|
ADD COLUMN `textButton` VARCHAR(191) NULL;
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `label` on the `Product` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `previewImage` on the `Product` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `item` on the `urlSettings` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `name` on the `urlSettings` table. All the data in the column will be lost.
|
||||||
|
- You are about to alter the column `sections` on the `urlSettings` table. The data in that column could be lost. The data in that column will be cast from `LongText` to `Json`.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Product` DROP COLUMN `label`,
|
||||||
|
DROP COLUMN `previewImage`;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `urlSettings` DROP COLUMN `item`,
|
||||||
|
DROP COLUMN `name`,
|
||||||
|
MODIFY `icon` LONGTEXT NULL,
|
||||||
|
MODIFY `sections` JSON NULL,
|
||||||
|
MODIFY `isHaveSidebar` BOOLEAN NULL;
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `isBackground` on the `Product` table. All the data in the column will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Product` DROP COLUMN `isBackground`,
|
||||||
|
ADD COLUMN `isBackgroundColor` BOOLEAN NOT NULL DEFAULT false;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
-- DropForeignKey
|
||||||
|
ALTER TABLE `Inventory` DROP FOREIGN KEY `Inventory_serverTypeId_fkey`;
|
||||||
|
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Inventory` MODIFY `serverTypeId` INTEGER NULL;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Inventory` ADD CONSTRAINT `Inventory_serverTypeId_fkey` FOREIGN KEY (`serverTypeId`) REFERENCES `ServerType`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Contacts` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` VARCHAR(191) NOT NULL,
|
||||||
|
`url` VARCHAR(191) NOT NULL,
|
||||||
|
`icon` VARCHAR(191) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Purchase` ADD COLUMN `dateOfPurchase` VARCHAR(191) NULL;
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Product` MODIFY `discount` INTEGER NULL DEFAULT 1,
|
||||||
|
MODIFY `saleDiscount` INTEGER NULL DEFAULT 1;
|
||||||
@ -0,0 +1,4 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Inventory` ADD COLUMN `isCanBeRefund` BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
ADD COLUMN `isPartOfPack` BOOLEAN NULL,
|
||||||
|
ADD COLUMN `packId` INTEGER NULL;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Visitors` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`ip` VARCHAR(191) NOT NULL,
|
||||||
|
`date` VARCHAR(191) NOT NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
11
prisma/migrations/20230814135012_upd_visitors/migration.sql
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `date` on the `Visitors` table. All the data in the column will be lost.
|
||||||
|
- Added the required column `sortDate` to the `Visitors` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Visitors` DROP COLUMN `date`,
|
||||||
|
ADD COLUMN `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
ADD COLUMN `sortDate` VARCHAR(191) NOT NULL;
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- Added the required column `sortedMonth` to the `Visitors` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Visitors` ADD COLUMN `sortedMonth` VARCHAR(191) NOT NULL;
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the column `description` on the `Product` table. All the data in the column will be lost.
|
||||||
|
- You are about to drop the column `name` on the `Product` table. All the data in the column will be lost.
|
||||||
|
- Added the required column `name_en` to the `Product` table without a default value. This is not possible if the table is not empty.
|
||||||
|
- Added the required column `name_ru` to the `Product` table without a default value. This is not possible if the table is not empty.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Product` RENAME COLUMN `description` TO `description_ru`,
|
||||||
|
RENAME COLUMN `name` TO `name_ru`,
|
||||||
|
ADD COLUMN `description_en` LONGTEXT NULL,
|
||||||
|
ADD COLUMN `name_en` LONGTEXT NOT NULL;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Product` ADD COLUMN `textButton_en` VARCHAR(191) NULL;
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `baseSettings` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- You are about to drop the `urlSettings` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropTable
|
||||||
|
ALTER TABLE `baseSettings` RENAME TO `BaseSettings`;
|
||||||
|
ALTER TABLE `urlSettings` RENAME TO `UrlSettings`;
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Transaction` MODIFY `status` ENUM('SUCCESS', 'FALSE', 'DENIED', 'REFUND') NOT NULL DEFAULT 'FALSE';
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Transaction` MODIFY `status` ENUM('SUCCESS', 'FALSE', 'DENIED', 'REFUND', 'IN_PROGRESS') NOT NULL DEFAULT 'FALSE';
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Transaction` ADD COLUMN `sendNotification` BOOLEAN NOT NULL DEFAULT false;
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `baseSettings` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- You are about to drop the `urlSettings` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `baseSettings` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`header` LONGTEXT NOT NULL,
|
||||||
|
`saleMode` BOOLEAN NOT NULL DEFAULT false,
|
||||||
|
`startBalance` INTEGER NOT NULL,
|
||||||
|
`mainPage` LONGTEXT NOT NULL,
|
||||||
|
`apiKey` LONGTEXT NOT NULL,
|
||||||
|
`IPWhiteList` LONGTEXT NOT NULL,
|
||||||
|
`panelURLs` JSON NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `urlSettings` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`icon` LONGTEXT NULL,
|
||||||
|
`text` LONGTEXT NULL,
|
||||||
|
`typeUrl` ENUM('SITE_SECTION', 'CUSTOM_PAGE', 'EXTERNAL_LINK', 'DROPDOWN_LIST') NOT NULL DEFAULT 'CUSTOM_PAGE',
|
||||||
|
`url` LONGTEXT NOT NULL,
|
||||||
|
`hidden` BOOLEAN NOT NULL DEFAULT true,
|
||||||
|
`sections` JSON NULL,
|
||||||
|
`isHaveSidebar` BOOLEAN NULL,
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- CreateTable
|
||||||
|
CREATE TABLE `Orders` (
|
||||||
|
`id` INTEGER NOT NULL AUTO_INCREMENT,
|
||||||
|
`userId` INTEGER NULL,
|
||||||
|
`createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
|
||||||
|
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
|
||||||
|
|
||||||
|
-- AddForeignKey
|
||||||
|
ALTER TABLE `Orders` ADD CONSTRAINT `Orders_userId_fkey` FOREIGN KEY (`userId`) REFERENCES `User`(`id`) ON DELETE SET NULL ON UPDATE CASCADE;
|
||||||
@ -0,0 +1,3 @@
|
|||||||
|
-- AlterTable
|
||||||
|
ALTER TABLE `Orders` ADD COLUMN `balance` INTEGER NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN `status` VARCHAR(191) NOT NULL DEFAULT 'PENDING';
|
||||||
12
prisma/migrations/20240919070647_init/migration.sql
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/*
|
||||||
|
Warnings:
|
||||||
|
|
||||||
|
- You are about to drop the `baseSettings` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
- You are about to drop the `urlSettings` table. If the table is not empty, all the data it contains will be lost.
|
||||||
|
|
||||||
|
*/
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE `baseSettings`;
|
||||||
|
|
||||||
|
-- DropTable
|
||||||
|
DROP TABLE `urlSettings`;
|
||||||
3
prisma/migrations/migration_lock.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Please do not edit this file manually
|
||||||
|
# It should be added in your version-control system (i.e. Git)
|
||||||
|
provider = "mysql"
|
||||||
320
prisma/schema.prisma
Normal file
@ -0,0 +1,320 @@
|
|||||||
|
generator client {
|
||||||
|
provider = "prisma-client-js"
|
||||||
|
binaryTargets = ["debian-openssl-1.1.x", "windows", "darwin-arm64"]
|
||||||
|
}
|
||||||
|
|
||||||
|
datasource db {
|
||||||
|
provider = "mysql"
|
||||||
|
url = env("DATABASE_URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
model User {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
steamName String @db.LongText
|
||||||
|
steamID String @unique
|
||||||
|
email String? @unique
|
||||||
|
steamAvatar String @db.LongText
|
||||||
|
mainBalance Int
|
||||||
|
bonusBalance Int @default(0)
|
||||||
|
firstDateAuth DateTime @default(now())
|
||||||
|
lvl Int @default(1)
|
||||||
|
experience Int @default(0)
|
||||||
|
sumOfDeposits Int @default(0)
|
||||||
|
sumOfRefunds Int @default(0)
|
||||||
|
discordLink String? @db.LongText
|
||||||
|
VKLink String? @db.LongText
|
||||||
|
TGLink String? @db.LongText
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lastActivity DateTime?
|
||||||
|
role USER_ROLE @default(CLIENT)
|
||||||
|
Inventory Inventory[]
|
||||||
|
Orders Orders[]
|
||||||
|
Purchase Purchase[]
|
||||||
|
Tokens Token[]
|
||||||
|
Transaction Transaction[]
|
||||||
|
TransfersReceiver Transfers[] @relation("receiver")
|
||||||
|
TransfersSender Transfers[] @relation("sender")
|
||||||
|
UserGifts UserGifts[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Transaction {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
userId Int?
|
||||||
|
method String @db.LongText
|
||||||
|
amount Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
status EStatusOfRefill @default(FALSE)
|
||||||
|
sendNotification Boolean @default(false)
|
||||||
|
pointAmount Int @default(0)
|
||||||
|
paymentId String? @db.VarChar(255)
|
||||||
|
user User? @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@index([userId], map: "Transaction_userId_fkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Purchase {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
userId Int?
|
||||||
|
amount Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
lostMainBalance Int
|
||||||
|
lostBonusBalance Int
|
||||||
|
refund Boolean
|
||||||
|
productId Int
|
||||||
|
dateOfPurchase String?
|
||||||
|
Inventory Inventory[]
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
user User? @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@index([productId], map: "Purchase_productId_fkey")
|
||||||
|
@@index([userId], map: "Purchase_userId_fkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Transfers {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
senderId Int
|
||||||
|
receiverId Int
|
||||||
|
amount Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
receiver User @relation("receiver", fields: [receiverId], references: [id])
|
||||||
|
sender User @relation("sender", fields: [senderId], references: [id])
|
||||||
|
|
||||||
|
@@index([receiverId], map: "Transfers_receiverId_fkey")
|
||||||
|
@@index([senderId], map: "Transfers_senderId_fkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Inventory {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
amount Int
|
||||||
|
status EStatusOfProductInInventory @default(INVENTORY)
|
||||||
|
dateOfReceive DateTime?
|
||||||
|
historyOfPurchaseId Int?
|
||||||
|
userId Int
|
||||||
|
serverTypeId Int?
|
||||||
|
serverId Int?
|
||||||
|
serverName String? @db.LongText
|
||||||
|
productId Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
isCanBeRefund Boolean @default(true)
|
||||||
|
isPartOfPack Boolean?
|
||||||
|
packId Int?
|
||||||
|
purchase Purchase? @relation(fields: [historyOfPurchaseId], references: [id], onDelete: Restrict)
|
||||||
|
product Product @relation(fields: [productId], references: [id])
|
||||||
|
server Server? @relation(fields: [serverId], references: [id])
|
||||||
|
serverType ServerType? @relation(fields: [serverTypeId], references: [id])
|
||||||
|
user User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@index([historyOfPurchaseId], map: "Inventory_historyOfPurchaseId_fkey")
|
||||||
|
@@index([productId], map: "Inventory_productId_fkey")
|
||||||
|
@@index([serverId], map: "Inventory_serverId_fkey")
|
||||||
|
@@index([serverTypeId], map: "Inventory_serverTypeId_fkey")
|
||||||
|
@@index([userId], map: "Inventory_userId_fkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
model ServerType {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String @db.LongText
|
||||||
|
description String @db.LongText
|
||||||
|
number Int
|
||||||
|
hidden Boolean @default(true)
|
||||||
|
Inventory Inventory[]
|
||||||
|
Product Product[]
|
||||||
|
Server Server[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model Server {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
serverTypeId Int
|
||||||
|
IP String @db.LongText
|
||||||
|
port String @db.LongText
|
||||||
|
apiKey String @db.LongText
|
||||||
|
name String @default("SERVER")
|
||||||
|
serverID Int
|
||||||
|
Inventory Inventory[]
|
||||||
|
typeOfSever ServerType @relation(fields: [serverTypeId], references: [id])
|
||||||
|
|
||||||
|
@@index([serverTypeId], map: "Server_serverTypeId_fkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Product {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name_ru String @db.LongText
|
||||||
|
description_ru String? @db.LongText
|
||||||
|
image String? @db.LongText
|
||||||
|
type ETypeOfProduct @default(GAME_ITEM)
|
||||||
|
productContent Json?
|
||||||
|
serverTypeId Int?
|
||||||
|
amount Int @default(1)
|
||||||
|
isChangeAmount Boolean @default(false)
|
||||||
|
price Int
|
||||||
|
discount Int? @default(1)
|
||||||
|
saleDiscount Int? @default(1)
|
||||||
|
saleDeadline DateTime?
|
||||||
|
maxCountOfSale Int?
|
||||||
|
hidden Boolean @default(true)
|
||||||
|
number Int?
|
||||||
|
autoactivation Boolean @default(false)
|
||||||
|
blockSize Int @default(1)
|
||||||
|
isBackgroundImage Boolean @default(false)
|
||||||
|
nameID String?
|
||||||
|
buttonColor EButtonColor @default(BLUE)
|
||||||
|
height Int?
|
||||||
|
iconButton String?
|
||||||
|
textButton String?
|
||||||
|
isBackgroundColor Boolean @default(false)
|
||||||
|
description_en String? @db.LongText
|
||||||
|
name_en String @db.LongText
|
||||||
|
textButton_en String?
|
||||||
|
isGift Boolean? @default(false)
|
||||||
|
lvl Int?
|
||||||
|
iconUrl String? @db.VarChar(255)
|
||||||
|
Inventory Inventory[]
|
||||||
|
serverType ServerType? @relation(fields: [serverTypeId], references: [id])
|
||||||
|
Purchase Purchase[]
|
||||||
|
|
||||||
|
@@index([serverTypeId], map: "Product_serverTypeId_fkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Promocodes {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String @db.LongText
|
||||||
|
countOfActivation Int
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
startDate DateTime
|
||||||
|
endDate DateTime
|
||||||
|
discountAmount Int?
|
||||||
|
depositBonus Int?
|
||||||
|
plusBonusBalance Int?
|
||||||
|
limitActivation Int
|
||||||
|
groupId String @default(uuid()) @db.LongText
|
||||||
|
itemSet String? @db.LongText
|
||||||
|
}
|
||||||
|
|
||||||
|
model BaseSettings {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
header String @db.LongText
|
||||||
|
saleMode Boolean @default(false)
|
||||||
|
startBalance Int
|
||||||
|
mainPage String @db.LongText
|
||||||
|
apiKey String @db.LongText
|
||||||
|
IPWhiteList String @db.LongText
|
||||||
|
panelURLs Json?
|
||||||
|
}
|
||||||
|
|
||||||
|
model UrlSettings {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
icon String? @db.LongText
|
||||||
|
text String? @db.LongText
|
||||||
|
typeUrl ETypeOfURL @default(CUSTOM_PAGE)
|
||||||
|
url String @db.LongText
|
||||||
|
hidden Boolean @default(true)
|
||||||
|
sections Json?
|
||||||
|
isHaveSidebar Boolean?
|
||||||
|
}
|
||||||
|
|
||||||
|
model Token {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
token String @db.LongText
|
||||||
|
userId Int
|
||||||
|
browser String?
|
||||||
|
clientIp String?
|
||||||
|
deviceName String?
|
||||||
|
deviceType String?
|
||||||
|
os String?
|
||||||
|
User User @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@index([userId], map: "Token_userId_fkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Contacts {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
name String
|
||||||
|
url String
|
||||||
|
icon String
|
||||||
|
}
|
||||||
|
|
||||||
|
model Visitors {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
ip String
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
sortDate String
|
||||||
|
sortedMonth String
|
||||||
|
}
|
||||||
|
|
||||||
|
model Orders {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
userId Int?
|
||||||
|
createdAt DateTime @default(now())
|
||||||
|
status String @default("PENDING") @db.VarChar(255)
|
||||||
|
balance Int? @default(0)
|
||||||
|
user User? @relation(fields: [userId], references: [id])
|
||||||
|
|
||||||
|
@@index([userId], map: "Orders_userId_fkey")
|
||||||
|
}
|
||||||
|
|
||||||
|
model Gifts {
|
||||||
|
id Int @id @default(autoincrement())
|
||||||
|
lvl Int
|
||||||
|
iconUrl String? @db.VarChar(255)
|
||||||
|
name String @db.VarChar(255)
|
||||||
|
type Gifts_type
|
||||||
|
UserGifts UserGifts[]
|
||||||
|
}
|
||||||
|
|
||||||
|
model UserGifts {
|
||||||
|
userId Int
|
||||||
|
giftId Int
|
||||||
|
amount Int? @default(0)
|
||||||
|
User User @relation(fields: [userId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "UserGifts_ibfk_1")
|
||||||
|
Gifts Gifts @relation(fields: [giftId], references: [id], onDelete: Cascade, onUpdate: NoAction, map: "UserGifts_ibfk_2")
|
||||||
|
|
||||||
|
@@id([userId, giftId])
|
||||||
|
@@index([giftId], map: "giftId")
|
||||||
|
}
|
||||||
|
|
||||||
|
enum USER_ROLE {
|
||||||
|
CLIENT
|
||||||
|
ADMINISTRATOR
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EStatusOfRefill {
|
||||||
|
SUCCESS
|
||||||
|
FALSE
|
||||||
|
DENIED
|
||||||
|
REFUND
|
||||||
|
IN_PROGRESS
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EStatusOfProductInInventory {
|
||||||
|
INVENTORY
|
||||||
|
ON_SERVER
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ETypeOfProduct {
|
||||||
|
GAME_ITEM
|
||||||
|
SERVICE
|
||||||
|
SETS_OF_PRODUCTS
|
||||||
|
HTTP_REQUEST
|
||||||
|
CURRENCY
|
||||||
|
CARDS
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ETypeOfURL {
|
||||||
|
SITE_SECTION
|
||||||
|
CUSTOM_PAGE
|
||||||
|
EXTERNAL_LINK
|
||||||
|
DROPDOWN_LIST
|
||||||
|
}
|
||||||
|
|
||||||
|
enum EButtonColor {
|
||||||
|
GREEN
|
||||||
|
BLUE
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Gifts_type {
|
||||||
|
item
|
||||||
|
product
|
||||||
|
service
|
||||||
|
privilege
|
||||||
|
set
|
||||||
|
}
|
||||||
187
src/api-rust/api-rust.controller.ts
Normal file
@ -0,0 +1,187 @@
|
|||||||
|
import { Controller, Get, Query, Res } from '@nestjs/common';
|
||||||
|
import { ApiRustService } from './api-rust.service';
|
||||||
|
import { ApiOkResponse, ApiOperation, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { ResponseApiDto } from './dto/response.dto';
|
||||||
|
import { Response } from 'express';
|
||||||
|
|
||||||
|
@Controller('api-rust')
|
||||||
|
@ApiTags('api-rust')
|
||||||
|
export class ApiRustController {
|
||||||
|
constructor(private readonly apiService: ApiRustService) {}
|
||||||
|
|
||||||
|
@Get('/products.get?')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Получения списка товаров, доступных на этом сервере',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
isArray: false,
|
||||||
|
status: 200,
|
||||||
|
type: ResponseApiDto,
|
||||||
|
})
|
||||||
|
async getProducts(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query('server') serverID: string,
|
||||||
|
@Query('token') token: string,
|
||||||
|
) {
|
||||||
|
const data = await this.apiService.getProducts(Number(serverID), token);
|
||||||
|
if (data.status == 'success') {
|
||||||
|
res.status(200).json(data);
|
||||||
|
} else {
|
||||||
|
res.status(400).json(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/product.give?')
|
||||||
|
@ApiOperation({
|
||||||
|
summary:
|
||||||
|
'Выдача товара в инвентарь пользователя. В истории покупок добавлять как бесплатную покупку.',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
isArray: false,
|
||||||
|
status: 200,
|
||||||
|
type: ResponseApiDto,
|
||||||
|
})
|
||||||
|
async productGive(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query('product') productId: string,
|
||||||
|
@Query('token') token: string,
|
||||||
|
@Query('steamid') steamId: string,
|
||||||
|
@Query('quanity') quanity: number,
|
||||||
|
) {
|
||||||
|
const data = await this.apiService.productGive(
|
||||||
|
token,
|
||||||
|
steamId,
|
||||||
|
productId,
|
||||||
|
Number(quanity),
|
||||||
|
);
|
||||||
|
if (data.status == 'success') {
|
||||||
|
res.status(200).json(data);
|
||||||
|
} else {
|
||||||
|
res.status(400).json(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/queue.auto?')
|
||||||
|
@ApiOperation({
|
||||||
|
summary:
|
||||||
|
'Получение списка товаров, у которых указана автоактивация на этом сервере',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
isArray: false,
|
||||||
|
status: 200,
|
||||||
|
type: ResponseApiDto,
|
||||||
|
})
|
||||||
|
async getQueueAuto(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query('server') serverID: string,
|
||||||
|
@Query('token') token: string,
|
||||||
|
) {
|
||||||
|
const data = await this.apiService.getQueueAuto(Number(serverID), token);
|
||||||
|
if (data.status == 'success') {
|
||||||
|
res.status(200).json(data);
|
||||||
|
} else {
|
||||||
|
res.status(400).json(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/queue.get?')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Получение списка товаров пользователя, доступные на этом сервере',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
isArray: false,
|
||||||
|
status: 200,
|
||||||
|
type: ResponseApiDto,
|
||||||
|
})
|
||||||
|
async queueGet(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query('server') serverID: string,
|
||||||
|
@Query('token') token: string,
|
||||||
|
@Query('steamid') steamId: string,
|
||||||
|
) {
|
||||||
|
const data = await this.apiService.queueGet(
|
||||||
|
Number(serverID),
|
||||||
|
token,
|
||||||
|
steamId,
|
||||||
|
);
|
||||||
|
if (data.status == 'success') {
|
||||||
|
res.status(200).json(data);
|
||||||
|
} else {
|
||||||
|
res.status(400).json(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/queue.give?')
|
||||||
|
@ApiOperation({ summary: 'Отметить товар выданным' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
isArray: false,
|
||||||
|
status: 200,
|
||||||
|
type: ResponseApiDto,
|
||||||
|
})
|
||||||
|
async queueGive(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query('server') serverID: string,
|
||||||
|
@Query('token') token: string,
|
||||||
|
@Query('steamid') steamId: string,
|
||||||
|
@Query('queueid') queueId: string,
|
||||||
|
) {
|
||||||
|
const data = await this.apiService.queueGive(
|
||||||
|
Number(serverID),
|
||||||
|
token,
|
||||||
|
steamId,
|
||||||
|
Number(queueId),
|
||||||
|
);
|
||||||
|
if (data.status == 'success') {
|
||||||
|
res.status(200).json(data);
|
||||||
|
} else {
|
||||||
|
res.status(400).json(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/users.get?')
|
||||||
|
@ApiOperation({ summary: 'Получение данных пользователя' })
|
||||||
|
@ApiOkResponse({
|
||||||
|
isArray: false,
|
||||||
|
status: 200,
|
||||||
|
type: ResponseApiDto,
|
||||||
|
})
|
||||||
|
async userGet(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query('token') token: string,
|
||||||
|
@Query('steamid') steamId: string,
|
||||||
|
) {
|
||||||
|
const data = await this.apiService.userGet(token, steamId);
|
||||||
|
if (data.status == 'success') {
|
||||||
|
res.status(200).json(data);
|
||||||
|
} else {
|
||||||
|
res.status(400).json(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/users.addBonus?')
|
||||||
|
@ApiOperation({
|
||||||
|
summary: 'Добавление пользователю бонусного баланса.',
|
||||||
|
})
|
||||||
|
@ApiOkResponse({
|
||||||
|
status: 200,
|
||||||
|
})
|
||||||
|
async userAddBonus(
|
||||||
|
@Res() res: Response,
|
||||||
|
@Query('server') server: string,
|
||||||
|
@Query('token') token: string,
|
||||||
|
@Query('steamid') steamId: string,
|
||||||
|
@Query('sum') sum: number,
|
||||||
|
) {
|
||||||
|
const data = await this.apiService.userAddBonus(
|
||||||
|
Number(server),
|
||||||
|
token,
|
||||||
|
steamId,
|
||||||
|
Number(sum),
|
||||||
|
);
|
||||||
|
if (data.status == 'success') {
|
||||||
|
res.status(200).json(data);
|
||||||
|
} else {
|
||||||
|
res.status(400).json(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/api-rust/api-rust.module.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ApiRustController } from './api-rust.controller';
|
||||||
|
import { ApiRustService } from './api-rust.service';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [ApiRustController],
|
||||||
|
providers: [ApiRustService, PrismaService],
|
||||||
|
})
|
||||||
|
export class ApiRustModule {}
|
||||||
475
src/api-rust/api-rust.service.ts
Normal file
@ -0,0 +1,475 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ApiRustService {
|
||||||
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getProducts(serverID: number, token: string) {
|
||||||
|
try {
|
||||||
|
const serverCandidate = await this.prisma.server.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
serverID,
|
||||||
|
apiKey: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const productList = await this.prisma.product.findMany({
|
||||||
|
where: {
|
||||||
|
hidden: false,
|
||||||
|
serverTypeId: serverCandidate.serverTypeId,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
type: true,
|
||||||
|
name_ru: true,
|
||||||
|
image: true,
|
||||||
|
productContent: true,
|
||||||
|
nameID: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const resultData = productList.map((el) => {
|
||||||
|
const type = getNumberByType(el.type);
|
||||||
|
switch (el.type) {
|
||||||
|
case 'GAME_ITEM':
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
type,
|
||||||
|
NameID: el.nameID,
|
||||||
|
name: el.name_ru,
|
||||||
|
image: el.image,
|
||||||
|
};
|
||||||
|
case 'SERVICE':
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
type,
|
||||||
|
NameID: el.nameID,
|
||||||
|
name: el.name_ru,
|
||||||
|
image: el.image,
|
||||||
|
};
|
||||||
|
case 'SETS_OF_PRODUCTS':
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
type,
|
||||||
|
NameID: el.nameID,
|
||||||
|
name: el.name_ru,
|
||||||
|
image: el.image,
|
||||||
|
data: el.productContent,
|
||||||
|
};
|
||||||
|
case 'CURRENCY':
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
type,
|
||||||
|
NameID: el.nameID,
|
||||||
|
name: el.name_ru,
|
||||||
|
image: el.image,
|
||||||
|
};
|
||||||
|
case 'HTTP_REQUEST':
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
type,
|
||||||
|
NameID: el.nameID,
|
||||||
|
name: el.name_ru,
|
||||||
|
image: el.image,
|
||||||
|
data: el.productContent,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
type,
|
||||||
|
NameID: el.nameID,
|
||||||
|
name: el.name_ru,
|
||||||
|
image: el.image,
|
||||||
|
data: el.nameID,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: resultData,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getQueueAuto(serverID: number, token: string) {
|
||||||
|
try {
|
||||||
|
const serverCandidate = await this.prisma.server.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
serverID,
|
||||||
|
apiKey: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const autoActivationItems = await this.prisma.inventory.findMany({
|
||||||
|
where: {
|
||||||
|
serverId: serverCandidate.id,
|
||||||
|
status: 'INVENTORY',
|
||||||
|
product: {
|
||||||
|
autoactivation: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
amount: true,
|
||||||
|
user: {
|
||||||
|
select: {
|
||||||
|
steamID: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
nameID: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await this.prisma.$transaction(async (tx) => {
|
||||||
|
await Promise.all(
|
||||||
|
autoActivationItems.map(async (el) => {
|
||||||
|
await tx.inventory.update({
|
||||||
|
where: {
|
||||||
|
id: el.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: 'ON_SERVER',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const resultData = autoActivationItems.map((el) => {
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
quantity: el.amount,
|
||||||
|
steamid: el.user.steamID,
|
||||||
|
productID: el.product.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: resultData,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async queueGet(serverID: number, token: string, steamid: string) {
|
||||||
|
try {
|
||||||
|
const serverCandidate = await this.prisma.server.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
serverID,
|
||||||
|
apiKey: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await this.prisma.user.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
steamID: steamid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const productList = await this.prisma.inventory.findMany({
|
||||||
|
where: {
|
||||||
|
status: 'INVENTORY',
|
||||||
|
OR: [
|
||||||
|
{
|
||||||
|
serverId: serverCandidate.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
serverId: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
serverTypeId: serverCandidate.serverTypeId,
|
||||||
|
userId: user.id,
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
amount: true,
|
||||||
|
product: {
|
||||||
|
select: {
|
||||||
|
id: true,
|
||||||
|
nameID: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const resultData = productList.map((el) => {
|
||||||
|
return {
|
||||||
|
id: el.id,
|
||||||
|
quantity: el.amount,
|
||||||
|
productID: el.product.id,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: resultData,
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async queueGive(
|
||||||
|
serverID: number,
|
||||||
|
token: string,
|
||||||
|
steamid: string,
|
||||||
|
queueid: number,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const serverCandidate = await this.prisma.server.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
serverID,
|
||||||
|
apiKey: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await this.prisma.user.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
steamID: steamid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.prisma.$transaction(async (tx) => {
|
||||||
|
const item = await tx.inventory.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
id: queueid,
|
||||||
|
userId: user.id,
|
||||||
|
status: 'INVENTORY',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (item.serverId != null && item.serverId != serverCandidate.id) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Предмет активирован на другом сервере',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (item.isPartOfPack) {
|
||||||
|
const partsOfPacks = await tx.inventory.findMany({
|
||||||
|
where: {
|
||||||
|
packId: item.packId,
|
||||||
|
historyOfPurchaseId: item.historyOfPurchaseId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
for (let i = 0; i < partsOfPacks.length; i++) {
|
||||||
|
await tx.inventory.update({
|
||||||
|
where: {
|
||||||
|
id: partsOfPacks[i].id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
isCanBeRefund: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await tx.inventory.update({
|
||||||
|
where: {
|
||||||
|
id: item.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: 'ON_SERVER',
|
||||||
|
isCanBeRefund: false,
|
||||||
|
serverId: serverCandidate.id,
|
||||||
|
serverName: serverCandidate.name,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async userGet(token: string, steamid: string) {
|
||||||
|
try {
|
||||||
|
const isValidToken = await this.prisma.baseSettings.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
apiKey: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await this.prisma.user.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
steamID: steamid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
data: {
|
||||||
|
name: user.steamName,
|
||||||
|
avatar: user.steamAvatar,
|
||||||
|
balance: user.mainBalance,
|
||||||
|
bonus: user.bonusBalance,
|
||||||
|
level: user.lvl,
|
||||||
|
xp: user.experience,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async productGive(
|
||||||
|
token: string,
|
||||||
|
steamid: string,
|
||||||
|
productId: string,
|
||||||
|
quanity: number,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
{
|
||||||
|
/*const isValidToken = await this.prisma.baseSettings.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
apiKey: token,
|
||||||
|
},
|
||||||
|
});*/
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await this.prisma.user.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
steamID: steamid,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const itemCandidate = await this.prisma.product.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
OR: [
|
||||||
|
{ nameID: productId }, // Поиск по nameID
|
||||||
|
{ id: Number(productId) }, // Если nameID null, ищем по id
|
||||||
|
],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.prisma.$transaction(async (tx) => {
|
||||||
|
const newPurchase = await tx.purchase.create({
|
||||||
|
data: {
|
||||||
|
amount: quanity,
|
||||||
|
lostBonusBalance: 0,
|
||||||
|
lostMainBalance: 0,
|
||||||
|
refund: false,
|
||||||
|
productId: itemCandidate.id,
|
||||||
|
userId: user.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const newItem = await tx.inventory.create({
|
||||||
|
data: {
|
||||||
|
amount: quanity,
|
||||||
|
productId: itemCandidate.id,
|
||||||
|
userId: user.id,
|
||||||
|
historyOfPurchaseId: newPurchase.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async userAddBonus(
|
||||||
|
serverID: number,
|
||||||
|
token: string,
|
||||||
|
steamId: string,
|
||||||
|
sum: number,
|
||||||
|
) {
|
||||||
|
try {
|
||||||
|
const serverCandidate = await this.prisma.server.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
serverID,
|
||||||
|
apiKey: token,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const user = await this.prisma.user.findFirstOrThrow({
|
||||||
|
where: {
|
||||||
|
steamID: steamId,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const newData = await this.prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: user.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
bonusBalance: user.bonusBalance + sum,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'success',
|
||||||
|
};
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
return {
|
||||||
|
status: 'error',
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async promoCreate(
|
||||||
|
token: string,
|
||||||
|
promoName: string,
|
||||||
|
group: string,
|
||||||
|
dateFrom: string,
|
||||||
|
dateTo: string,
|
||||||
|
pd: boolean,
|
||||||
|
sum?: number,
|
||||||
|
discount?: number,
|
||||||
|
productId?: number,
|
||||||
|
) {}
|
||||||
|
}
|
||||||
|
function getNumberByType(type: string) {
|
||||||
|
switch (type) {
|
||||||
|
case 'GAME_ITEM':
|
||||||
|
return 1;
|
||||||
|
case 'SERVICE':
|
||||||
|
return 2;
|
||||||
|
case 'SETS_OF_PRODUCTS':
|
||||||
|
return 3;
|
||||||
|
case 'HTTP_REQUEST':
|
||||||
|
return 4;
|
||||||
|
case 'CURRENCY':
|
||||||
|
return 5;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/api-rust/dto/response.dto.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class ResponseApiDto {
|
||||||
|
@ApiProperty({ description: 'Success | Error' })
|
||||||
|
status: 'Success' | 'Error';
|
||||||
|
@ApiProperty()
|
||||||
|
data: any;
|
||||||
|
@ApiProperty({ description: 'Описание ошибки' })
|
||||||
|
message: 'error.message';
|
||||||
|
}
|
||||||
55
src/app.module.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common';
|
||||||
|
|
||||||
|
import { PrismaModule } from './prisma/prisma.module';
|
||||||
|
import { UsersModule } from './users/users.module';
|
||||||
|
import { AuthModule } from './auth/auth.module';
|
||||||
|
import { PassportModule } from '@nestjs/passport';
|
||||||
|
import { TokenModule } from './token/token.module';
|
||||||
|
import { ProfileModule } from './profile/profile.module';
|
||||||
|
import { FileModule } from './file/file.module';
|
||||||
|
import { StoreModule } from './store/store.module';
|
||||||
|
import { CustompageModule } from './custompage/custompage.module';
|
||||||
|
import { ServersModule } from './servers/servers.module';
|
||||||
|
import { UserAgentMiddleware } from './auth/middleware/req.middleware';
|
||||||
|
import { ApiRustModule } from './api-rust/api-rust.module';
|
||||||
|
import { NotificationModule } from './notification/notification.module';
|
||||||
|
import { StatiscticModule } from './statisctic/statisctic.module';
|
||||||
|
import { ContactsModule } from './contacts/contacts.module';
|
||||||
|
import { VisitorMiddleware } from './visitor-middleware/visitor.middleware';
|
||||||
|
import { LanguageMiddleware } from './store/middlewares/language.middleware';
|
||||||
|
import { ScheduleModule } from '@nestjs/schedule';
|
||||||
|
import { PaymentModule } from './payment/payment.module';
|
||||||
|
import { OrdersModule } from './orders/orders.module';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
PrismaModule,
|
||||||
|
UsersModule,
|
||||||
|
AuthModule,
|
||||||
|
PassportModule.register({ defaultStrategy: 'steam' }),
|
||||||
|
TokenModule,
|
||||||
|
ProfileModule,
|
||||||
|
FileModule,
|
||||||
|
StoreModule,
|
||||||
|
CustompageModule,
|
||||||
|
ServersModule,
|
||||||
|
ApiRustModule,
|
||||||
|
NotificationModule,
|
||||||
|
StatiscticModule,
|
||||||
|
ContactsModule,
|
||||||
|
ScheduleModule.forRoot(),
|
||||||
|
PaymentModule,
|
||||||
|
OrdersModule,
|
||||||
|
],
|
||||||
|
controllers: [],
|
||||||
|
providers: [],
|
||||||
|
})
|
||||||
|
export class AppModule implements NestModule {
|
||||||
|
configure(consumer: MiddlewareConsumer) {
|
||||||
|
consumer
|
||||||
|
.apply(VisitorMiddleware)
|
||||||
|
.forRoutes('*')
|
||||||
|
.apply(LanguageMiddleware)
|
||||||
|
.forRoutes('*');
|
||||||
|
}
|
||||||
|
}
|
||||||
144
src/auth/auth.controller.ts
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
Get,
|
||||||
|
UseGuards,
|
||||||
|
Req,
|
||||||
|
Res,
|
||||||
|
Param,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Headers,
|
||||||
|
Query,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { ApiBearerAuth, ApiSecurity, ApiTags } from '@nestjs/swagger';
|
||||||
|
import { HttpService } from '@nestjs/axios';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
import { Request, Response } from 'express';
|
||||||
|
import {
|
||||||
|
httpOnlyRequest,
|
||||||
|
sameSiteRequest,
|
||||||
|
secureRequst,
|
||||||
|
} from 'src/core/config';
|
||||||
|
|
||||||
|
@ApiTags('auth')
|
||||||
|
@Controller('auth')
|
||||||
|
export class AuthController {
|
||||||
|
constructor(
|
||||||
|
private readonly authService: AuthService,
|
||||||
|
private readonly httpService: HttpService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Get('steam')
|
||||||
|
@UseGuards(AuthGuard('steam'))
|
||||||
|
async redirectToSteamAuth(): Promise<void> {}
|
||||||
|
|
||||||
|
@Get('/refresh')
|
||||||
|
async refresh(@Req() req: Request, @Res() res: Response) {
|
||||||
|
const data = await this.authService.refresh(req.cookies.refreshToken, {
|
||||||
|
clientIp: req.headers['x-real-ip'].toString(),
|
||||||
|
browser: req.browser,
|
||||||
|
deviceName: req.deviceName,
|
||||||
|
deviceType: req.deviceType,
|
||||||
|
os: req.os,
|
||||||
|
});
|
||||||
|
|
||||||
|
return res
|
||||||
|
.cookie('refreshToken', data.refreshToken, {
|
||||||
|
maxAge: 30 * 24 * 60 * 1000,
|
||||||
|
secure: secureRequst,
|
||||||
|
sameSite: sameSiteRequest,
|
||||||
|
httpOnly: httpOnlyRequest,
|
||||||
|
})
|
||||||
|
.json({
|
||||||
|
accessToken: data.accessToken,
|
||||||
|
user: data.user,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@UseGuards(AuthGuard('jwt'))
|
||||||
|
@Get('/logout')
|
||||||
|
async logout(@Res() res: Response, @Req() req: Request) {
|
||||||
|
const data = await this.authService.logout(req.cookies.refreshToken);
|
||||||
|
|
||||||
|
return res
|
||||||
|
.cookie('refreshToken', '', {
|
||||||
|
maxAge: 30 * 24 * 60 * 1000,
|
||||||
|
secure: secureRequst,
|
||||||
|
sameSite: sameSiteRequest,
|
||||||
|
httpOnly: httpOnlyRequest,
|
||||||
|
})
|
||||||
|
.json(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/openId/:id')
|
||||||
|
async getUserData(
|
||||||
|
@Param('id') id: string,
|
||||||
|
@Res() res: Response,
|
||||||
|
@Req() req: Request,
|
||||||
|
@Headers('signature') signature,
|
||||||
|
) {
|
||||||
|
// Логирование параметра id
|
||||||
|
console.log('Получен id:', id);
|
||||||
|
|
||||||
|
if (!id) {
|
||||||
|
console.error('Ошибка: id не предоставлен');
|
||||||
|
throw new HttpException('id is not provided', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Логирование подписи (signature)
|
||||||
|
console.log('Получена подпись (signature):', signature);
|
||||||
|
|
||||||
|
if (signature) {
|
||||||
|
try {
|
||||||
|
await this.authService.verifySignature(signature);
|
||||||
|
console.log('Подпись успешно проверена');
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка проверки подписи:', error);
|
||||||
|
throw new HttpException('Signature verification failed', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Логирование переданных данных для signUpIn
|
||||||
|
console.log('Параметры для signUpIn:', {
|
||||||
|
clientIp: req.headers['x-real-ip'] ? req.headers['x-real-ip'].toString() : 'IP не найден',
|
||||||
|
browser: req.browser,
|
||||||
|
deviceName: req.deviceName,
|
||||||
|
deviceType: req.deviceType,
|
||||||
|
os: req.os,
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await this.authService.signUpIn(id, {
|
||||||
|
clientIp: req.headers['x-real-ip'] ? req.headers['x-real-ip'].toString() : 'IP не найден',
|
||||||
|
browser: req.browser,
|
||||||
|
deviceName: req.deviceName,
|
||||||
|
deviceType: req.deviceType,
|
||||||
|
os: req.os,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Логирование данных, возвращаемых из signUpIn
|
||||||
|
console.log('Данные, полученные из signUpIn:', data);
|
||||||
|
|
||||||
|
return res
|
||||||
|
.cookie('refreshToken', data.refreshToken, {
|
||||||
|
maxAge: 30 * 24 * 60 * 1000,
|
||||||
|
secure: secureRequst,
|
||||||
|
sameSite: sameSiteRequest,
|
||||||
|
httpOnly: httpOnlyRequest,
|
||||||
|
})
|
||||||
|
.json(data);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в процессе регистрации/входа пользователя:', error);
|
||||||
|
throw new HttpException('User sign up/in failed', HttpStatus.INTERNAL_SERVER_ERROR);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/validateAdmin')
|
||||||
|
validateAdmin(@Headers('Authorization') authorization) {
|
||||||
|
const token = authorization.split(' ')[1];
|
||||||
|
return this.authService.validateAdmin(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
50
src/auth/auth.module.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
|
import { UsersModule } from '../users/users.module';
|
||||||
|
import { PassportModule } from '@nestjs/passport';
|
||||||
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
import { AuthController } from './auth.controller';
|
||||||
|
import { UsersService } from '../users/users.service';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { HttpModule } from '@nestjs/axios';
|
||||||
|
import { SteamStrategy } from './strategy/auth.steam.strategy';
|
||||||
|
import { TokenModule } from 'src/token/token.module';
|
||||||
|
import { JwtStrategy } from './strategy/jwt.strategy';
|
||||||
|
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
ConfigModule.forRoot({
|
||||||
|
isGlobal: true, // Это позволяет сделать конфигурацию глобальной для всего приложения
|
||||||
|
}),
|
||||||
|
HttpModule,
|
||||||
|
UsersModule,
|
||||||
|
TokenModule,
|
||||||
|
PassportModule.register({
|
||||||
|
defaultStrategy: 'jwt',
|
||||||
|
property: 'user',
|
||||||
|
session: false,
|
||||||
|
}),
|
||||||
|
PassportModule.register({ defaultStrategy: 'steam' }),
|
||||||
|
JwtModule.registerAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
inject: [ConfigService],
|
||||||
|
useFactory: async (configService: ConfigService) => ({
|
||||||
|
secret: configService.get<string>('SECRET_KEY'),
|
||||||
|
signOptions: {
|
||||||
|
expiresIn: configService.get<string>('EXPIRESIN'),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
controllers: [AuthController],
|
||||||
|
providers: [
|
||||||
|
AuthService,
|
||||||
|
UsersService,
|
||||||
|
JwtStrategy,
|
||||||
|
PrismaService,
|
||||||
|
SteamStrategy,
|
||||||
|
],
|
||||||
|
exports: [PassportModule, JwtModule],
|
||||||
|
})
|
||||||
|
export class AuthModule {}
|
||||||
324
src/auth/auth.service.ts
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
import * as crypto from 'crypto';
|
||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { UsersService } from '../users/users.service';
|
||||||
|
import { PrismaService } from '../prisma/prisma.service';
|
||||||
|
import { User } from '@prisma/client';
|
||||||
|
import { HttpService } from '@nestjs/axios';
|
||||||
|
import { TokenService } from 'src/token/token.service';
|
||||||
|
import { ResponseUserDto } from './dto/responseUser.dto';
|
||||||
|
import { JwtPayload } from './dto/jwtPayload.dto';
|
||||||
|
import { UserAgentDto } from './dto/userAgent.dto';
|
||||||
|
import { SECRET_KEY, STEAM_API_KEY } from 'src/core/config';
|
||||||
|
import { ParsedUrlQuery } from 'querystring';
|
||||||
|
import { catchError, firstValueFrom } from 'rxjs';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AuthService {
|
||||||
|
constructor(
|
||||||
|
private readonly prisma: PrismaService,
|
||||||
|
private readonly userService: UsersService,
|
||||||
|
private readonly tokenService: TokenService,
|
||||||
|
private readonly httpService: HttpService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async validateSteamAccount(identifier: string): Promise<any> {
|
||||||
|
// Здесь вы должны реализовать логику проверки и сохранения пользователя
|
||||||
|
// на основе полученных данных от Steam
|
||||||
|
// Например, вы можете сохранить идентификатор Steam в базу данных
|
||||||
|
// и возвращать соответствующего пользователя
|
||||||
|
return {
|
||||||
|
steamId: identifier,
|
||||||
|
// Другие данные пользователя
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateUser(payload: JwtPayload): Promise<any> {
|
||||||
|
const user = await this.userService.findById(payload.id);
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException('INVALID_TOKEN', HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
|
||||||
|
async signUpIn(id: string, userAgent: UserAgentDto) {
|
||||||
|
try {
|
||||||
|
console.log(`Попытка входа/регистрации пользователя с Steam ID: ${id}`);
|
||||||
|
|
||||||
|
const candidate = await this.userService.findBySteamId(id);
|
||||||
|
|
||||||
|
if (!candidate) {
|
||||||
|
console.log('Пользователь не найден, создаем нового');
|
||||||
|
|
||||||
|
const user = await this.userService.create(id);
|
||||||
|
const tokens = this.tokenService.generateTokens({
|
||||||
|
id: user.id,
|
||||||
|
steamId: user.steamID,
|
||||||
|
role: user.role,
|
||||||
|
avatar: user.steamAvatar,
|
||||||
|
name: user.steamName,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Токены для нового пользователя созданы:', tokens);
|
||||||
|
|
||||||
|
await this.tokenService.saveToken({
|
||||||
|
userId: user.id,
|
||||||
|
token: tokens.refreshToken,
|
||||||
|
userAgent: {
|
||||||
|
browser: userAgent.browser,
|
||||||
|
clientIp: userAgent.clientIp,
|
||||||
|
deviceName: userAgent.deviceName,
|
||||||
|
deviceType: userAgent.deviceType,
|
||||||
|
os: userAgent.os,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Токен сохранён для нового пользователя:', user.id);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: tokens.accessToken,
|
||||||
|
refreshToken: tokens.refreshToken,
|
||||||
|
user: new ResponseUserDto(user),
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
console.log('Пользователь найден, обновляем его данные');
|
||||||
|
|
||||||
|
const userSteamData = await firstValueFrom(
|
||||||
|
this.httpService
|
||||||
|
.get(
|
||||||
|
`https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=${STEAM_API_KEY}&steamids=${id}`,
|
||||||
|
)
|
||||||
|
.pipe(
|
||||||
|
catchError((error: AxiosError) => {
|
||||||
|
console.error(error.response.data);
|
||||||
|
throw 'An error happened!';
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
const mainData = userSteamData.data.response.players[0];
|
||||||
|
console.log('Обновление данных пользователя с Steam API:', mainData);
|
||||||
|
|
||||||
|
const updUser = await this.prisma.user.update({
|
||||||
|
where: {
|
||||||
|
id: candidate.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
steamName: mainData.personaname,
|
||||||
|
steamAvatar: mainData.avatarfull,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const tokens = this.tokenService.generateTokens({
|
||||||
|
id: updUser.id,
|
||||||
|
steamId: updUser.steamID,
|
||||||
|
role: updUser.role,
|
||||||
|
avatar: updUser.steamAvatar,
|
||||||
|
name: updUser.steamName,
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log('Токены для обновленного пользователя созданы:', tokens);
|
||||||
|
|
||||||
|
await this.tokenService.saveToken({
|
||||||
|
userId: candidate.id,
|
||||||
|
token: tokens.refreshToken,
|
||||||
|
userAgent: {
|
||||||
|
browser: userAgent.browser,
|
||||||
|
clientIp: userAgent.clientIp,
|
||||||
|
deviceName: userAgent.deviceName,
|
||||||
|
deviceType: userAgent.deviceType,
|
||||||
|
os: userAgent.os,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
'Токен сохранён для обновленного пользователя:',
|
||||||
|
candidate.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: tokens.accessToken,
|
||||||
|
refreshToken: tokens.refreshToken,
|
||||||
|
user: new ResponseUserDto(updUser),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Ошибка в процессе входа/регистрации пользователя:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async refresh(token: string, userAgent: UserAgentDto) {
|
||||||
|
const userData = await this.tokenService.validateRefreshToken(
|
||||||
|
token,
|
||||||
|
userAgent,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!userData) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Пользователь не авторизован',
|
||||||
|
HttpStatus.UNAUTHORIZED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = await this.userService.findById(userData.id);
|
||||||
|
|
||||||
|
const tokens = this.tokenService.generateTokens({
|
||||||
|
id: user.id,
|
||||||
|
steamId: user.steamID,
|
||||||
|
role: user.role,
|
||||||
|
avatar: user.steamAvatar,
|
||||||
|
name: user.steamName,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.tokenService.saveToken({
|
||||||
|
userId: user.id,
|
||||||
|
token: tokens.refreshToken,
|
||||||
|
userAgent,
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: tokens.accessToken,
|
||||||
|
refreshToken: tokens.refreshToken,
|
||||||
|
user: new ResponseUserDto(user),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async logout(refreshToken: string) {
|
||||||
|
const token = await this.tokenService.deleteToken(refreshToken);
|
||||||
|
if (token) {
|
||||||
|
return { message: 'Вы успешно разлогинены' };
|
||||||
|
} else {
|
||||||
|
throw new HttpException('Токен не существует', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getByOpenId(id: string, userAgent: UserAgentDto) {
|
||||||
|
const candidate = await this.userService.findBySteamId(id);
|
||||||
|
|
||||||
|
const tokens = this.tokenService.generateTokens({
|
||||||
|
id: candidate.id,
|
||||||
|
steamId: candidate.steamID,
|
||||||
|
role: candidate.role,
|
||||||
|
avatar: candidate.steamAvatar,
|
||||||
|
name: candidate.steamName,
|
||||||
|
});
|
||||||
|
|
||||||
|
await this.tokenService.saveToken({
|
||||||
|
userId: candidate.id,
|
||||||
|
token: tokens.refreshToken,
|
||||||
|
userAgent: {
|
||||||
|
browser: userAgent.browser,
|
||||||
|
clientIp: userAgent.clientIp,
|
||||||
|
deviceName: userAgent.deviceName,
|
||||||
|
deviceType: userAgent.deviceType,
|
||||||
|
os: userAgent.os,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
accessToken: tokens.accessToken,
|
||||||
|
refreshToken: tokens.refreshToken,
|
||||||
|
user: new ResponseUserDto(candidate),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async validateAdmin(token: string) {
|
||||||
|
const data = this.tokenService.validateAdmin(token);
|
||||||
|
if (!data) {
|
||||||
|
throw new HttpException('JWT expired', HttpStatus.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
return data.role == 'ADMINISTRATOR' ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async verifySignature(searchOrParsedUrlQuery: string | ParsedUrlQuery) {
|
||||||
|
let sign: string | undefined;
|
||||||
|
const queryParams: IQueryParam[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Функция, которая обрабатывает входящий query-параметр. В случае передачи
|
||||||
|
* параметра, отвечающего за подпись, подменяет "sign". В случае встречи
|
||||||
|
* корректного в контексте подписи параметра добавляет его в массив
|
||||||
|
* известных параметров.
|
||||||
|
* @param key
|
||||||
|
* @param value
|
||||||
|
*/
|
||||||
|
const processQueryParam = (key: string, value: any) => {
|
||||||
|
if (typeof value === 'string') {
|
||||||
|
if (key.startsWith('openid.')) {
|
||||||
|
queryParams.push({ key, value });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (typeof searchOrParsedUrlQuery === 'string') {
|
||||||
|
// Если строка начинается с вопроса (когда передан window.location.search),
|
||||||
|
// его необходимо удалить.
|
||||||
|
const formattedSearch = searchOrParsedUrlQuery.startsWith('?')
|
||||||
|
? searchOrParsedUrlQuery.slice(1)
|
||||||
|
: searchOrParsedUrlQuery;
|
||||||
|
|
||||||
|
// Пытаемся спарсить строку как query-параметр.
|
||||||
|
for (const param of formattedSearch.split('&')) {
|
||||||
|
const [key, value] = param.split('=');
|
||||||
|
processQueryParam(key, value);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const key of Object.keys(searchOrParsedUrlQuery)) {
|
||||||
|
const value = searchOrParsedUrlQuery[key];
|
||||||
|
processQueryParam(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const index = Array.prototype.findIndex.call(
|
||||||
|
queryParams,
|
||||||
|
(x) => x.key == 'openid.mode',
|
||||||
|
);
|
||||||
|
queryParams[index].value = 'check_authentication';
|
||||||
|
|
||||||
|
if (queryParams.length === 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const queryString = queryParams.reduce<string>(
|
||||||
|
(acc, { key, value }, idx) => {
|
||||||
|
return (
|
||||||
|
acc + (idx === 0 ? '' : '&') + `${key}=${decodeURIComponent(value)}`
|
||||||
|
);
|
||||||
|
},
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
|
||||||
|
const userSteamData = await firstValueFrom(
|
||||||
|
this.httpService
|
||||||
|
.get(`https://steamcommunity.com/openid/login?${queryString}`)
|
||||||
|
.pipe(
|
||||||
|
catchError((error: AxiosError) => {
|
||||||
|
console.error(error.response.data);
|
||||||
|
throw 'An error happened!';
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const flag = userSteamData.data.split('\n')[1].split(':')[1];
|
||||||
|
if (flag) {
|
||||||
|
return 'success';
|
||||||
|
} else {
|
||||||
|
throw new HttpException('verification failed', HttpStatus.FORBIDDEN);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface RegistrationStatus {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data?: User;
|
||||||
|
}
|
||||||
|
export interface RegistrationSeederStatus {
|
||||||
|
success: boolean;
|
||||||
|
message: string;
|
||||||
|
data?: User[];
|
||||||
|
}
|
||||||
|
export interface IQueryParam {
|
||||||
|
key: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
7
src/auth/dto/jwtPayload.dto.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export class JwtPayload {
|
||||||
|
readonly id: number;
|
||||||
|
readonly steamId: string;
|
||||||
|
readonly role: string;
|
||||||
|
readonly name: string;
|
||||||
|
readonly avatar: string;
|
||||||
|
}
|
||||||
17
src/auth/dto/registerResponse.dto.ts
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { ResponseUserDto } from './responseUser.dto';
|
||||||
|
|
||||||
|
export class RegisterResponseDto {
|
||||||
|
readonly accessToken: string;
|
||||||
|
readonly refreshToken: string;
|
||||||
|
readonly user: ResponseUserDto;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
accessToken: string,
|
||||||
|
refreshToken: string,
|
||||||
|
user: ResponseUserDto,
|
||||||
|
) {
|
||||||
|
this.accessToken = accessToken;
|
||||||
|
this.refreshToken = refreshToken;
|
||||||
|
this.user = user;
|
||||||
|
}
|
||||||
|
}
|
||||||
19
src/auth/dto/responseUser.dto.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
export class ResponseUserDto {
|
||||||
|
readonly id: number;
|
||||||
|
readonly steamId: string;
|
||||||
|
readonly role: string;
|
||||||
|
readonly avatar: string;
|
||||||
|
readonly balance: number;
|
||||||
|
readonly name: string;
|
||||||
|
readonly lvl: number; // Добавляем уровень
|
||||||
|
|
||||||
|
constructor(model) {
|
||||||
|
this.id = model.id;
|
||||||
|
this.role = model.role;
|
||||||
|
this.steamId = model.steamID;
|
||||||
|
this.avatar = model.steamAvatar;
|
||||||
|
this.balance = model.mainBalance;
|
||||||
|
this.name = model.steamName;
|
||||||
|
this.lvl = model.lvl; // Присваиваем уровень из модели
|
||||||
|
}
|
||||||
|
}
|
||||||
7
src/auth/dto/userAgent.dto.ts
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
export class UserAgentDto {
|
||||||
|
readonly deviceType: string;
|
||||||
|
readonly deviceName: string;
|
||||||
|
readonly browser: string;
|
||||||
|
readonly clientIp: string;
|
||||||
|
readonly os: string;
|
||||||
|
}
|
||||||
53
src/auth/guards/auth.guard.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import {
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Injectable,
|
||||||
|
UnauthorizedException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import { ROLES_KEY } from './roles-guard.decorator';
|
||||||
|
import { SECRET_KEY } from 'src/core/config';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RolesGuard implements CanActivate {
|
||||||
|
constructor(private jwtService: JwtService, private reflector: Reflector) {}
|
||||||
|
|
||||||
|
canActivate(
|
||||||
|
context: ExecutionContext,
|
||||||
|
): boolean | Promise<boolean> | Observable<boolean> {
|
||||||
|
try {
|
||||||
|
const requiredRoles = this.reflector.getAllAndOverride<string[]>(
|
||||||
|
ROLES_KEY,
|
||||||
|
[context.getHandler(), context.getClass()],
|
||||||
|
);
|
||||||
|
if (!requiredRoles) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
const req = context.switchToHttp().getRequest();
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
const bearer = authHeader.split(' ')[0];
|
||||||
|
const token = authHeader.split(' ')[1];
|
||||||
|
|
||||||
|
if (bearer !== 'Bearer' || !token) {
|
||||||
|
throw new UnauthorizedException({
|
||||||
|
message: 'Пользователь не авторизован',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = this.jwtService.verify(token, {
|
||||||
|
secret: SECRET_KEY,
|
||||||
|
});
|
||||||
|
|
||||||
|
req.user = user;
|
||||||
|
return requiredRoles.includes(user.role);
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e);
|
||||||
|
|
||||||
|
throw new HttpException('Токен истек', HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
src/auth/guards/jwt-auth.guard.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import {
|
||||||
|
CanActivate,
|
||||||
|
ExecutionContext,
|
||||||
|
HttpException,
|
||||||
|
HttpStatus,
|
||||||
|
Injectable,
|
||||||
|
UnauthorizedException,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
import { SECRET_KEY } from 'src/core/config';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtAuthGuard implements CanActivate {
|
||||||
|
constructor(private jwtService: JwtService) {}
|
||||||
|
|
||||||
|
canActivate(
|
||||||
|
context: ExecutionContext,
|
||||||
|
): boolean | Promise<boolean> | Observable<boolean> {
|
||||||
|
const req = context.switchToHttp().getRequest();
|
||||||
|
try {
|
||||||
|
const authHeader = req.headers.authorization;
|
||||||
|
const bearer = authHeader.split(' ')[0];
|
||||||
|
const token = authHeader.split(' ')[1];
|
||||||
|
|
||||||
|
if (bearer !== 'Bearer' || !token) {
|
||||||
|
throw new UnauthorizedException({
|
||||||
|
message: 'Пользователь не авторизован',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const user = this.jwtService.verify(token, { secret: SECRET_KEY });
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException('Токен истек', HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
req.user = user;
|
||||||
|
return true;
|
||||||
|
} catch (e) {
|
||||||
|
if (req.headers.language == 'ru') {
|
||||||
|
throw new HttpException(
|
||||||
|
'Пользователь не авторизован',
|
||||||
|
HttpStatus.UNAUTHORIZED,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw new HttpException(
|
||||||
|
'You are not logged in',
|
||||||
|
HttpStatus.UNAUTHORIZED,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
5
src/auth/guards/roles-guard.decorator.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
|
||||||
|
export const ROLES_KEY = 'roles';
|
||||||
|
|
||||||
|
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);
|
||||||
57
src/auth/middleware/req.middleware.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
import * as UAParser from 'ua-parser-js';
|
||||||
|
|
||||||
|
declare global {
|
||||||
|
namespace Express {
|
||||||
|
export interface Request {
|
||||||
|
deviceType?: string;
|
||||||
|
deviceName?: string;
|
||||||
|
browser?: string;
|
||||||
|
clientIp?: string;
|
||||||
|
os?: string;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class UserAgentMiddleware implements NestMiddleware {
|
||||||
|
use(req: Request, res: Response, next: NextFunction) {
|
||||||
|
const parser = new UAParser();
|
||||||
|
parser.setUA(req.headers['user-agent']);
|
||||||
|
|
||||||
|
const device = parser.getDevice();
|
||||||
|
|
||||||
|
if (device.model) {
|
||||||
|
if (device.type == 'Macintosh') {
|
||||||
|
req.deviceType = 'Macbook';
|
||||||
|
req.deviceName = device.model;
|
||||||
|
} else {
|
||||||
|
req.deviceType = device.type;
|
||||||
|
req.deviceName = device.model;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.deviceType = 'pc';
|
||||||
|
req.deviceName = 'pc';
|
||||||
|
}
|
||||||
|
|
||||||
|
const os = parser.getOS();
|
||||||
|
if (!os.name && !os.version) {
|
||||||
|
req.os = 'default windows';
|
||||||
|
} else {
|
||||||
|
req.os = os.name + ' ' + os.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parser.getBrowser().name) {
|
||||||
|
req.browser = parser.getBrowser().name;
|
||||||
|
} else {
|
||||||
|
req.browser = 'postman';
|
||||||
|
}
|
||||||
|
|
||||||
|
const clientIp = req.headers['x-real-ip'];
|
||||||
|
|
||||||
|
req.clientIp = clientIp.toString();
|
||||||
|
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
}
|
||||||
24
src/auth/strategy/auth.steam.strategy.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { Strategy } from 'passport-steam';
|
||||||
|
import { AuthService } from '../auth.service';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class SteamStrategy extends PassportStrategy(Strategy, 'steam') {
|
||||||
|
constructor(
|
||||||
|
private readonly authService: AuthService,
|
||||||
|
private readonly configService: ConfigService, //
|
||||||
|
) {
|
||||||
|
super({
|
||||||
|
returnURL: "https://magicrust.gg/store",
|
||||||
|
realm: "https://magicrust.gg",
|
||||||
|
apiKey: configService.get<string>('STEAM_API_KEY'),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(identifier: string, profile: any): Promise<any> {
|
||||||
|
const user = await this.authService.validateSteamAccount(identifier);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
25
src/auth/strategy/jwt.strategy.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
|
import { ExtractJwt, Strategy } from 'passport-jwt';
|
||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { AuthService } from '../auth.service';
|
||||||
|
import { JwtPayload } from '../dto/jwtPayload.dto';
|
||||||
|
import { SECRET_KEY } from 'src/core/config';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||||
|
constructor(private readonly authService: AuthService) {
|
||||||
|
super({
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
ignoreExpiration: true,
|
||||||
|
secretOrKey: SECRET_KEY,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(payload: JwtPayload): Promise<any> {
|
||||||
|
const user = await this.authService.validateUser(payload);
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException('Invalid token', HttpStatus.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/contacts/contacts.controller.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
import { ContactsService } from './contacts.service';
|
||||||
|
|
||||||
|
@Controller('contacts')
|
||||||
|
export class ContactsController {
|
||||||
|
constructor(private readonly contactService: ContactsService) {}
|
||||||
|
|
||||||
|
@Get()
|
||||||
|
getContacts() {
|
||||||
|
return this.contactService.getAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
10
src/contacts/contacts.module.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ContactsService } from './contacts.service';
|
||||||
|
import { ContactsController } from './contacts.controller';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [ContactsService, PrismaService],
|
||||||
|
controllers: [ContactsController],
|
||||||
|
})
|
||||||
|
export class ContactsModule {}
|
||||||
11
src/contacts/contacts.service.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ContactsService {
|
||||||
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async getAll() {
|
||||||
|
return this.prisma.contacts.findMany();
|
||||||
|
}
|
||||||
|
}
|
||||||
51
src/core/config/index.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
export const SALT_ROUNDS = process.env.SALT_ROUNDS || 10;
|
||||||
|
export const PORT = process.env.PORT || 4500;
|
||||||
|
|
||||||
|
export const STEAM_API_KEY =
|
||||||
|
process.env.STEAM_API_KEY || 'B9598DCBD84F402096CB3D411AE02396';
|
||||||
|
export const BASE_IP = process.env.BASE_IP || `http://localhost`;
|
||||||
|
export const BASE_STEAM_API_URL =
|
||||||
|
process.env.BASE_STEAM_API_URL || 'https://api.steampowered.com/';
|
||||||
|
export const BASE_RETURN_URL =
|
||||||
|
`https://magicrust.gg/store`;
|
||||||
|
export const BASE_REALM = `https://magicrust.gg/store`;
|
||||||
|
export const SECRET_KEY = process.env.SECRET_KEY || 'SQGmGagfJt797J9p';
|
||||||
|
export const secureRequst = true;
|
||||||
|
export const httpOnlyRequest = true;
|
||||||
|
export const sameSiteRequest = 'lax';
|
||||||
|
|
||||||
|
export const expiresAccessToken = '7d';
|
||||||
|
|
||||||
|
export const PROJECT_KEY = 485;
|
||||||
|
export const MONEY_SECRET_KEY =
|
||||||
|
process.env.MONEY_SECRET_KEY || 'qD56qTW7DrVhhiQOvukciW35UodtvQwK';
|
||||||
|
export const RSA_PRIVATE_KEY =
|
||||||
|
process.env.RSA_PRIVATE_KEY ||
|
||||||
|
`-----BEGIN RSA PRIVATE KEY-----
|
||||||
|
MIIEogIBAAKCAQEAnNs2yiGDkxyaUQ37PDZgqPiZ3jES927nd37u9ElGH8qcL7U1
|
||||||
|
2Iriqnp7qpgPK+VBiF82DqNXW5RpcxAm5UekUn2swrmMhPwgqoLlPWpSainInqKe
|
||||||
|
+86aUIpkiEEsMI/0KnHjh29o/01Z56TkBgo/M808mSeKbMxMgBvN74GnVa7WrEwx
|
||||||
|
UPfcwp47uW77HcE4jEvAYOWhZvVio2SR8xtV6mHS2E/URUbqPcYCcx1ofXTcy7wn
|
||||||
|
2bOq7WYZvzWRcHnsrvWeFVyd56/VhNbqAjNcuf+BFgxQqsaSsjiUWXutFdwXMOas
|
||||||
|
UMv6N6+Tvcd+UQd3ShuxgXurpCm/Nt8PRHmvqwIDAQABAoIBACiRccw5esQzo53P
|
||||||
|
FeluzvUkxvb2jkc8gdUgGUFJKj/SsS6NmY2V7iXVY+KtTU83DgntRhfnGxLugY9m
|
||||||
|
4AhwzQkgw+vg/XzvkKtJC20k8IfQoriUqrXJq5OlF3c+E+XDdYz8w7IlmGxfXx9Q
|
||||||
|
QEFnqQ0GyeN6nIyjEi4YAt0tFVBNbkMkZkmJ1QbX4nw4MTXZz6tnSw8OTz4F4X0x
|
||||||
|
3HZJlO1/DMQ9Wctd8JOHZxHANMI5uNFUZM21rDo7NRtul32GHtQ+AAKDw8rXV88r
|
||||||
|
UdFqJyADjCC0orKtypkFlKEGTtCNS8s6RsehoMBuX1ck6Iz7daNvIUZF7z32djJ4
|
||||||
|
WF/ok0ECgYEAynjQK+ppSoSI8zogM05cgE0/vcFpJRBOrHJWwAmHwldsPW73r05X
|
||||||
|
hAM8I+S5Z63nngTwlHaRPi2qOZ/EWE+E7H7+nUNocY69berf8CrEiASnrlYjtp8r
|
||||||
|
66wqUi6xoaJjYAjp34g17lTKpNXkqwlbPkSfdD3p7aVTjNxA7pch75sCgYEAxlMp
|
||||||
|
O9VptfdyVE50H9TI87wWVXlgDpAlvMKouVovg6OSvnkMx1gF1/bQ4DM9B3Mc1MQe
|
||||||
|
ZmDFa4MOvete7y53GEGnQRd+F2e7HfNErq08IuFYAJlp/DJSSeJ//b2EqbCbmUYp
|
||||||
|
qA3IdI0Hy4LXcSvDBO6TwmxUbilIAyhJFWgBKTECgYBr/nyqr+FxSbfyY/KA6y8T
|
||||||
|
kCCLifoXNtd/y4zR6UNsOU0Wh3W9H8A2PbN6QalSz8NW2bOovIVD8P92tOz5XZC8
|
||||||
|
xA/yud2flE8drcW8SFODaVg2+OMbqVK35aC9LQK5/++Zbaew/uolMTzVJvFM+TSd
|
||||||
|
xeR3D/8SLridzW0k1Z/YOQKBgGO0o2JFZKPrBzuDRbZLD8wZn1DReI1Zwt8nLhQh
|
||||||
|
VwnjTn8b9GSzyBxPJavRlrkMEk6VWoM124q1lM++aTfuMEmtmByNZwL1T4k7KCh5
|
||||||
|
R2ZxzABhIHt+AQjMKnSytuNoupFQSNkINOMDlAuoeA+ZZK4yE28Hb1sCvgV4V2W/
|
||||||
|
p/XBAoGAQJ6TQ20QSpdZB3k2JVqnCfVupcnGwqiwtmPxIG8M+48wV0PDt9zCn1wV
|
||||||
|
emXXfT2EDUgb+6mvjRdyZe72+Orp5uhKOchcc9hR2Yi0Z0R0WSu0qSoY02N6xLPn
|
||||||
|
DWgA/sXKt/cxvdjSwR5p5HMaLD/FNFW/Wy6zyqGwkErjE0lR19M=
|
||||||
|
-----END RSA PRIVATE KEY-----`;
|
||||||
|
export const PRIVATE_KEY_PATH = 'private.pem';
|
||||||
11
src/core/constant/index.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
export const getOnlineURL = 'https://vk.magicrust.ru/api/getOnline';
|
||||||
|
export const getLeaderboardURL =
|
||||||
|
'https://stat.magic-rust.ru/api/getPublicData.php?server=';
|
||||||
|
export const getBanListURL = 'https://vk.magicrust.ru/api/getBans';
|
||||||
|
export const gmTerminal = 'https://paygate.gamemoney.com/terminal/create';
|
||||||
|
export const gmApiTerminal = 'https://paygate.gamemoney.com/invoice';
|
||||||
|
export const gmStatus = 'https://pay.pgs.limited/pay/invoice/status';
|
||||||
|
export const success_url = 'https://magicrust.gg/ru?payment=success';
|
||||||
|
export const fail_url = 'https://mrust.ru/profile';
|
||||||
|
export const gmRefund = 'https://paygate.gamemoney.com/checkout/insert';
|
||||||
|
|
||||||
44
src/custompage/custompage.controller.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
Get,
|
||||||
|
Headers,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
Put,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { CustompageService } from './custompage.service';
|
||||||
|
import { CreatePageDto } from './dto/createPage.dto';
|
||||||
|
import { ApiTags } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
@ApiTags('Custom pages')
|
||||||
|
@Controller('page')
|
||||||
|
export class CustompageController {
|
||||||
|
constructor(private readonly pageService: CustompageService) {}
|
||||||
|
|
||||||
|
@Post('/create')
|
||||||
|
createPage(@Body() dto: CreatePageDto) {
|
||||||
|
return this.pageService.createPage(dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/custom/:id')
|
||||||
|
getPageByUrl(@Param('id') id: string, @Headers('Language') lang) {
|
||||||
|
return this.pageService.getPageByUrl(Number(id), lang);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Get('/')
|
||||||
|
getAll() {
|
||||||
|
return this.pageService.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* @Put('/update')
|
||||||
|
updatePage(@Body() dto: CreatePageDto) {
|
||||||
|
return this.pageService.createPage(dto);
|
||||||
|
} */
|
||||||
|
|
||||||
|
/* @Delete('/delete/:url')
|
||||||
|
deletePage(@Param('url') url: string) {
|
||||||
|
return this.pageService.deletePage(url);
|
||||||
|
} */
|
||||||
|
}
|
||||||
10
src/custompage/custompage.module.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { CustompageController } from './custompage.controller';
|
||||||
|
import { CustompageService } from './custompage.service';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [CustompageController],
|
||||||
|
providers: [CustompageService, PrismaService],
|
||||||
|
})
|
||||||
|
export class CustompageModule {}
|
||||||
123
src/custompage/custompage.service.ts
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
import { CreatePageDto } from './dto/createPage.dto';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class CustompageService {
|
||||||
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
async createPage(dto: CreatePageDto) {
|
||||||
|
const candidate = await this.prisma.urlSettings.findFirst({
|
||||||
|
where: {
|
||||||
|
url: dto.url,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (candidate) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Страница с данным URL уже существует',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const newPage = await this.prisma.urlSettings.create({
|
||||||
|
data: {
|
||||||
|
icon: dto.mainIcon,
|
||||||
|
sections: JSON.parse(JSON.stringify(dto.items)),
|
||||||
|
isHaveSidebar: dto.isHaveSidebar,
|
||||||
|
url: dto.url,
|
||||||
|
text: dto.mainTitle,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return newPage;
|
||||||
|
}
|
||||||
|
|
||||||
|
async getPageByUrl(id: number, lang: string) {
|
||||||
|
const page = await this.prisma.urlSettings.findFirst({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (page.hidden) {
|
||||||
|
throw new HttpException('Страница недоступна', HttpStatus.FORBIDDEN);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lang == 'ru') {
|
||||||
|
const ruContent = JSON.parse(JSON.stringify(page.sections));
|
||||||
|
return { ...page, sections: ruContent.ru };
|
||||||
|
} else if (lang == 'en') {
|
||||||
|
const enContent = JSON.parse(JSON.stringify(page.sections));
|
||||||
|
return { ...page, sections: enContent.en };
|
||||||
|
}
|
||||||
|
return page;
|
||||||
|
}
|
||||||
|
|
||||||
|
async updPage(dto: CreatePageDto) {
|
||||||
|
const candidate = await this.prisma.urlSettings.findFirst({
|
||||||
|
where: {
|
||||||
|
url: dto.url,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!candidate) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Страница с данным URL не существует',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const updPage = await this.prisma.urlSettings.update({
|
||||||
|
where: {
|
||||||
|
id: candidate.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
icon: dto.mainIcon,
|
||||||
|
sections: JSON.parse(JSON.stringify(dto.items)),
|
||||||
|
isHaveSidebar: dto.isHaveSidebar,
|
||||||
|
url: dto.url,
|
||||||
|
text: dto.mainTitle,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async deletePage(url: string) {
|
||||||
|
const candidate = await this.prisma.urlSettings.findFirst({
|
||||||
|
where: {
|
||||||
|
url: url,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!candidate) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Страница с данным URL не существует',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
await this.prisma.urlSettings.delete({
|
||||||
|
where: {
|
||||||
|
id: candidate.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return 'Страница удалена';
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAll() {
|
||||||
|
return this.prisma.urlSettings.findMany({
|
||||||
|
where: {
|
||||||
|
hidden: false,
|
||||||
|
typeUrl: 'CUSTOM_PAGE',
|
||||||
|
},
|
||||||
|
select: {
|
||||||
|
icon: true,
|
||||||
|
id: true,
|
||||||
|
isHaveSidebar: true,
|
||||||
|
url: true,
|
||||||
|
typeUrl: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
23
src/custompage/dto/createPage.dto.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { ApiProperty } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
export class CreatePageDto {
|
||||||
|
@ApiProperty()
|
||||||
|
readonly url: string;
|
||||||
|
@ApiProperty()
|
||||||
|
readonly mainTitle: string;
|
||||||
|
@ApiProperty()
|
||||||
|
readonly isHaveSidebar: boolean;
|
||||||
|
@ApiProperty()
|
||||||
|
readonly items: Item[];
|
||||||
|
@ApiProperty()
|
||||||
|
readonly mainIcon: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Item {
|
||||||
|
@ApiProperty()
|
||||||
|
readonly title: string;
|
||||||
|
@ApiProperty()
|
||||||
|
readonly icon: string;
|
||||||
|
@ApiProperty()
|
||||||
|
readonly html: string;
|
||||||
|
}
|
||||||
36
src/file/file.controller.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import {
|
||||||
|
Controller,
|
||||||
|
Post,
|
||||||
|
UploadedFile,
|
||||||
|
UseInterceptors,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { FileInterceptor } from '@nestjs/platform-express';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
|
||||||
|
@Controller('file')
|
||||||
|
export class FileController {
|
||||||
|
constructor(private readonly prisma: PrismaService) {}
|
||||||
|
|
||||||
|
@Post()
|
||||||
|
@UseInterceptors(FileInterceptor('file'))
|
||||||
|
async uploadJSON(@UploadedFile() file: Express.Multer.File) {
|
||||||
|
try {
|
||||||
|
const arrayOfItems = JSON.parse(file.buffer.toString());
|
||||||
|
for (let i = 0; i < arrayOfItems.length; i++) {
|
||||||
|
await this.prisma.product.create({
|
||||||
|
data: {
|
||||||
|
image: arrayOfItems[i].image,
|
||||||
|
name_ru: arrayOfItems[i].market_name,
|
||||||
|
price: Math.round(arrayOfItems[i].priceByCurrency.RUB.safe),
|
||||||
|
nameID: arrayOfItems[i].nameID,
|
||||||
|
name_en: '',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return 'Products were created';
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
9
src/file/file.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { FileController } from './file.controller';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [FileController],
|
||||||
|
providers: [PrismaService],
|
||||||
|
})
|
||||||
|
export class FileModule {}
|
||||||
BIN
src/icons/lvl1.png
Normal file
|
After Width: | Height: | Size: 111 KiB |
BIN
src/icons/lvl10.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
src/icons/lvl11.png
Normal file
|
After Width: | Height: | Size: 57 KiB |
BIN
src/icons/lvl12.png
Normal file
|
After Width: | Height: | Size: 29 KiB |
BIN
src/icons/lvl13.png
Normal file
|
After Width: | Height: | Size: 32 KiB |
BIN
src/icons/lvl14.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
src/icons/lvl16.png
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
src/icons/lvl17.png
Normal file
|
After Width: | Height: | Size: 79 KiB |
BIN
src/icons/lvl18.png
Normal file
|
After Width: | Height: | Size: 39 KiB |
BIN
src/icons/lvl19.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
src/icons/lvl2.png
Normal file
|
After Width: | Height: | Size: 105 KiB |
BIN
src/icons/lvl20.png
Normal file
|
After Width: | Height: | Size: 54 KiB |
BIN
src/icons/lvl3.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
src/icons/lvl4.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
BIN
src/icons/lvl5.png
Normal file
|
After Width: | Height: | Size: 88 KiB |
BIN
src/icons/lvl6.png
Normal file
|
After Width: | Height: | Size: 116 KiB |
BIN
src/icons/lvl7.png
Normal file
|
After Width: | Height: | Size: 249 KiB |
BIN
src/icons/lvl8.png
Normal file
|
After Width: | Height: | Size: 62 KiB |
BIN
src/icons/lvl9.png
Normal file
|
After Width: | Height: | Size: 5.0 MiB |
47
src/main.ts
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
import { PORT } from './core/config';
|
||||||
|
import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger';
|
||||||
|
import * as cookieParser from 'cookie-parser';
|
||||||
|
import { join } from 'path';
|
||||||
|
import { NestExpressApplication } from '@nestjs/platform-express'; // Импортируем NestExpressApplication
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
const app = await NestFactory.create<NestExpressApplication>(AppModule, {
|
||||||
|
logger: ['log', 'error', 'warn', 'debug', 'verbose'],
|
||||||
|
});
|
||||||
|
|
||||||
|
app.enableCors({
|
||||||
|
origin: [
|
||||||
|
'http://localhost:3000',
|
||||||
|
'http://localhost:3001',
|
||||||
|
'http://localhost:5000',
|
||||||
|
'http://127.0.0.1:3000',
|
||||||
|
'http://127.0.0.1:3001',
|
||||||
|
'http://127.0.0.1:5000',
|
||||||
|
'https://api.steampowered.com',
|
||||||
|
'https://magicrust.gg/',
|
||||||
|
],
|
||||||
|
credentials: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
app.use(cookieParser());
|
||||||
|
|
||||||
|
// Настройка статических файлов
|
||||||
|
app.useStaticAssets(join(__dirname, '..', 'src/icons'), {
|
||||||
|
prefix: '/src/icons/',
|
||||||
|
});
|
||||||
|
|
||||||
|
const config = new DocumentBuilder()
|
||||||
|
.setTitle('Magicow Rust Shop')
|
||||||
|
.setDescription('The backend Node API description')
|
||||||
|
.setVersion('1.4.5')
|
||||||
|
.addBearerAuth()
|
||||||
|
.addTag('Dartar')
|
||||||
|
.build();
|
||||||
|
const document = SwaggerModule.createDocument(app, config);
|
||||||
|
SwaggerModule.setup('/api/docs', app, document);
|
||||||
|
|
||||||
|
await app.listen(PORT);
|
||||||
|
}
|
||||||
|
bootstrap();
|
||||||
16
src/notification/notification.controller.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Controller, Get, Headers, Param, UseGuards } from '@nestjs/common';
|
||||||
|
import { NotificationService } from './notification.service';
|
||||||
|
import { JwtAuthGuard } from 'src/auth/guards/jwt-auth.guard';
|
||||||
|
|
||||||
|
@Controller('notification')
|
||||||
|
export class NotificationController {
|
||||||
|
constructor(private readonly notificationService: NotificationService) {}
|
||||||
|
|
||||||
|
@Get('/')
|
||||||
|
@UseGuards(JwtAuthGuard)
|
||||||
|
getNotificationCode(@Headers('Authorization') authorization) {
|
||||||
|
const token = authorization.split(' ')[1];
|
||||||
|
|
||||||
|
return this.notificationService.generateCode(token);
|
||||||
|
}
|
||||||
|
}
|
||||||
15
src/notification/notification.module.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { NotificationController } from './notification.controller';
|
||||||
|
import { NotificationService } from './notification.service';
|
||||||
|
import { HttpModule } from '@nestjs/axios';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
import { UsersModule } from 'src/users/users.module';
|
||||||
|
import { TokenModule } from 'src/token/token.module';
|
||||||
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
controllers: [NotificationController],
|
||||||
|
providers: [NotificationService, PrismaService],
|
||||||
|
imports: [HttpModule, UsersModule, TokenModule, JwtModule],
|
||||||
|
})
|
||||||
|
export class NotificationModule {}
|
||||||
53
src/notification/notification.service.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { HttpService } from '@nestjs/axios';
|
||||||
|
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
|
||||||
|
import { AxiosError } from 'axios';
|
||||||
|
import { catchError, firstValueFrom, map } from 'rxjs';
|
||||||
|
import { PrismaService } from 'src/prisma/prisma.service';
|
||||||
|
import { TokenService } from 'src/token/token.service';
|
||||||
|
import { UsersService } from 'src/users/users.service';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class NotificationService {
|
||||||
|
constructor(
|
||||||
|
private httpService: HttpService,
|
||||||
|
private prisma: PrismaService,
|
||||||
|
private tokenService: TokenService,
|
||||||
|
private userService: UsersService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async generateCode(token: string) {
|
||||||
|
try {
|
||||||
|
const isUser = await this.tokenService.validateAccessToken(token);
|
||||||
|
|
||||||
|
const user = await this.userService.findBySteamId(isUser.steamId);
|
||||||
|
|
||||||
|
if (!user) {
|
||||||
|
throw new HttpException(
|
||||||
|
'Пользователь не найден',
|
||||||
|
HttpStatus.BAD_REQUEST,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = await this.prisma.baseSettings.findFirst({
|
||||||
|
select: {
|
||||||
|
apiKey: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const newCode = this.httpService
|
||||||
|
.get(
|
||||||
|
`https://vk.magicrust.ru/api/getTestAlertCode?apiKey=${settings.apiKey}&steamid=${user.steamID}`,
|
||||||
|
)
|
||||||
|
.pipe(map((resp) => resp.data));
|
||||||
|
|
||||||
|
return newCode;
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error);
|
||||||
|
|
||||||
|
return {
|
||||||
|
status: 'Error',
|
||||||
|
message: error.message,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
src/orders/dto/create-order.dto.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export class CreateOrderDto {}
|
||||||
4
src/orders/dto/update-order.dto.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
import { PartialType } from '@nestjs/swagger';
|
||||||
|
import { CreateOrderDto } from './create-order.dto';
|
||||||
|
|
||||||
|
export class UpdateOrderDto extends PartialType(CreateOrderDto) {}
|
||||||