initial commit

This commit is contained in:
Sergey 2025-01-17 19:44:36 +05:00
commit 4901ea4105
144 changed files with 18933 additions and 0 deletions

31
.eslintrc.js Normal file
View 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
View 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
View File

@ -0,0 +1,4 @@
{
"singleQuote": true,
"trailingComma": "all"
}

73
README.md Normal file
View 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>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](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
View 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
View 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

File diff suppressed because one or more lines are too long

73
migrater.txt Normal file
View 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
View 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

File diff suppressed because it is too large Load Diff

99
package.json Normal file
View 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"
}
}

View 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;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Product` MODIFY `hidden` BOOLEAN NOT NULL DEFAULT true;

View 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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Inventory` ADD COLUMN `createdAt` DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3);

View File

@ -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;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `urlSettings` MODIFY `typeUrl` ENUM('SITE_SECTION', 'CUSTOM_PAGE', 'EXTERNAL_LINK', 'DROPDOWN_LIST') NOT NULL DEFAULT 'CUSTOM_PAGE';

View 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;

View File

@ -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;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Token` MODIFY `os` VARCHAR(191) NULL DEFAULT 'windows';

View File

@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE `Token` ALTER COLUMN `browser` DROP DEFAULT,
ALTER COLUMN `os` DROP DEFAULT;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Purchase` ADD COLUMN `dateOfPurchase` VARCHAR(191) NULL;

View File

@ -0,0 +1,3 @@
-- AlterTable
ALTER TABLE `Product` MODIFY `discount` INTEGER NULL DEFAULT 1,
MODIFY `saleDiscount` INTEGER NULL DEFAULT 1;

View File

@ -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;

View File

@ -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;

View 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;

View File

@ -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;

View File

@ -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;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Product` ADD COLUMN `textButton_en` VARCHAR(191) NULL;

View File

@ -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`;

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Transaction` MODIFY `status` ENUM('SUCCESS', 'FALSE', 'DENIED', 'REFUND') NOT NULL DEFAULT 'FALSE';

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Transaction` MODIFY `status` ENUM('SUCCESS', 'FALSE', 'DENIED', 'REFUND', 'IN_PROGRESS') NOT NULL DEFAULT 'FALSE';

View File

@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE `Transaction` ADD COLUMN `sendNotification` BOOLEAN NOT NULL DEFAULT false;

View File

@ -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;

View File

@ -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';

View 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`;

View 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
View 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
}

View 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);
}
}
}

View 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 {}

View 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;
}
}

View 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
View 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
View 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
View 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
View 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;
}

View File

@ -0,0 +1,7 @@
export class JwtPayload {
readonly id: number;
readonly steamId: string;
readonly role: string;
readonly name: string;
readonly avatar: string;
}

View 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;
}
}

View 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; // Присваиваем уровень из модели
}
}

View File

@ -0,0 +1,7 @@
export class UserAgentDto {
readonly deviceType: string;
readonly deviceName: string;
readonly browser: string;
readonly clientIp: string;
readonly os: string;
}

View 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);
}
}
}

View 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,
);
}
}
}
}

View File

@ -0,0 +1,5 @@
import { SetMetadata } from '@nestjs/common';
export const ROLES_KEY = 'roles';
export const Roles = (...roles: string[]) => SetMetadata(ROLES_KEY, roles);

View 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();
}
}

View 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;
}
}

View 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;
}
}

View 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();
}
}

View 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 {}

View 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
View 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';

View 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';

View 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);
} */
}

View 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 {}

View 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,
},
});
}
}

View 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;
}

View 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
View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 111 KiB

BIN
src/icons/lvl10.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

BIN
src/icons/lvl11.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

BIN
src/icons/lvl12.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

BIN
src/icons/lvl13.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
src/icons/lvl14.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
src/icons/lvl16.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

BIN
src/icons/lvl17.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

BIN
src/icons/lvl18.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
src/icons/lvl19.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

BIN
src/icons/lvl2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

BIN
src/icons/lvl20.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

BIN
src/icons/lvl3.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

BIN
src/icons/lvl4.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

BIN
src/icons/lvl5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

BIN
src/icons/lvl6.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 116 KiB

BIN
src/icons/lvl7.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

BIN
src/icons/lvl8.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

BIN
src/icons/lvl9.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 MiB

47
src/main.ts Normal file
View 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();

View 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);
}
}

View 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 {}

View 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,
};
}
}
}

View File

@ -0,0 +1 @@
export class CreateOrderDto {}

View File

@ -0,0 +1,4 @@
import { PartialType } from '@nestjs/swagger';
import { CreateOrderDto } from './create-order.dto';
export class UpdateOrderDto extends PartialType(CreateOrderDto) {}

Some files were not shown because too many files have changed in this diff Show More